gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[GNUnet-SVN] [gnunet-secushare] 01/03: initial import from gnunet.git


From: gnunet
Subject: [GNUnet-SVN] [gnunet-secushare] 01/03: initial import from gnunet.git
Date: Mon, 11 Feb 2019 21:00:11 +0100

This is an automated email from the git hooks/post-receive script.

grothoff pushed a commit to branch master
in repository gnunet-secushare.

commit 1f59e703d82b47f3aeaf432045a2633c2841169b
Author: Christian Grothoff <address@hidden>
AuthorDate: Mon Feb 11 20:39:36 2019 +0100

    initial import from gnunet.git
---
 ABOUT-NLS                                         | 1282 ++++
 AUTHORS                                           |    1 +
 COPYING                                           |  674 ++
 ChangeLog                                         |   14 +
 INSTALL                                           |  365 +
 Makefile.am                                       |    9 +
 NEWS                                              |    0
 README                                            |    1 +
 bootstrap                                         |    8 +
 config.rpath                                      |  672 ++
 configure.ac                                      |  221 +
 m4/ac_define_dir.m4                               |   35 +
 m4/gettext.m4                                     |  383 +
 m4/iconv.m4                                       |  214 +
 m4/lib-ld.m4                                      |  110 +
 m4/lib-link.m4                                    |  774 ++
 m4/lib-prefix.m4                                  |  224 +
 m4/libtool.m4                                     | 8387 +++++++++++++++++++++
 m4/ltoptions.m4                                   |  437 ++
 m4/ltsugar.m4                                     |  124 +
 m4/ltversion.m4                                   |   23 +
 m4/lt~obsolete.m4                                 |   99 +
 m4/nls.m4                                         |   32 +
 m4/pkg.m4                                         |   57 +
 m4/po.m4                                          |  449 ++
 m4/progtest.m4                                    |   92 +
 pkgconfig/Makefile.am                             |   25 +
 pkgconfig/gnunetmulticast.pc.in                   |   12 +
 pkgconfig/gnunetpsyc.pc.in                        |   12 +
 pkgconfig/gnunetpsycstore.pc.in                   |   12 +
 po/.gitignore                                     |    6 +
 po/ChangeLog                                      |   12 +
 po/Makefile.in.in                                 |  444 ++
 po/Makevars                                       |   41 +
 po/POTFILES.in                                    |    2 +
 po/Rules-quot                                     |   47 +
 po/boldquot.sed                                   |   10 +
 po/address@hidden                             |   25 +
 po/address@hidden                                 |   22 +
 po/insert-header.sin                              |   23 +
 po/quot.sed                                       |    6 +
 po/remove-potcdate.sin                            |   19 +
 src/.gitignore                                    |    2 +
 src/Makefile.am                                   |    7 +
 src/include/.gitignore                            |    2 +
 src/include/Makefile.am                           |   15 +
 src/include/gnunet_multicast_service.h            |  925 +++
 src/include/gnunet_psyc_env.h                     |  340 +
 src/include/gnunet_psyc_message.h                 |  278 +
 src/include/gnunet_psyc_service.h                 | 1364 ++++
 src/include/gnunet_psyc_slicer.h                  |  378 +
 src/include/gnunet_psyc_util_lib.h                |   53 +
 src/include/gnunet_psycstore_plugin.h             |  383 +
 src/include/gnunet_psycstore_service.h            |  701 ++
 src/include/gnunet_social_service.h               | 1344 ++++
 src/multicast/.gitignore                          |    7 +
 src/multicast/Makefile.am                         |   79 +
 src/multicast/gnunet-multicast.c                  |   79 +
 src/multicast/gnunet-service-multicast.c          | 2234 ++++++
 src/multicast/multicast.conf.in                   |   22 +
 src/multicast/multicast.h                         |  303 +
 src/multicast/multicast_api.c                     | 1399 ++++
 src/multicast/test_multicast.c                    |  758 ++
 src/multicast/test_multicast.conf                 |   56 +
 src/multicast/test_multicast_2peers.c             |  520 ++
 src/multicast/test_multicast_line.conf            |   63 +
 src/multicast/test_multicast_multipeer.c          |  643 ++
 src/multicast/test_multicast_star.conf            |   64 +
 src/psyc/.gitignore                               |    2 +
 src/psyc/Makefile.am                              |   77 +
 src/psyc/gnunet-service-psyc.c                    | 2860 +++++++
 src/psyc/psyc.conf.in                             |   12 +
 src/psyc/psyc.h                                   |  178 +
 src/psyc/psyc_api.c                               | 1584 ++++
 src/psyc/psyc_test_lib.h                          |   67 +
 src/psyc/test_psyc.c                              | 1018 +++
 src/psyc/test_psyc2.c                             |  284 +
 src/psyc/test_psyc_api_join.c                     |  282 +
 src/psycstore/.gitignore                          |    5 +
 src/psycstore/Makefile.am                         |  155 +
 src/psycstore/gnunet-service-psycstore.c          | 1049 +++
 src/psycstore/plugin_psycstore_mysql.c            | 1960 +++++
 src/psycstore/plugin_psycstore_postgres.c         | 1530 ++++
 src/psycstore/plugin_psycstore_sqlite.c           | 1948 +++++
 src/psycstore/psycstore.conf.in                   |   28 +
 src/psycstore/psycstore.h                         |  520 ++
 src/psycstore/psycstore_api.c                     | 1285 ++++
 src/psycstore/test_plugin_psycstore.c             |  532 ++
 src/psycstore/test_plugin_psycstore_mysql.conf    |    7 +
 src/psycstore/test_plugin_psycstore_postgres.conf |    2 +
 src/psycstore/test_plugin_psycstore_sqlite.conf   |    2 +
 src/psycstore/test_psycstore.c                    |  586 ++
 src/psycstore/test_psycstore.conf                 |    8 +
 src/psycutil/.gitignore                           |    1 +
 src/psycutil/Makefile.am                          |   45 +
 src/psycutil/psyc_env.c                           |  196 +
 src/psycutil/psyc_message.c                       | 1355 ++++
 src/psycutil/psyc_slicer.c                        |  711 ++
 src/psycutil/test_psyc_env.c                      |   96 +
 src/social/.gitignore                             |    3 +
 src/social/Makefile.am                            |   79 +
 src/social/gnunet-service-social.c                | 3760 +++++++++
 src/social/gnunet-social.c                        | 1411 ++++
 src/social/social.conf.in                         |   15 +
 src/social/social.h                               |  292 +
 src/social/social_api.c                           | 2827 +++++++
 src/social/test_social.c                          | 1449 ++++
 src/social/test_social.conf                       |   19 +
 108 files changed, 55664 insertions(+)

diff --git a/ABOUT-NLS b/ABOUT-NLS
new file mode 100644
index 0000000..b1de1b6
--- /dev/null
+++ b/ABOUT-NLS
@@ -0,0 +1,1282 @@
+1 Notes on the Free Translation Project
+***************************************
+
+Free software is going international!  The Free Translation Project is
+a way to get maintainers of free software, translators, and users all
+together, so that free software will gradually become able to speak many
+languages.  A few packages already provide translations for their
+messages.
+
+   If you found this `ABOUT-NLS' file inside a distribution, you may
+assume that the distributed package does use GNU `gettext' internally,
+itself available at your nearest GNU archive site.  But you do _not_
+need to install GNU `gettext' prior to configuring, installing or using
+this package with messages translated.
+
+   Installers will find here some useful hints.  These notes also
+explain how users should proceed for getting the programs to use the
+available translations.  They tell how people wanting to contribute and
+work on translations can contact the appropriate team.
+
+1.1 INSTALL Matters
+===================
+
+Some packages are "localizable" when properly installed; the programs
+they contain can be made to speak your own native language.  Most such
+packages use GNU `gettext'.  Other packages have their own ways to
+internationalization, predating GNU `gettext'.
+
+   By default, this package will be installed to allow translation of
+messages.  It will automatically detect whether the system already
+provides the GNU `gettext' functions.  Installers may use special
+options at configuration time for changing the default behaviour.  The
+command:
+
+     ./configure --disable-nls
+
+will _totally_ disable translation of messages.
+
+   When you already have GNU `gettext' installed on your system and run
+configure without an option for your new package, `configure' will
+probably detect the previously built and installed `libintl' library
+and will decide to use it.  If not, you may have to to use the
+`--with-libintl-prefix' option to tell `configure' where to look for it.
+
+   Internationalized packages usually have many `po/LL.po' files, where
+LL gives an ISO 639 two-letter code identifying the language.  Unless
+translations have been forbidden at `configure' time by using the
+`--disable-nls' switch, all available translations are installed
+together with the package.  However, the environment variable `LINGUAS'
+may be set, prior to configuration, to limit the installed set.
+`LINGUAS' should then contain a space separated list of two-letter
+codes, stating which languages are allowed.
+
+1.2 Using This Package
+======================
+
+As a user, if your language has been installed for this package, you
+only have to set the `LANG' environment variable to the appropriate
+`LL_CC' combination.  If you happen to have the `LC_ALL' or some other
+`LC_xxx' environment variables set, you should unset them before
+setting `LANG', otherwise the setting of `LANG' will not have the
+desired effect.  Here `LL' is an ISO 639 two-letter language code, and
+`CC' is an ISO 3166 two-letter country code.  For example, let's
+suppose that you speak German and live in Germany.  At the shell
+prompt, merely execute `setenv LANG de_DE' (in `csh'),
+`export LANG; LANG=de_DE' (in `sh') or `export LANG=de_DE' (in `bash').
+This can be done from your `.login' or `.profile' file, once and for
+all.
+
+   You might think that the country code specification is redundant.
+But in fact, some languages have dialects in different countries.  For
+example, `de_AT' is used for Austria, and `pt_BR' for Brazil.  The
+country code serves to distinguish the dialects.
+
+   The locale naming convention of `LL_CC', with `LL' denoting the
+language and `CC' denoting the country, is the one use on systems based
+on GNU libc.  On other systems, some variations of this scheme are
+used, such as `LL' or `LL_CC.ENCODING'.  You can get the list of
+locales supported by your system for your language by running the
+command `locale -a | grep '^LL''.
+
+   Not all programs have translations for all languages.  By default, an
+English message is shown in place of a nonexistent translation.  If you
+understand other languages, you can set up a priority list of languages.
+This is done through a different environment variable, called
+`LANGUAGE'.  GNU `gettext' gives preference to `LANGUAGE' over `LANG'
+for the purpose of message handling, but you still need to have `LANG'
+set to the primary language; this is required by other parts of the
+system libraries.  For example, some Swedish users who would rather
+read translations in German than English for when Swedish is not
+available, set `LANGUAGE' to `sv:de' while leaving `LANG' to `sv_SE'.
+
+   Special advice for Norwegian users: The language code for Norwegian
+bokma*l changed from `no' to `nb' recently (in 2003).  During the
+transition period, while some message catalogs for this language are
+installed under `nb' and some older ones under `no', it's recommended
+for Norwegian users to set `LANGUAGE' to `nb:no' so that both newer and
+older translations are used.
+
+   In the `LANGUAGE' environment variable, but not in the `LANG'
+environment variable, `LL_CC' combinations can be abbreviated as `LL'
+to denote the language's main dialect.  For example, `de' is equivalent
+to `de_DE' (German as spoken in Germany), and `pt' to `pt_PT'
+(Portuguese as spoken in Portugal) in this context.
+
+1.3 Translating Teams
+=====================
+
+For the Free Translation Project to be a success, we need interested
+people who like their own language and write it well, and who are also
+able to synergize with other translators speaking the same language.
+Each translation team has its own mailing list.  The up-to-date list of
+teams can be found at the Free Translation Project's homepage,
+`http://translationproject.org/', in the "Teams" area.
+
+   If you'd like to volunteer to _work_ at translating messages, you
+should become a member of the translating team for your own language.
+The subscribing address is _not_ the same as the list itself, it has
+`-request' appended.  For example, speakers of Swedish can send a
+message to address@hidden', having this message body:
+
+     subscribe
+
+   Keep in mind that team members are expected to participate
+_actively_ in translations, or at solving translational difficulties,
+rather than merely lurking around.  If your team does not exist yet and
+you want to start one, or if you are unsure about what to do or how to
+get started, please write to address@hidden' to
+reach the coordinator for all translator teams.
+
+   The English team is special.  It works at improving and uniformizing
+the terminology in use.  Proven linguistic skills are praised more than
+programming skills, here.
+
+1.4 Available Packages
+======================
+
+Languages are not equally supported in all packages.  The following
+matrix shows the current state of internationalization, as of June
+2010.  The matrix shows, in regard of each package, for which languages
+PO files have been submitted to translation coordination, with a
+translation percentage of at least 50%.
+
+     Ready PO files       af am an ar as ast az be address@hidden bg bn_IN bs 
ca
+                        +--------------------------------------------------+
+     a2ps               |                       []                      [] |
+     aegis              |                                                  |
+     ant-phone          |                                                  |
+     anubis             |                                                  |
+     aspell             |                []                             [] |
+     bash               |                                                  |
+     bfd                |                                                  |
+     bibshelf           |                []                                |
+     binutils           |                                                  |
+     bison              |                                                  |
+     bison-runtime      |                []                                |
+     bluez-pin          | []             []                                |
+     bombono-dvd        |                                                  |
+     buzztard           |                                                  |
+     cflow              |                                                  |
+     clisp              |                                                  |
+     coreutils          |                                   []          [] |
+     cpio               |                                                  |
+     cppi               |                                                  |
+     cpplib             |                                               [] |
+     cryptsetup         |                                                  |
+     dfarc              |                                                  |
+     dialog             |                             []                [] |
+     dico               |                                                  |
+     diffutils          |                                               [] |
+     dink               |                                                  |
+     doodle             |                                                  |
+     e2fsprogs          |                                               [] |
+     enscript           |                                               [] |
+     exif               |                                                  |
+     fetchmail          |                                               [] |
+     findutils          |                                   []             |
+     flex               |                                               [] |
+     freedink           |                                                  |
+     gas                |                                                  |
+     gawk               |                []                             [] |
+     gcal               |                                               [] |
+     gcc                |                                                  |
+     gettext-examples   | []             []                 []          [] |
+     gettext-runtime    |                                   []          [] |
+     gettext-tools      |                                   []          [] |
+     gip                |                                   []             |
+     gjay               |                                                  |
+     gliv               |                                   []             |
+     glunarclock        |                []                 []             |
+     gnubiff            |                                                  |
+     gnucash            |                                               [] |
+     gnuedu             |                                                  |
+     gnulib             |                                                  |
+     gnunet             |                                                  |
+     gnunet-gtk         |                                                  |
+     gnutls             |                                                  |
+     gold               |                                                  |
+     gpe-aerial         |                                                  |
+     gpe-beam           |                                                  |
+     gpe-bluetooth      |                                                  |
+     gpe-calendar       |                                                  |
+     gpe-clock          |                []                                |
+     gpe-conf           |                                                  |
+     gpe-contacts       |                                                  |
+     gpe-edit           |                                                  |
+     gpe-filemanager    |                                                  |
+     gpe-go             |                                                  |
+     gpe-login          |                                                  |
+     gpe-ownerinfo      |                []                                |
+     gpe-package        |                                                  |
+     gpe-sketchbook     |                                                  |
+     gpe-su             |                []                                |
+     gpe-taskmanager    |                []                                |
+     gpe-timesheet      |                []                                |
+     gpe-today          |                []                                |
+     gpe-todo           |                                                  |
+     gphoto2            |                                                  |
+     gprof              |                                   []             |
+     gpsdrive           |                                                  |
+     gramadoir          |                                                  |
+     grep               |                                                  |
+     grub               |                []                             [] |
+     gsasl              |                                                  |
+     gss                |                                                  |
+     gst-plugins-bad    |                                   []             |
+     gst-plugins-base   |                                   []             |
+     gst-plugins-good   |                                   []             |
+     gst-plugins-ugly   |                                   []             |
+     gstreamer          | []                                []          [] |
+     gtick              |                                                  |
+     gtkam              |                       []                         |
+     gtkorphan          |                                   []             |
+     gtkspell           | []             []     []                         |
+     gutenprint         |                                                  |
+     hello              |                                   []             |
+     help2man           |                                                  |
+     hylafax            |                                                  |
+     idutils            |                                                  |
+     indent             |                                   []          [] |
+     iso_15924          |                                                  |
+     iso_3166           | []          []        []          []  []   [] [] |
+     iso_3166_2         |                                                  |
+     iso_4217           |                                                  |
+     iso_639            |             [] []     []              []         |
+     iso_639_3          |                                                  |
+     jwhois             |                                                  |
+     kbd                |                                                  |
+     keytouch           |                                               [] |
+     keytouch-editor    |                                                  |
+     keytouch-keyboa... |                                               [] |
+     klavaro            |          []                                      |
+     latrine            |                                                  |
+     ld                 |                                   []             |
+     leafpad            |                                   []          [] |
+     libc               |                                   []          [] |
+     libexif            |                       ()                         |
+     libextractor       |                                                  |
+     libgnutls          |                                                  |
+     libgpewidget       |                                                  |
+     libgpg-error       |                                                  |
+     libgphoto2         |                                                  |
+     libgphoto2_port    |                                                  |
+     libgsasl           |                                                  |
+     libiconv           |                                   []             |
+     libidn             |                                                  |
+     lifelines          |                                                  |
+     liferea            |                             []                [] |
+     lilypond           |                                                  |
+     linkdr             |          []                                      |
+     lordsawar          |                                                  |
+     lprng              |                                                  |
+     lynx               |                                               [] |
+     m4                 |                                                  |
+     mailfromd          |                                                  |
+     mailutils          |                                                  |
+     make               |                                                  |
+     man-db             |                                                  |
+     man-db-manpages    |                                                  |
+     minicom            |                                                  |
+     mkisofs            |                                                  |
+     myserver           |                                                  |
+     nano               |                                   []          [] |
+     opcodes            |                                                  |
+     parted             |                                                  |
+     pies               |                                                  |
+     popt               |                                                  |
+     psmisc             |                                                  |
+     pspp               |                                               [] |
+     pwdutils           |                                                  |
+     radius             |                                               [] |
+     recode             |                       []                      [] |
+     rosegarden         |                                                  |
+     rpm                |                                                  |
+     rush               |                                                  |
+     sarg               |                                                  |
+     screem             |                                                  |
+     scrollkeeper       |                    [] []                      [] |
+     sed                |                []                             [] |
+     sharutils          |                                   []          [] |
+     shishi             |                                                  |
+     skencil            |                                                  |
+     solfege            |                                                  |
+     solfege-manual     |                                                  |
+     soundtracker       |                                                  |
+     sp                 |                                                  |
+     sysstat            |                                                  |
+     tar                |                                   []             |
+     texinfo            |                                                  |
+     tin                |                                                  |
+     unicode-han-tra... |                                                  |
+     unicode-transla... |                                                  |
+     util-linux-ng      |                                               [] |
+     vice               |                                                  |
+     vmm                |                                                  |
+     vorbis-tools       |                                                  |
+     wastesedge         |                                                  |
+     wdiff              |                                                  |
+     wget               |                       []                      [] |
+     wyslij-po          |                                                  |
+     xchat              |                []     []          []          [] |
+     xdg-user-dirs      | []    []    [] []     []    []    []  []      [] |
+     xkeyboard-config   |                                   []          [] |
+                        +--------------------------------------------------+
+                          af am an ar as ast az be address@hidden bg bn_IN bs 
ca
+                           6  0  1  2  3 19   1 10     3    28   3    1 38
+
+                          crh cs da  de  el en en_GB en_ZA eo es et eu fa
+                        +-------------------------------------------------+
+     a2ps               |     [] []  []  []     []            [] []       |
+     aegis              |        []  []                       []          |
+     ant-phone          |        []  ()                                   |
+     anubis             |        []  []                                   |
+     aspell             |     [] []  []         []            []          |
+     bash               |     []                           [] []          |
+     bfd                |                                     []          |
+     bibshelf           |        []  []                       []          |
+     binutils           |                                     []          |
+     bison              |            []  []                               |
+     bison-runtime      |        []  []  []                      []       |
+     bluez-pin          |     [] []  []  []                [] []          |
+     bombono-dvd        |        []                                       |
+     buzztard           |     [] []  []                                   |
+     cflow              |        []  []                                   |
+     clisp              |        []  []     []                []          |
+     coreutils          |     [] []  []                          []       |
+     cpio               |                                                 |
+     cppi               |                                                 |
+     cpplib             |        []  []                       []          |
+     cryptsetup         |            []                                   |
+     dfarc              |        []  []                       []          |
+     dialog             |        []  []                    [] []    []    |
+     dico               |                                                 |
+     diffutils          |     [] []  []  []                [] []          |
+     dink               |        []  []                       []          |
+     doodle             |            []                                   |
+     e2fsprogs          |     []     []                       []          |
+     enscript           |        []  []         []                        |
+     exif               |     () []  []                                   |
+     fetchmail          |     [] []  ()  []     []            []          |
+     findutils          |     [] []  []                                   |
+     flex               |            []                       []          |
+     freedink           |        []  []                       []          |
+     gas                |                                     []          |
+     gawk               |        []  []                       []          |
+     gcal               |                                     []          |
+     gcc                |            []                       []          |
+     gettext-examples   |            []  []                [] []          |
+     gettext-runtime    |        []  []                    [] []          |
+     gettext-tools      |            []                       []    []    |
+     gip                |        []  []                       []    []    |
+     gjay               |            []                                   |
+     gliv               |     [] []  []                                   |
+     glunarclock        |        []  []                                   |
+     gnubiff            |            ()                                   |
+     gnucash            |     []     ()  ()     ()            ()          |
+     gnuedu             |        []                           []          |
+     gnulib             |            []                       []          |
+     gnunet             |                                                 |
+     gnunet-gtk         |        []                                       |
+     gnutls             |     []     []                                   |
+     gold               |                                     []          |
+     gpe-aerial         |     [] []  []                       []          |
+     gpe-beam           |     [] []  []                       []          |
+     gpe-bluetooth      |        []  []                                   |
+     gpe-calendar       |        []                                       |
+     gpe-clock          |     [] []  []                       []          |
+     gpe-conf           |     [] []  []                                   |
+     gpe-contacts       |        []  []                       []          |
+     gpe-edit           |        []  []                                   |
+     gpe-filemanager    |        []  []                       []          |
+     gpe-go             |     [] []  []                       []          |
+     gpe-login          |        []  []                                   |
+     gpe-ownerinfo      |     [] []  []                       []          |
+     gpe-package        |        []  []                       []          |
+     gpe-sketchbook     |     [] []  []                       []          |
+     gpe-su             |     [] []  []                       []          |
+     gpe-taskmanager    |     [] []  []                       []          |
+     gpe-timesheet      |     [] []  []                       []          |
+     gpe-today          |     [] []  []                       []          |
+     gpe-todo           |        []  []                       []          |
+     gphoto2            |     [] []  ()         []            []    []    |
+     gprof              |        []  []                       []          |
+     gpsdrive           |        []                           [] []       |
+     gramadoir          |        []  []                    []             |
+     grep               |     []                                          |
+     grub               |        []  []                                   |
+     gsasl              |            []                                   |
+     gss                |                                                 |
+     gst-plugins-bad    |     [] []  []                       []    []    |
+     gst-plugins-base   |     [] []  []                       []    []    |
+     gst-plugins-good   |     [] []  []  []                   []    []    |
+     gst-plugins-ugly   |     [] []  []  []                   []    []    |
+     gstreamer          |     [] []  []                       []    []    |
+     gtick              |        []  ()                    []             |
+     gtkam              |     [] []  ()                    [] []          |
+     gtkorphan          |     [] []  []                    []             |
+     gtkspell           |     [] []  []  []                [] []    []    |
+     gutenprint         |        []  []         []                        |
+     hello              |        []  []                    [] []          |
+     help2man           |            []                                   |
+     hylafax            |            []                       []          |
+     idutils            |        []  []                                   |
+     indent             |     [] []  []                    [] [] [] []    |
+     iso_15924          |        []      ()                [] []          |
+     iso_3166           | []  [] []  []  ()                [] [] [] ()    |
+     iso_3166_2         |                ()                               |
+     iso_4217           |     [] []  []  ()                   [] []       |
+     iso_639            | []  [] []  []  ()                [] []          |
+     iso_639_3          | []                                              |
+     jwhois             |                                     []          |
+     kbd                |     [] []  []  []                   []          |
+     keytouch           |        []  []                                   |
+     keytouch-editor    |        []  []                                   |
+     keytouch-keyboa... |        []                                       |
+     klavaro            |     [] []  []                    []             |
+     latrine            |        []  ()                                   |
+     ld                 |        []                           []          |
+     leafpad            |     [] []  []  []                   []    []    |
+     libc               |     [] []  []                       []          |
+     libexif            |        []  []         ()                        |
+     libextractor       |                                                 |
+     libgnutls          |     []                                          |
+     libgpewidget       |        []  []                                   |
+     libgpg-error       |     []     []                                   |
+     libgphoto2         |        []  ()                                   |
+     libgphoto2_port    |        []  ()                             []    |
+     libgsasl           |                                                 |
+     libiconv           |     [] []  []                    []    []       |
+     libidn             |     []     []                    []             |
+     lifelines          |        []  ()                                   |
+     liferea            |     []     []  []                   []    []    |
+     lilypond           |     []     []                       []          |
+     linkdr             |        []  []                       []          |
+     lordsawar          |        []                                       |
+     lprng              |                                                 |
+     lynx               |     [] []  []                          []       |
+     m4                 |     [] []  []  []                               |
+     mailfromd          |                                                 |
+     mailutils          |                                     []          |
+     make               |        []  []                       []          |
+     man-db             |                                                 |
+     man-db-manpages    |                                                 |
+     minicom            |     [] []  []                       []          |
+     mkisofs            |                                                 |
+     myserver           |                                                 |
+     nano               |            []                       []    []    |
+     opcodes            |            []                       []          |
+     parted             |     []     []                                   |
+     pies               |                                                 |
+     popt               |     [] []  []                    [] []          |
+     psmisc             |     []     []                             []    |
+     pspp               |                                     []          |
+     pwdutils           |        []                                       |
+     radius             |                                     []          |
+     recode             |     [] []  []  []                [] []          |
+     rosegarden         |     ()     ()                       ()          |
+     rpm                |        []  []                       []          |
+     rush               |                                                 |
+     sarg               |                                                 |
+     screem             |                                                 |
+     scrollkeeper       |     [] []  []         []            []          |
+     sed                |     []     []  []                [] [] []       |
+     sharutils          |        []  []                       [] []       |
+     shishi             |                                                 |
+     skencil            |        []  ()                       []          |
+     solfege            |            []                    []    []       |
+     solfege-manual     |                                  []    []       |
+     soundtracker       |        []  []                       []          |
+     sp                 |            []                                   |
+     sysstat            |        []  []                             []    |
+     tar                |     []     []                          [] []    |
+     texinfo            |            []                    [] []          |
+     tin                |            []                          []       |
+     unicode-han-tra... |                                                 |
+     unicode-transla... |                                                 |
+     util-linux-ng      |     [] []  []                       []          |
+     vice               |        ()  ()                                   |
+     vmm                |            []                                   |
+     vorbis-tools       |     []                           []             |
+     wastesedge         |        []                                       |
+     wdiff              |            []                       []          |
+     wget               |     []     []                          []       |
+     wyslij-po          |                                                 |
+     xchat              |     []     []  []                   [] []       |
+     xdg-user-dirs      | []  [] []  []  []                [] [] [] []    |
+     xkeyboard-config   | []  [] []  []                    [] []          |
+                        +-------------------------------------------------+
+                          crh cs da  de  el en en_GB en_ZA eo es et eu fa
+                           5  64 105 117 18  1   8     0   28 89 18 19  0
+
+                          fi  fr  ga gl gu he hi hr hu hy id  is it ja ka kn
+                        +----------------------------------------------------+
+     a2ps               | []  []                          []        []       |
+     aegis              |     []                                 []          |
+     ant-phone          |     []                                 []          |
+     anubis             | []  []                          []     []          |
+     aspell             |     []  []                      []     []          |
+     bash               | []  []                          []        []       |
+     bfd                | []  []                          []                 |
+     bibshelf           | []  []  []                      []     []          |
+     binutils           | []  []                          []                 |
+     bison              | []  []  []                      []                 |
+     bison-runtime      | []  []  []                      []     [] []       |
+     bluez-pin          | []  []  []                [] [] []  []    []       |
+     bombono-dvd        | []                                                 |
+     buzztard           |                                 []                 |
+     cflow              | []      []                      []                 |
+     clisp              |     []                                             |
+     coreutils          |     []  []                []    []     []          |
+     cpio               | []  []  []                      []                 |
+     cppi               | []  []                                             |
+     cpplib             | []  []                          []                 |
+     cryptsetup         |     []                          []     []          |
+     dfarc              | []  []                                 []          |
+     dialog             |     []  [] []                   []  [] [] []       |
+     dico               |                                                    |
+     diffutils          | []  []  [] []    []       []    []     [] []       |
+     dink               |     []                                             |
+     doodle             |         []                             []          |
+     e2fsprogs          |     []                          []                 |
+     enscript           |     []  []             []       []                 |
+     exif               | []  []                          []  [] [] []       |
+     fetchmail          |     []                          []     [] []       |
+     findutils          | []  []  []                []    []     []          |
+     flex               | []  []  []                                         |
+     freedink           | []  []                          []                 |
+     gas                |     []                          []                 |
+     gawk               |     []  []       []             []     () []       |
+     gcal               |     []                                             |
+     gcc                |                                 []                 |
+     gettext-examples   | []  []  []                []    []     [] []       |
+     gettext-runtime    | []  []  []                      []     [] []       |
+     gettext-tools      |     []                          []     [] []       |
+     gip                | []  []  [] []                   []        []       |
+     gjay               | []                                                 |
+     gliv               | []  ()                                             |
+     glunarclock        | []      []                []    []                 |
+     gnubiff            |     ()                          []     ()          |
+     gnucash            | ()  ()           ()       ()           () []       |
+     gnuedu             |     []                                 []          |
+     gnulib             | []  []  []                []           [] []       |
+     gnunet             |                                                    |
+     gnunet-gtk         |     []                                             |
+     gnutls             |     []                                 []          |
+     gold               | []                              []                 |
+     gpe-aerial         | []  []                          []                 |
+     gpe-beam           | []  []                          []        []       |
+     gpe-bluetooth      | []                              []     [] []       |
+     gpe-calendar       | []                                        []       |
+     gpe-clock          | []  []                    []    []        []       |
+     gpe-conf           | []  []                          []        []       |
+     gpe-contacts       | []  []                          []        []       |
+     gpe-edit           | []                              []        []       |
+     gpe-filemanager    | []                        []    []        []       |
+     gpe-go             | []  []                    []    []        []       |
+     gpe-login          | []                              []        []       |
+     gpe-ownerinfo      | []  []                    []    []        []       |
+     gpe-package        | []                              []        []       |
+     gpe-sketchbook     | []  []                          []        []       |
+     gpe-su             | []  []     []             []    []        []       |
+     gpe-taskmanager    | []  []                    []    []        []       |
+     gpe-timesheet      | []  []  []                      []        []       |
+     gpe-today          | []  []  [] []             []    []        []       |
+     gpe-todo           | []                              []        []       |
+     gphoto2            | []  []                    []    []     [] []       |
+     gprof              | []  []  []                      []                 |
+     gpsdrive           |            []                   []     []          |
+     gramadoir          |     []  []                      []                 |
+     grep               | []                                     []          |
+     grub               | []                        []    []     []          |
+     gsasl              | []  []  []                      []     []          |
+     gss                | []  []  []                      []     []          |
+     gst-plugins-bad    | []  []                    []    []     [] []       |
+     gst-plugins-base   | []  []                    []    []     [] []       |
+     gst-plugins-good   | []  []                    []    []     [] []       |
+     gst-plugins-ugly   | []  []                    []    []     [] []       |
+     gstreamer          | []  []                    []    []     []          |
+     gtick              | []  []  []                      []     []          |
+     gtkam              |     []                    []    []     [] []       |
+     gtkorphan          |     []                          []     []          |
+     gtkspell           | []  []  [] []             [] [] []     [] []       |
+     gutenprint         | []  []                    []           []          |
+     hello              | []      []                      []                 |
+     help2man           | []  []                                             |
+     hylafax            |                                 []                 |
+     idutils            | []  []  []                []    []     []          |
+     indent             | []  []  [] []             []    []     [] []       |
+     iso_15924          | []  ()                          []     []          |
+     iso_3166           | []  ()  [] [] [] [] [] [] []    []     [] []       |
+     iso_3166_2         |     ()                    []    []     []          |
+     iso_4217           | []  ()                    []    []     [] []       |
+     iso_639            | []  ()  []    []          []    []     [] []    [] |
+     iso_639_3          |     ()                                 []       [] |
+     jwhois             | []  []                    []    []     []          |
+     kbd                |     []                          []                 |
+     keytouch           | []  []  []                []    []     []          |
+     keytouch-editor    | []      []                []    []     []          |
+     keytouch-keyboa... | []      []                []    []     []          |
+     klavaro            |            []             []                       |
+     latrine            | []                              []     []          |
+     ld                 | []  []  []                      []                 |
+     leafpad            | []  []  []       []       []    []     [] ()       |
+     libc               | []  []     []                   []        []       |
+     libexif            |                                        []          |
+     libextractor       |                                                    |
+     libgnutls          |     []                                 []          |
+     libgpewidget       | []      []                      []        []       |
+     libgpg-error       |     []                                 []          |
+     libgphoto2         |     []                                 [] []       |
+     libgphoto2_port    |     []                                 [] []       |
+     libgsasl           | []  []  []                      []     []          |
+     libiconv           | []  []  []                      []     [] []       |
+     libidn             | []  []                          []     []          |
+     lifelines          |     ()                                             |
+     liferea            |     []                    []           [] []       |
+     lilypond           | []  []                                             |
+     linkdr             | []               []    [] []           []          |
+     lordsawar          |                                                    |
+     lprng              |                                 []                 |
+     lynx               |     []                    []    []     [] []       |
+     m4                 | []  []  [] []                   []        []       |
+     mailfromd          |                                                    |
+     mailutils          |     []                          []                 |
+     make               | []  []  [] []    []    []       []     [] []       |
+     man-db             |                                 []     []          |
+     man-db-manpages    |                                 []                 |
+     minicom            | []  []                    []    []        []       |
+     mkisofs            | []  []                          []     []          |
+     myserver           |                                                    |
+     nano               | []  []  [] []             []           []          |
+     opcodes            | []  []  []                      []                 |
+     parted             |     []                          []     [] []       |
+     pies               |                                                    |
+     popt               | []  []  [] []             []    []  [] [] []       |
+     psmisc             | []  []                          []                 |
+     pspp               |                                                    |
+     pwdutils           |     []                          []                 |
+     radius             |     []                          []                 |
+     recode             | []  []  [] []    []       []    []     []          |
+     rosegarden         | ()  ()                          ()     () ()       |
+     rpm                |                                 []        []       |
+     rush               |                                                    |
+     sarg               |     []                                             |
+     screem             |                                        [] []       |
+     scrollkeeper       | []                        []    []     []          |
+     sed                | []  []  [] []             []    []     [] []       |
+     sharutils          | []  []  []                []    []     [] []       |
+     shishi             |     []                                             |
+     skencil            |     []                                             |
+     solfege            | []  []     []                          []          |
+     solfege-manual     |     []     []                                      |
+     soundtracker       |     []                                 []          |
+     sp                 |     []                                    ()       |
+     sysstat            | []  []                          []     [] []       |
+     tar                | []  []  []                []    []     [] []       |
+     texinfo            |     []                          []     [] []       |
+     tin                |     []                                             |
+     unicode-han-tra... |                                                    |
+     unicode-transla... |     []  []                                         |
+     util-linux-ng      | []  []                    []    []     [] []       |
+     vice               |     ()                    ()           ()          |
+     vmm                |     []                                             |
+     vorbis-tools       |                                 []                 |
+     wastesedge         |     ()                                 ()          |
+     wdiff              | []                                                 |
+     wget               | []  []  []             [] []    []     [] []       |
+     wyslij-po          | []  []                          []                 |
+     xchat              | []  []        []    []    []    []     [] []    [] |
+     xdg-user-dirs      | []  []  [] [] [] [] []    []    []  [] [] []    [] |
+     xkeyboard-config   | []  []                    []    []     []          |
+                        +----------------------------------------------------+
+                          fi  fr  ga gl gu he hi hr hu hy id  is it ja ka kn
+                          105 121 53 20  4  8  3  5 53  2 120  5 84 67  0  4
+
+                          ko ku ky lg lt lv mk ml mn mr ms mt nb nds ne
+                        +-----------------------------------------------+
+     a2ps               |                               []              |
+     aegis              |                                               |
+     ant-phone          |                                               |
+     anubis             |                               []    []        |
+     aspell             |                         []                    |
+     bash               |                                               |
+     bfd                |                                               |
+     bibshelf           |                []             []              |
+     binutils           |                                               |
+     bison              |                               []              |
+     bison-runtime      |       []    [] []             []    []        |
+     bluez-pin          |    [] []    [] []             []              |
+     bombono-dvd        |                                               |
+     buzztard           |                                               |
+     cflow              |                                               |
+     clisp              |                                               |
+     coreutils          |          []                                   |
+     cpio               |                                               |
+     cppi               |                                               |
+     cpplib             |                                               |
+     cryptsetup         |                                               |
+     dfarc              |                   []                          |
+     dialog             |    []       [] []             []    []        |
+     dico               |                                               |
+     diffutils          |                []             []              |
+     dink               |                                               |
+     doodle             |                                               |
+     e2fsprogs          |                                               |
+     enscript           |                                               |
+     exif               |                []                             |
+     fetchmail          |                                               |
+     findutils          |                                               |
+     flex               |                                               |
+     freedink           |                                     []        |
+     gas                |                                               |
+     gawk               |                                               |
+     gcal               |                                               |
+     gcc                |                                               |
+     gettext-examples   |       []       []             [] []           |
+     gettext-runtime    | []                                            |
+     gettext-tools      | []                                            |
+     gip                |                []             []              |
+     gjay               |                                               |
+     gliv               |                                               |
+     glunarclock        |                []                             |
+     gnubiff            |                                               |
+     gnucash            | ()          ()                      ()     () |
+     gnuedu             |                                               |
+     gnulib             |                                               |
+     gnunet             |                                               |
+     gnunet-gtk         |                                               |
+     gnutls             |                               []              |
+     gold               |                                               |
+     gpe-aerial         |                []                             |
+     gpe-beam           |                []                             |
+     gpe-bluetooth      |                []                []           |
+     gpe-calendar       |                []                             |
+     gpe-clock          | []    []       []             [] []           |
+     gpe-conf           | []             []                             |
+     gpe-contacts       | []             []                             |
+     gpe-edit           |                []                             |
+     gpe-filemanager    | []             []                             |
+     gpe-go             | []             []                []           |
+     gpe-login          |                []                             |
+     gpe-ownerinfo      |                []             []              |
+     gpe-package        | []             []                             |
+     gpe-sketchbook     | []             []                             |
+     gpe-su             | []    []       []             [] [] []        |
+     gpe-taskmanager    | [] [] []       []             [] []           |
+     gpe-timesheet      |                []             []              |
+     gpe-today          |       []       []             [] []           |
+     gpe-todo           |                []                   []        |
+     gphoto2            |                                               |
+     gprof              |                               []              |
+     gpsdrive           |                                               |
+     gramadoir          |                                               |
+     grep               |                                               |
+     grub               |                                               |
+     gsasl              |                                               |
+     gss                |                                               |
+     gst-plugins-bad    |             [] []                [] []        |
+     gst-plugins-base   |             [] []                             |
+     gst-plugins-good   |                []                []           |
+     gst-plugins-ugly   |             [] []             [] [] []        |
+     gstreamer          |                                               |
+     gtick              |                                               |
+     gtkam              |                                     []        |
+     gtkorphan          |                []                      []     |
+     gtkspell           |       []    [] []       []    []    [] []     |
+     gutenprint         |                                               |
+     hello              | []             []             []              |
+     help2man           |                                               |
+     hylafax            |                                               |
+     idutils            |                                               |
+     indent             |                                               |
+     iso_15924          |             [] []                             |
+     iso_3166           | [] []       () [] [] []    []       []        |
+     iso_3166_2         |                                               |
+     iso_4217           |             []                      []        |
+     iso_639            |                      []    []                 |
+     iso_639_3          |                            []                 |
+     jwhois             |                []                             |
+     kbd                |                                               |
+     keytouch           |                []                             |
+     keytouch-editor    |                []                             |
+     keytouch-keyboa... |                []                             |
+     klavaro            |                                     []        |
+     latrine            |                []                             |
+     ld                 |                                               |
+     leafpad            | []          [] []                             |
+     libc               | []                                            |
+     libexif            |                                               |
+     libextractor       |                                               |
+     libgnutls          |                               []              |
+     libgpewidget       |                []             []              |
+     libgpg-error       |                                               |
+     libgphoto2         |                                               |
+     libgphoto2_port    |                                               |
+     libgsasl           |                                               |
+     libiconv           |                                               |
+     libidn             |                                               |
+     lifelines          |                                               |
+     liferea            |                                               |
+     lilypond           |                                               |
+     linkdr             |                                               |
+     lordsawar          |                                               |
+     lprng              |                                               |
+     lynx               |                                               |
+     m4                 |                                               |
+     mailfromd          |                                               |
+     mailutils          |                                               |
+     make               | []                                            |
+     man-db             |                                               |
+     man-db-manpages    |                                               |
+     minicom            |                                     []        |
+     mkisofs            |                                               |
+     myserver           |                                               |
+     nano               |                               []    []        |
+     opcodes            |                                               |
+     parted             |                                               |
+     pies               |                                               |
+     popt               | []             []                   []        |
+     psmisc             |                                               |
+     pspp               |                                               |
+     pwdutils           |                                               |
+     radius             |                                               |
+     recode             |                                               |
+     rosegarden         |                                               |
+     rpm                |                                               |
+     rush               |                                               |
+     sarg               |                                               |
+     screem             |                                               |
+     scrollkeeper       |                                     []     [] |
+     sed                |                                               |
+     sharutils          |                                               |
+     shishi             |                                               |
+     skencil            |                                               |
+     solfege            |                                     []        |
+     solfege-manual     |                                               |
+     soundtracker       |                                               |
+     sp                 |                                               |
+     sysstat            |                []                             |
+     tar                |       []                                      |
+     texinfo            |                                     []        |
+     tin                |                                               |
+     unicode-han-tra... |                                               |
+     unicode-transla... |                                               |
+     util-linux-ng      |                                               |
+     vice               |                                               |
+     vmm                |                                               |
+     vorbis-tools       |                                               |
+     wastesedge         |                                               |
+     wdiff              |                                               |
+     wget               |             []                                |
+     wyslij-po          |                                               |
+     xchat              | []             [] []                          |
+     xdg-user-dirs      | [] []       [] [] []       []       [] []     |
+     xkeyboard-config   | []    []    []                                |
+                        +-----------------------------------------------+
+                          ko ku ky lg lt lv mk ml mn mr ms mt nb nds ne
+                          20  5 10  1 13 48  4  2  2  4 24 10 20  3   1
+
+                          nl  nn or pa pl  ps pt pt_BR ro ru rw sk sl sq sr
+                        +---------------------------------------------------+
+     a2ps               | []           []     []  []   [] []       []    [] |
+     aegis              | []                      []      []                |
+     ant-phone          |                         []   []                   |
+     anubis             | []           []                 []                |
+     aspell             | []                           [] []    [] []       |
+     bash               | []                                    []          |
+     bfd                |                                 []                |
+     bibshelf           | []  []                                            |
+     binutils           |                                 []    []          |
+     bison              | []           []                 []                |
+     bison-runtime      | []           []     []  []   [] []       []       |
+     bluez-pin          | []           []         []   [] []    [] []    [] |
+     bombono-dvd        |     []                          ()                |
+     buzztard           | []  []                                            |
+     cflow              |              []                                   |
+     clisp              | []                              []                |
+     coreutils          | []           []     []  []      []       []       |
+     cpio               | []           []                 []                |
+     cppi               |              []                                   |
+     cpplib             | []                                                |
+     cryptsetup         | []                                                |
+     dfarc              |              []                                   |
+     dialog             | []           []         []      []                |
+     dico               |              []                                   |
+     diffutils          | []           []         []   [] []             [] |
+     dink               | ()                                                |
+     doodle             | []                                          []    |
+     e2fsprogs          | []           []                                   |
+     enscript           | []                      []   [] []       []       |
+     exif               | []           []              [] ()    []          |
+     fetchmail          | []           []                 []          []    |
+     findutils          | []           []     []          []       []       |
+     flex               | []           []         []   [] []                |
+     freedink           | []           []                                   |
+     gas                |                                                   |
+     gawk               | []           []         []   []                   |
+     gcal               |                                                   |
+     gcc                |                                                [] |
+     gettext-examples   | []           []     []       [] []    [] []    [] |
+     gettext-runtime    | []  []       []     []       [] []    [] []    [] |
+     gettext-tools      |              []              [] []    [] []    [] |
+     gip                | []           []                 []    []       [] |
+     gjay               |                                                   |
+     gliv               | []           []         []   [] []    []          |
+     glunarclock        | []                      []   []       []       [] |
+     gnubiff            | []                           ()                   |
+     gnucash            | []           ()         ()      ()                |
+     gnuedu             | []                                                |
+     gnulib             | []           []                 []       []       |
+     gnunet             |                                                   |
+     gnunet-gtk         |                                                   |
+     gnutls             | []           []                                   |
+     gold               |                                                   |
+     gpe-aerial         | []                  []  []   [] []       []    [] |
+     gpe-beam           | []                  []  []   [] []       []    [] |
+     gpe-bluetooth      | []                      []                        |
+     gpe-calendar       |                         []      []       []    [] |
+     gpe-clock          | []                  []  []   [] []    [] []    [] |
+     gpe-conf           | []                  []  []   [] []    [] []       |
+     gpe-contacts       |                         []   [] []       []    [] |
+     gpe-edit           | []           []                          []       |
+     gpe-filemanager    | []                              []       []       |
+     gpe-go             | []           []         []   [] []    [] []    [] |
+     gpe-login          | []                      []                        |
+     gpe-ownerinfo      | []                  []  []   [] []    [] []    [] |
+     gpe-package        | []                                       []       |
+     gpe-sketchbook     | []                  []  []   [] []       []    [] |
+     gpe-su             | []                  []  []   [] []    [] []    [] |
+     gpe-taskmanager    | []                  []  []   [] []    [] []    [] |
+     gpe-timesheet      | []                  []  []   [] []    [] []    [] |
+     gpe-today          | []                  []  []   [] []    [] []    [] |
+     gpe-todo           | []                      []      []       []    [] |
+     gphoto2            | []        [] []         []   [] []    []       [] |
+     gprof              | []                      []   []                   |
+     gpsdrive           | []                              []                |
+     gramadoir          | []                                    []          |
+     grep               | []           []                 []    []          |
+     grub               | []           []                 []                |
+     gsasl              | []           []                       []       [] |
+     gss                |              []              []       []          |
+     gst-plugins-bad    | []           []         []      []    []    []    |
+     gst-plugins-base   | []           []         []      []    []          |
+     gst-plugins-good   | []           []         []      []    []          |
+     gst-plugins-ugly   | []           []         []      []    [] []       |
+     gstreamer          | []           []         []      []    []          |
+     gtick              | []                              []    []          |
+     gtkam              | []        [] []         []      []    []          |
+     gtkorphan          | []                                                |
+     gtkspell           | []           []     []  []   [] []    [] [] [] [] |
+     gutenprint         | []                              []                |
+     hello              | []           []                       [] []       |
+     help2man           |              []                 []                |
+     hylafax            | []                                                |
+     idutils            | []           []         []   [] []                |
+     indent             | []           []         []   [] []    []       [] |
+     iso_15924          | []           []                 []       []       |
+     iso_3166           | []  [] [] [] []     ()  []   [] [] [] [] [] [] [] |
+     iso_3166_2         | []           []                          []       |
+     iso_4217           | []  []       []     []          [] []    []    [] |
+     iso_639            | []     [] [] []                 [] [] [] []    [] |
+     iso_639_3          |        [] []                                      |
+     jwhois             | []           []         []   []                   |
+     kbd                | []           []              []                   |
+     keytouch           | []           []                       []          |
+     keytouch-editor    | []           []                       []          |
+     keytouch-keyboa... | []           []                       []          |
+     klavaro            | []                      []                        |
+     latrine            |              []                 []                |
+     ld                 |                                                   |
+     leafpad            | []  []       []     []  []      []    [] []    [] |
+     libc               | []           []                 []    []          |
+     libexif            | []           []         ()            []          |
+     libextractor       |                                                   |
+     libgnutls          | []           []                                   |
+     libgpewidget       | []           []                          []       |
+     libgpg-error       |              []              []                   |
+     libgphoto2         | []           []                                   |
+     libgphoto2_port    | []           []         []      []    []          |
+     libgsasl           | []           []              []       []       [] |
+     libiconv           | []           []                       [] []    [] |
+     libidn             | []           []                                   |
+     lifelines          | []           []                                   |
+     liferea            | []           []     []  []   [] ()    ()    []    |
+     lilypond           | []                                                |
+     linkdr             | []                  []          []                |
+     lordsawar          |                                                   |
+     lprng              |              []                                   |
+     lynx               | []                      []      []                |
+     m4                 | []           []         []   [] []                |
+     mailfromd          |              []                                   |
+     mailutils          |              []                                   |
+     make               | []           []         []      []                |
+     man-db             | []           []                 []                |
+     man-db-manpages    | []           []                 []                |
+     minicom            |              []         []   [] []                |
+     mkisofs            | []           []                 []                |
+     myserver           |                                                   |
+     nano               | []           []         []      []                |
+     opcodes            | []                           []                   |
+     parted             | []           []                 []    []          |
+     pies               |              []                                   |
+     popt               | []           []     []          []                |
+     psmisc             | []           []                 []                |
+     pspp               | []                      []                        |
+     pwdutils           |              []                                   |
+     radius             | []           []                 []                |
+     recode             | []           []     []  []   [] []    [] []       |
+     rosegarden         |              ()                 ()                |
+     rpm                | []           []     []                            |
+     rush               | []           []                                   |
+     sarg               |                                                   |
+     screem             |                                                   |
+     scrollkeeper       | []  []       []              [] []    []    [] [] |
+     sed                | []           []     []  []   [] []    [] []    [] |
+     sharutils          | []           []                 []             [] |
+     shishi             |              []                                   |
+     skencil            |                     []  []                        |
+     solfege            | []           []         []      []                |
+     solfege-manual     | []           []         []                        |
+     soundtracker       |                                       []          |
+     sp                 |                                                   |
+     sysstat            | []           []         []      []                |
+     tar                | []           []                 []       []       |
+     texinfo            | []           []              [] []                |
+     tin                |                                 []                |
+     unicode-han-tra... |                                                   |
+     unicode-transla... |                                                   |
+     util-linux-ng      | []           []         []      []       []       |
+     vice               | []                                                |
+     vmm                | []                                                |
+     vorbis-tools       | []           []                                   |
+     wastesedge         | []                                                |
+     wdiff              | []           []                                   |
+     wget               | []           []     []  []      []    [] []       |
+     wyslij-po          | []  []       []                                   |
+     xchat              | []        [] []     []          []    [] [] [] [] |
+     xdg-user-dirs      | []  [] [] [] []  [] []  []   [] []    [] [] [] [] |
+     xkeyboard-config   | []           []                 []                |
+                        +---------------------------------------------------+
+                          nl  nn or pa pl  ps pt pt_BR ro ru rw sk sl sq sr
+                          135 10  4  7 105  1 29  62   47 91  3 54 46  9 37
+
+                          sv  sw ta te tg th tr uk vi  wa zh_CN zh_HK zh_TW
+                        +---------------------------------------------------+
+     a2ps               | []              [] [] [] []                       | 
27
+     aegis              |                          []                       |  
9
+     ant-phone          | []                 []    []      []               |  
9
+     anubis             | []                 [] [] []                       | 
15
+     aspell             |                       [] []  []                   | 
20
+     bash               | []                    [] []                       | 
12
+     bfd                |                          []                       |  
6
+     bibshelf           | []                       []      []               | 
16
+     binutils           |                       [] []                       |  
8
+     bison              | []                       []                       | 
12
+     bison-runtime      | []              []    [] []      []          []   | 
29
+     bluez-pin          | []              [] [] [] []  []  []          []   | 
37
+     bombono-dvd        |                          []                       |  
4
+     buzztard           |                          []                       |  
7
+     cflow              |                       [] []      []               |  
9
+     clisp              |                                                   | 
10
+     coreutils          | []                    [] []      []               | 
22
+     cpio               | []                 [] [] []      []          []   | 
13
+     cppi               |                       [] []                       |  
5
+     cpplib             | []                 [] [] []      []          []   | 
14
+     cryptsetup         | []                       []                       |  
7
+     dfarc              |                          []                       |  
9
+     dialog             | []  []          []       []  []  []          []   | 
30
+     dico               |                       []                          |  
2
+     diffutils          | []                 [] [] []      []          []   | 
30
+     dink               |                                                   |  
4
+     doodle             | []                       []                       |  
7
+     e2fsprogs          | []                 []    []                       | 
11
+     enscript           | []                 [] [] []                       | 
17
+     exif               | []                       []      []               | 
16
+     fetchmail          |                    []    []      []               | 
17
+     findutils          | []                 [] [] []      []               | 
20
+     flex               | []                 []    []                  []   | 
15
+     freedink           |                          []                       | 
10
+     gas                |                    []                             |  
4
+     gawk               | []                 []    []      []               | 
18
+     gcal               | []                 []                             |  
5
+     gcc                | []                 []            []               |  
7
+     gettext-examples   | []                 [] [] []      []    []    []   | 
34
+     gettext-runtime    | []                 [] [] []      []    []    []   | 
29
+     gettext-tools      | []                 [] [] []      []          []   | 
22
+     gip                | []                       []      []          []   | 
22
+     gjay               |                          []                       |  
3
+     gliv               | []                 []    []                       | 
14
+     glunarclock        | []                       []  []  []          []   | 
19
+     gnubiff            | []                       []                       |  
4
+     gnucash            |                    () [] ()      []          ()   | 
10
+     gnuedu             |                          []                  []   |  
7
+     gnulib             | []                    [] []      []               | 
16
+     gnunet             |                          []                       |  
1
+     gnunet-gtk         | []                 []    []                       |  
5
+     gnutls             | []                       []      []               | 
10
+     gold               |                          []                       |  
4
+     gpe-aerial         | []                       []      []               | 
18
+     gpe-beam           | []                       []      []               | 
19
+     gpe-bluetooth      | []                       []      []               | 
13
+     gpe-calendar       | []                       []  []  []               | 
12
+     gpe-clock          | []                 []    []  []  []               | 
28
+     gpe-conf           | []                       []  []  []               | 
20
+     gpe-contacts       | []                       []      []               | 
17
+     gpe-edit           | []                       []      []               | 
12
+     gpe-filemanager    | []                       []  []  []               | 
16
+     gpe-go             | []                 []    []  []  []               | 
25
+     gpe-login          | []                       []      []               | 
11
+     gpe-ownerinfo      | []                 []    []      []          []   | 
25
+     gpe-package        | []                       []      []               | 
13
+     gpe-sketchbook     | []                       []      []               | 
20
+     gpe-su             | []                 []    []  []  []               | 
30
+     gpe-taskmanager    | []                 []    []  []  []               | 
29
+     gpe-timesheet      | []                 []    []      []          []   | 
25
+     gpe-today          | []                 []    []  []  []          []   | 
30
+     gpe-todo           | []                       []  []  []               | 
17
+     gphoto2            | []                    [] []      []          []   | 
24
+     gprof              | []                 []    []                       | 
15
+     gpsdrive           | []                       []      []               | 
11
+     gramadoir          | []                       []      []               | 
11
+     grep               |                 []       []      []               | 
10
+     grub               | []                       []      []               | 
14
+     gsasl              | []                       []      []          []   | 
14
+     gss                | []                       []      []               | 
11
+     gst-plugins-bad    | []                 []    []      []               | 
26
+     gst-plugins-base   | []                 [] [] []      []               | 
24
+     gst-plugins-good   | []                 []    []      []               | 
24
+     gst-plugins-ugly   | []                 [] [] []      []               | 
29
+     gstreamer          | []                    [] []      []               | 
22
+     gtick              |                       [] []      []               | 
13
+     gtkam              | []                       []      []               | 
20
+     gtkorphan          | []                       []      []               | 
14
+     gtkspell           | []              [] [] [] []  []  []    []    []   | 
45
+     gutenprint         | []                                                | 
10
+     hello              | []              [] []    []      []          []   | 
21
+     help2man           | []                       []                       |  
7
+     hylafax            |                          []                       |  
5
+     idutils            | []                 []    []      []               | 
17
+     indent             | []                 [] [] []      []          []   | 
30
+     iso_15924          |                 ()    [] ()      []          []   | 
16
+     iso_3166           | []        []    () [] [] ()  []  []    []    ()   | 
53
+     iso_3166_2         |                 ()    [] ()      []               |  
9
+     iso_4217           | []              () [] [] ()      []    []         | 
26
+     iso_639            | []     [] []    ()    [] ()  []  []    []    []   | 
38
+     iso_639_3          |        []                ()                       |  
8
+     jwhois             | []                 []    []      []          []   | 
16
+     kbd                | []                 [] [] []      []               | 
15
+     keytouch           | []                       []      []               | 
16
+     keytouch-editor    | []                       []      []               | 
14
+     keytouch-keyboa... | []                       []      []               | 
14
+     klavaro            |                          []                       | 
11
+     latrine            |                    []    []      []               | 
10
+     ld                 | []                 []    []                  []   | 
11
+     leafpad            | []                 [] [] []      []          []   | 
33
+     libc               | []                 []    []      []          []   | 
21
+     libexif            |                          []      ()               |  
7
+     libextractor       |                          []                       |  
1
+     libgnutls          | []                       []      []               |  
9
+     libgpewidget       | []                       []      []               | 
14
+     libgpg-error       | []                       []      []               |  
9
+     libgphoto2         |                       [] []                       |  
8
+     libgphoto2_port    | []                    [] []                  []   | 
14
+     libgsasl           | []                       []      []               | 
13
+     libiconv           | []                       []  []  []               | 
21
+     libidn             | ()                       []      []               | 
11
+     lifelines          | []                                                |  
4
+     liferea            | []                 []            []               | 
21
+     lilypond           |                          []                       |  
7
+     linkdr             | []                 []    []      []          []   | 
17
+     lordsawar          |                                                   |  
1
+     lprng              |                          []                       |  
3
+     lynx               | []                 [] [] []                       | 
17
+     m4                 | []                       []      []          []   | 
19
+     mailfromd          |                       [] []                       |  
3
+     mailutils          |                          []                       |  
5
+     make               | []                 []    []      []               | 
21
+     man-db             | []                       []      []               |  
8
+     man-db-manpages    |                                                   |  
4
+     minicom            | []                       []                       | 
16
+     mkisofs            |                          []      []               |  
9
+     myserver           |                                                   |  0
+     nano               | []                       []      []          []   | 
21
+     opcodes            | []                 []    []                       | 
11
+     parted             | []                 [] [] []                  []   | 
15
+     pies               |                       [] []                       |  
3
+     popt               | []              [] []    []      []          []   | 
27
+     psmisc             | []                       []                       | 
11
+     pspp               |                                                   |  
4
+     pwdutils           | []                       []                       |  
6
+     radius             |                       [] []                       |  
9
+     recode             | []                 []    []      []               | 
28
+     rosegarden         | ()                                                |  0
+     rpm                | []                       []                  []   | 
11
+     rush               |                       [] []                       |  
4
+     sarg               |                                                   |  
1
+     screem             |                          []                       |  
3
+     scrollkeeper       | []                 [] [] []                  []   | 
27
+     sed                | []                 []    []      []          []   | 
30
+     sharutils          | []                 []    []      []          []   | 
22
+     shishi             |                          []                       |  
3
+     skencil            | []                       []                       |  
7
+     solfege            | []                 []    []      []               | 
16
+     solfege-manual     |                    []                             |  
8
+     soundtracker       | []                 []    []                       |  
9
+     sp                 |                    []                             |  
3
+     sysstat            |                          []      []               | 
15
+     tar                | []                 [] [] []      []          []   | 
23
+     texinfo            | []                 [] [] []      []               | 
17
+     tin                |                                                   |  
4
+     unicode-han-tra... |                                                   |  0
+     unicode-transla... |                                                   |  
2
+     util-linux-ng      | []                 [] [] []                       | 
20
+     vice               | ()                 ()                             |  
1
+     vmm                |                          []                       |  
4
+     vorbis-tools       |                          []                       |  
6
+     wastesedge         |                                                   |  
2
+     wdiff              | []                       []                       |  
7
+     wget               | []                 []    []      []          []   | 
26
+     wyslij-po          |                       [] []                       |  
8
+     xchat              | []              []    [] []      []          []   | 
36
+     xdg-user-dirs      | []     [] []    [] [] [] []      []    []    []   | 
63
+     xkeyboard-config   | []                    [] []                       | 
22
+                        +---------------------------------------------------+
+       85 teams           sv  sw ta te tg th tr uk vi  wa zh_CN zh_HK zh_TW
+      178 domains         119  1  3  3  0 10 65 51 155 17  98     7    41    
2618
+
+   Some counters in the preceding matrix are higher than the number of
+visible blocks let us expect.  This is because a few extra PO files are
+used for implementing regional variants of languages, or language
+dialects.
+
+   For a PO file in the matrix above to be effective, the package to
+which it applies should also have been internationalized and
+distributed as such by its maintainer.  There might be an observable
+lag between the mere existence a PO file and its wide availability in a
+distribution.
+
+   If June 2010 seems to be old, you may fetch a more recent copy of
+this `ABOUT-NLS' file on most GNU archive sites.  The most up-to-date
+matrix with full percentage details can be found at
+`http://translationproject.org/extra/matrix.html'.
+
+1.5 Using `gettext' in new packages
+===================================
+
+If you are writing a freely available program and want to
+internationalize it you are welcome to use GNU `gettext' in your
+package.  Of course you have to respect the GNU Library General Public
+License which covers the use of the GNU `gettext' library.  This means
+in particular that even non-free programs can use `libintl' as a shared
+library, whereas only free software can use `libintl' as a static
+library or use modified versions of `libintl'.
+
+   Once the sources are changed appropriately and the setup can handle
+the use of `gettext' the only thing missing are the translations.  The
+Free Translation Project is also available for packages which are not
+developed inside the GNU project.  Therefore the information given above
+applies also for every other Free Software Project.  Contact
address@hidden' to make the `.pot' files available
+to the translation teams.
+
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..eb41b69
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1 @@
+Christian Grothoff
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..94a9ed0
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,674 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU 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
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/ChangeLog b/ChangeLog
new file mode 100644
index 0000000..5363c5b
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,14 @@
+2012-03-07  gettextize  <address@hidden>
+
+       * m4/gettext.m4: New file, from gettext-0.18.1.
+       * m4/iconv.m4: New file, from gettext-0.18.1.
+       * m4/lib-ld.m4: New file, from gettext-0.18.1.
+       * m4/lib-link.m4: New file, from gettext-0.18.1.
+       * m4/lib-prefix.m4: New file, from gettext-0.18.1.
+       * m4/nls.m4: New file, from gettext-0.18.1.
+       * m4/po.m4: New file, from gettext-0.18.1.
+       * m4/progtest.m4: New file, from gettext-0.18.1.
+       * Makefile.am (SUBDIRS): Add po.
+       * configure.ac (AC_OUTPUT): Add po/Makefile.in.
+       (AM_GNU_GETTEXT_VERSION): Bump to 0.18.1.
+
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..7d1c323
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,365 @@
+Installation Instructions
+*************************
+
+Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005,
+2006, 2007, 2008, 2009 Free Software Foundation, Inc.
+
+   Copying and distribution of this file, with or without modification,
+are permitted in any medium without royalty provided the copyright
+notice and this notice are preserved.  This file is offered as-is,
+without warranty of any kind.
+
+Basic Installation
+==================
+
+   Briefly, the shell commands `./configure; make; make install' should
+configure, build, and install this package.  The following
+more-detailed instructions are generic; see the `README' file for
+instructions specific to this package.  Some packages provide this
+`INSTALL' file but do not implement all of the features documented
+below.  The lack of an optional feature in a given package is not
+necessarily a bug.  More recommendations for GNU packages can be found
+in *note Makefile Conventions: (standards)Makefile Conventions.
+
+   The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation.  It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions.  Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, and a
+file `config.log' containing compiler output (useful mainly for
+debugging `configure').
+
+   It can also use an optional file (typically called `config.cache'
+and enabled with `--cache-file=config.cache' or simply `-C') that saves
+the results of its tests to speed up reconfiguring.  Caching is
+disabled by default to prevent problems with accidental use of stale
+cache files.
+
+   If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release.  If you are using the cache, and at
+some point `config.cache' contains results you don't want to keep, you
+may remove or edit it.
+
+   The file `configure.ac' (or `configure.in') is used to create
+`configure' by a program called `autoconf'.  You need `configure.ac' if
+you want to change it or regenerate `configure' using a newer version
+of `autoconf'.
+
+   The simplest way to compile this package is:
+
+  1. `cd' to the directory containing the package's source code and type
+     `./configure' to configure the package for your system.
+
+     Running `configure' might take a while.  While running, it prints
+     some messages telling which features it is checking for.
+
+  2. Type `make' to compile the package.
+
+  3. Optionally, type `make check' to run any self-tests that come with
+     the package, generally using the just-built uninstalled binaries.
+
+  4. Type `make install' to install the programs and any data files and
+     documentation.  When installing into a prefix owned by root, it is
+     recommended that the package be configured and built as a regular
+     user, and only the `make install' phase executed with root
+     privileges.
+
+  5. Optionally, type `make installcheck' to repeat any self-tests, but
+     this time using the binaries in their final installed location.
+     This target does not install anything.  Running this target as a
+     regular user, particularly if the prior `make install' required
+     root privileges, verifies that the installation completed
+     correctly.
+
+  6. You can remove the program binaries and object files from the
+     source code directory by typing `make clean'.  To also remove the
+     files that `configure' created (so you can compile the package for
+     a different kind of computer), type `make distclean'.  There is
+     also a `make maintainer-clean' target, but that is intended mainly
+     for the package's developers.  If you use it, you may have to get
+     all sorts of other programs in order to regenerate files that came
+     with the distribution.
+
+  7. Often, you can also type `make uninstall' to remove the installed
+     files again.  In practice, not all packages have tested that
+     uninstallation works correctly, even though it is required by the
+     GNU Coding Standards.
+
+  8. Some packages, particularly those that use Automake, provide `make
+     distcheck', which can by used by developers to test that all other
+     targets like `make install' and `make uninstall' work correctly.
+     This target is generally not run by end users.
+
+Compilers and Options
+=====================
+
+   Some systems require unusual options for compilation or linking that
+the `configure' script does not know about.  Run `./configure --help'
+for details on some of the pertinent environment variables.
+
+   You can give `configure' initial values for configuration parameters
+by setting variables in the command line or in the environment.  Here
+is an example:
+
+     ./configure CC=c99 CFLAGS=-g LIBS=-lposix
+
+   *Note Defining Variables::, for more details.
+
+Compiling For Multiple Architectures
+====================================
+
+   You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory.  To do this, you can use GNU `make'.  `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script.  `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'.  This
+is known as a "VPATH" build.
+
+   With a non-GNU `make', it is safer to compile the package for one
+architecture at a time in the source code directory.  After you have
+installed the package for one architecture, use `make distclean' before
+reconfiguring for another architecture.
+
+   On MacOS X 10.5 and later systems, you can create libraries and
+executables that work on multiple system types--known as "fat" or
+"universal" binaries--by specifying multiple `-arch' options to the
+compiler but only a single `-arch' option to the preprocessor.  Like
+this:
+
+     ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
+                 CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
+                 CPP="gcc -E" CXXCPP="g++ -E"
+
+   This is not guaranteed to produce working output in all cases, you
+may have to build one architecture at a time and combine the results
+using the `lipo' tool if you have problems.
+
+Installation Names
+==================
+
+   By default, `make install' installs the package's commands under
+`/usr/local/bin', include files under `/usr/local/include', etc.  You
+can specify an installation prefix other than `/usr/local' by giving
+`configure' the option `--prefix=PREFIX', where PREFIX must be an
+absolute file name.
+
+   You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files.  If you
+pass the option `--exec-prefix=PREFIX' to `configure', the package uses
+PREFIX as the prefix for installing programs and libraries.
+Documentation and other data files still use the regular prefix.
+
+   In addition, if you use an unusual directory layout you can give
+options like `--bindir=DIR' to specify different values for particular
+kinds of files.  Run `configure --help' for a list of the directories
+you can set and what kinds of files go in them.  In general, the
+default for these options is expressed in terms of `${prefix}', so that
+specifying just `--prefix' will affect all of the other directory
+specifications that were not explicitly provided.
+
+   The most portable way to affect installation locations is to pass the
+correct locations to `configure'; however, many packages provide one or
+both of the following shortcuts of passing variable assignments to the
+`make install' command line to change installation locations without
+having to reconfigure or recompile.
+
+   The first method involves providing an override variable for each
+affected directory.  For example, `make install
+prefix=/alternate/directory' will choose an alternate location for all
+directory configuration variables that were expressed in terms of
+`${prefix}'.  Any directories that were specified during `configure',
+but not in terms of `${prefix}', must each be overridden at install
+time for the entire installation to be relocated.  The approach of
+makefile variable overrides for each directory variable is required by
+the GNU Coding Standards, and ideally causes no recompilation.
+However, some platforms have known limitations with the semantics of
+shared libraries that end up requiring recompilation when using this
+method, particularly noticeable in packages that use GNU Libtool.
+
+   The second method involves providing the `DESTDIR' variable.  For
+example, `make install DESTDIR=/alternate/directory' will prepend
+`/alternate/directory' before all installation names.  The approach of
+`DESTDIR' overrides is not required by the GNU Coding Standards, and
+does not work on platforms that have drive letters.  On the other hand,
+it does better at avoiding recompilation issues, and works well even
+when some directory options were not specified in terms of `${prefix}'
+at `configure' time.
+
+Optional Features
+=================
+
+   If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+   Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System).  The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+   For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+   Some packages offer the ability to configure how verbose the
+execution of `make' will be.  For these packages, running `./configure
+--enable-silent-rules' sets the default to minimal output, which can be
+overridden with `make V=1'; while running `./configure
+--disable-silent-rules' sets the default to verbose, which can be
+overridden with `make V=0'.
+
+Particular systems
+==================
+
+   On HP-UX, the default C compiler is not ANSI C compatible.  If GNU
+CC is not installed, it is recommended to use the following options in
+order to use an ANSI C compiler:
+
+     ./configure CC="cc -Ae -D_XOPEN_SOURCE=500"
+
+and if that doesn't work, install pre-built binaries of GCC for HP-UX.
+
+   On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot
+parse its `<wchar.h>' header file.  The option `-nodtk' can be used as
+a workaround.  If GNU CC is not installed, it is therefore recommended
+to try
+
+     ./configure CC="cc"
+
+and if that doesn't work, try
+
+     ./configure CC="cc -nodtk"
+
+   On Solaris, don't put `/usr/ucb' early in your `PATH'.  This
+directory contains several dysfunctional programs; working variants of
+these programs are available in `/usr/bin'.  So, if you need `/usr/ucb'
+in your `PATH', put it _after_ `/usr/bin'.
+
+   On Haiku, software installed for all users goes in `/boot/common',
+not `/usr/local'.  It is recommended to use the following options:
+
+     ./configure --prefix=/boot/common
+
+Specifying the System Type
+==========================
+
+   There may be some features `configure' cannot figure out
+automatically, but needs to determine by the type of machine the package
+will run on.  Usually, assuming the package is built to be run on the
+_same_ architectures, `configure' can figure that out, but if it prints
+a message saying it cannot guess the machine type, give it the
+`--build=TYPE' option.  TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name which has the form:
+
+     CPU-COMPANY-SYSTEM
+
+where SYSTEM can have one of these forms:
+
+     OS
+     KERNEL-OS
+
+   See the file `config.sub' for the possible values of each field.  If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the machine type.
+
+   If you are _building_ compiler tools for cross-compiling, you should
+use the option `--target=TYPE' to select the type of system they will
+produce code for.
+
+   If you want to _use_ a cross compiler, that generates code for a
+platform different from the build platform, you should specify the
+"host" platform (i.e., that on which the generated programs will
+eventually be run) with `--host=TYPE'.
+
+Sharing Defaults
+================
+
+   If you want to set default values for `configure' scripts to share,
+you can create a site shell script called `config.site' that gives
+default values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists.  Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Defining Variables
+==================
+
+   Variables not defined in a site shell script can be set in the
+environment passed to `configure'.  However, some packages may run
+configure again during the build, and the customized values of these
+variables may be lost.  In order to avoid this problem, you should set
+them in the `configure' command line, using `VAR=value'.  For example:
+
+     ./configure CC=/usr/local2/bin/gcc
+
+causes the specified `gcc' to be used as the C compiler (unless it is
+overridden in the site shell script).
+
+Unfortunately, this technique does not work for `CONFIG_SHELL' due to
+an Autoconf bug.  Until the bug is fixed you can use this workaround:
+
+     CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash
+
+`configure' Invocation
+======================
+
+   `configure' recognizes the following options to control how it
+operates.
+
+`--help'
+`-h'
+     Print a summary of all of the options to `configure', and exit.
+
+`--help=short'
+`--help=recursive'
+     Print a summary of the options unique to this package's
+     `configure', and exit.  The `short' variant lists options used
+     only in the top level, while the `recursive' variant lists options
+     also present in any nested packages.
+
+`--version'
+`-V'
+     Print the version of Autoconf used to generate the `configure'
+     script, and exit.
+
+`--cache-file=FILE'
+     Enable the cache: use and save the results of the tests in FILE,
+     traditionally `config.cache'.  FILE defaults to `/dev/null' to
+     disable caching.
+
+`--config-cache'
+`-C'
+     Alias for `--cache-file=config.cache'.
+
+`--quiet'
+`--silent'
+`-q'
+     Do not print messages saying which checks are being made.  To
+     suppress all normal output, redirect it to `/dev/null' (any error
+     messages will still be shown).
+
+`--srcdir=DIR'
+     Look for the package's source code in directory DIR.  Usually
+     `configure' can determine that directory automatically.
+
+`--prefix=DIR'
+     Use DIR as the installation prefix.  *note Installation Names::
+     for more details, including other options available for fine-tuning
+     the installation locations.
+
+`--no-create'
+`-n'
+     Run the configure checks, but stop before creating any output
+     files.
+
+`configure' also accepts some other, not widely useful, options.  Run
+`configure --help' for more details.
+
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..4741c4d
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,9 @@
+# This Makefile.am is in the public domain
+## Process this file with automake to produce Makefile.in
+
+SUBDIRS = src po pkgconfig
+
+EXTRA_DIST = config.rpath  \
+  install-sh
+
+ACLOCAL_AMFLAGS = -I m4
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..e69de29
diff --git a/README b/README
new file mode 100644
index 0000000..b156738
--- /dev/null
+++ b/README
@@ -0,0 +1 @@
+Experimental code for SecuShare.
diff --git a/bootstrap b/bootstrap
new file mode 100755
index 0000000..c5d2098
--- /dev/null
+++ b/bootstrap
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+libtoolize --automake --copy --force
+aclocal -I m4
+autoheader
+autoconf
+automake --add-missing --copy
+
diff --git a/config.rpath b/config.rpath
new file mode 100755
index 0000000..17298f2
--- /dev/null
+++ b/config.rpath
@@ -0,0 +1,672 @@
+#! /bin/sh
+# Output a system dependent set of variables, describing how to set the
+# run time search path of shared libraries in an executable.
+#
+#   Copyright 1996-2010 Free Software Foundation, Inc.
+#   Taken from GNU libtool, 2001
+#   Originally by Gordon Matzigkeit <address@hidden>, 1996
+#
+#   This file is free software; the Free Software Foundation gives
+#   unlimited permission to copy and/or distribute it, with or without
+#   modifications, as long as this notice is preserved.
+#
+# The first argument passed to this file is the canonical host specification,
+#    CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
+# or
+#    CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
+# The environment variables CC, GCC, LDFLAGS, LD, with_gnu_ld
+# should be set by the caller.
+#
+# The set of defined variables is at the end of this script.
+
+# Known limitations:
+# - On IRIX 6.5 with CC="cc", the run time search patch must not be longer
+#   than 256 bytes, otherwise the compiler driver will dump core. The only
+#   known workaround is to choose shorter directory names for the build
+#   directory and/or the installation directory.
+
+# All known linkers require a `.a' archive for static linking (except MSVC,
+# which needs '.lib').
+libext=a
+shrext=.so
+
+host="$1"
+host_cpu=`echo "$host" | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'`
+host_vendor=`echo "$host" | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'`
+host_os=`echo "$host" | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'`
+
+# Code taken from libtool.m4's _LT_CC_BASENAME.
+
+for cc_temp in $CC""; do
+  case $cc_temp in
+    compile | *[\\/]compile | ccache | *[\\/]ccache ) ;;
+    distcc | *[\\/]distcc | purify | *[\\/]purify ) ;;
+    \-*) ;;
+    *) break;;
+  esac
+done
+cc_basename=`echo "$cc_temp" | sed -e 's%^.*/%%'`
+
+# Code taken from libtool.m4's _LT_COMPILER_PIC.
+
+wl=
+if test "$GCC" = yes; then
+  wl='-Wl,'
+else
+  case "$host_os" in
+    aix*)
+      wl='-Wl,'
+      ;;
+    darwin*)
+      case $cc_basename in
+        xlc*)
+          wl='-Wl,'
+          ;;
+      esac
+      ;;
+    mingw* | cygwin* | pw32* | os2* | cegcc*)
+      ;;
+    hpux9* | hpux10* | hpux11*)
+      wl='-Wl,'
+      ;;
+    irix5* | irix6* | nonstopux*)
+      wl='-Wl,'
+      ;;
+    newsos6)
+      ;;
+    linux* | k*bsd*-gnu)
+      case $cc_basename in
+        ecc*)
+          wl='-Wl,'
+          ;;
+        icc* | ifort*)
+          wl='-Wl,'
+          ;;
+        lf95*)
+          wl='-Wl,'
+          ;;
+        pgcc | pgf77 | pgf90)
+          wl='-Wl,'
+          ;;
+        ccc*)
+          wl='-Wl,'
+          ;;
+        como)
+          wl='-lopt='
+          ;;
+        *)
+          case `$CC -V 2>&1 | sed 5q` in
+            *Sun\ C*)
+              wl='-Wl,'
+              ;;
+          esac
+          ;;
+      esac
+      ;;
+    osf3* | osf4* | osf5*)
+      wl='-Wl,'
+      ;;
+    rdos*)
+      ;;
+    solaris*)
+      wl='-Wl,'
+      ;;
+    sunos4*)
+      wl='-Qoption ld '
+      ;;
+    sysv4 | sysv4.2uw2* | sysv4.3*)
+      wl='-Wl,'
+      ;;
+    sysv4*MP*)
+      ;;
+    sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*)
+      wl='-Wl,'
+      ;;
+    unicos*)
+      wl='-Wl,'
+      ;;
+    uts4*)
+      ;;
+  esac
+fi
+
+# Code taken from libtool.m4's _LT_LINKER_SHLIBS.
+
+hardcode_libdir_flag_spec=
+hardcode_libdir_separator=
+hardcode_direct=no
+hardcode_minus_L=no
+
+case "$host_os" in
+  cygwin* | mingw* | pw32* | cegcc*)
+    # FIXME: the MSVC++ port hasn't been tested in a loooong time
+    # When not using gcc, we currently assume that we are using
+    # Microsoft Visual C++.
+    if test "$GCC" != yes; then
+      with_gnu_ld=no
+    fi
+    ;;
+  interix*)
+    # we just hope/assume this is gcc and not c89 (= MSVC++)
+    with_gnu_ld=yes
+    ;;
+  openbsd*)
+    with_gnu_ld=no
+    ;;
+esac
+
+ld_shlibs=yes
+if test "$with_gnu_ld" = yes; then
+  # Set some defaults for GNU ld with shared library support. These
+  # are reset later if shared libraries are not supported. Putting them
+  # here allows them to be overridden if necessary.
+  # Unlike libtool, we use -rpath here, not --rpath, since the documented
+  # option of GNU ld is called -rpath, not --rpath.
+  hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+  case "$host_os" in
+    aix[3-9]*)
+      # On AIX/PPC, the GNU linker is very broken
+      if test "$host_cpu" != ia64; then
+        ld_shlibs=no
+      fi
+      ;;
+    amigaos*)
+      hardcode_libdir_flag_spec='-L$libdir'
+      hardcode_minus_L=yes
+      # Samuel A. Falvo II <address@hidden> reports
+      # that the semantics of dynamic libraries on AmigaOS, at least up
+      # to version 4, is to share data among multiple programs linked
+      # with the same dynamic library.  Since this doesn't match the
+      # behavior of shared libraries on other platforms, we cannot use
+      # them.
+      ld_shlibs=no
+      ;;
+    beos*)
+      if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then
+        :
+      else
+        ld_shlibs=no
+      fi
+      ;;
+    cygwin* | mingw* | pw32* | cegcc*)
+      # hardcode_libdir_flag_spec is actually meaningless, as there is
+      # no search path for DLLs.
+      hardcode_libdir_flag_spec='-L$libdir'
+      if $LD --help 2>&1 | grep 'auto-import' > /dev/null; then
+        :
+      else
+        ld_shlibs=no
+      fi
+      ;;
+    interix[3-9]*)
+      hardcode_direct=no
+      hardcode_libdir_flag_spec='${wl}-rpath,$libdir'
+      ;;
+    gnu* | linux* | k*bsd*-gnu)
+      if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then
+        :
+      else
+        ld_shlibs=no
+      fi
+      ;;
+    netbsd*)
+      ;;
+    solaris*)
+      if $LD -v 2>&1 | grep 'BFD 2\.8' > /dev/null; then
+        ld_shlibs=no
+      elif $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; 
then
+        :
+      else
+        ld_shlibs=no
+      fi
+      ;;
+    sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*)
+      case `$LD -v 2>&1` in
+        *\ [01].* | *\ 2.[0-9].* | *\ 2.1[0-5].*)
+          ld_shlibs=no
+          ;;
+        *)
+          if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; 
then
+            hardcode_libdir_flag_spec='`test -z "$SCOABSPATH" && echo 
${wl}-rpath,$libdir`'
+          else
+            ld_shlibs=no
+          fi
+          ;;
+      esac
+      ;;
+    sunos4*)
+      hardcode_direct=yes
+      ;;
+    *)
+      if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then
+        :
+      else
+        ld_shlibs=no
+      fi
+      ;;
+  esac
+  if test "$ld_shlibs" = no; then
+    hardcode_libdir_flag_spec=
+  fi
+else
+  case "$host_os" in
+    aix3*)
+      # Note: this linker hardcodes the directories in LIBPATH if there
+      # are no directories specified by -L.
+      hardcode_minus_L=yes
+      if test "$GCC" = yes; then
+        # Neither direct hardcoding nor static linking is supported with a
+        # broken collect2.
+        hardcode_direct=unsupported
+      fi
+      ;;
+    aix[4-9]*)
+      if test "$host_cpu" = ia64; then
+        # On IA64, the linker does run time linking by default, so we don't
+        # have to do anything special.
+        aix_use_runtimelinking=no
+      else
+        aix_use_runtimelinking=no
+        # Test if we are trying to use run time linking or normal
+        # AIX style linking. If -brtl is somewhere in LDFLAGS, we
+        # need to do runtime linking.
+        case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*)
+          for ld_flag in $LDFLAGS; do
+            if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then
+              aix_use_runtimelinking=yes
+              break
+            fi
+          done
+          ;;
+        esac
+      fi
+      hardcode_direct=yes
+      hardcode_libdir_separator=':'
+      if test "$GCC" = yes; then
+        case $host_os in aix4.[012]|aix4.[012].*)
+          collect2name=`${CC} -print-prog-name=collect2`
+          if test -f "$collect2name" && \
+            strings "$collect2name" | grep resolve_lib_name >/dev/null
+          then
+            # We have reworked collect2
+            :
+          else
+            # We have old collect2
+            hardcode_direct=unsupported
+            hardcode_minus_L=yes
+            hardcode_libdir_flag_spec='-L$libdir'
+            hardcode_libdir_separator=
+          fi
+          ;;
+        esac
+      fi
+      # Begin _LT_AC_SYS_LIBPATH_AIX.
+      echo 'int main () { return 0; }' > conftest.c
+      ${CC} ${LDFLAGS} conftest.c -o conftest
+      aix_libpath=`dump -H conftest 2>/dev/null | sed -n -e '/Import File 
Strings/,/^$/ { /^0/ { s/^0  *\(.*\)$/\1/; p; }
+}'`
+      if test -z "$aix_libpath"; then
+        aix_libpath=`dump -HX64 conftest 2>/dev/null | sed -n -e '/Import File 
Strings/,/^$/ { /^0/ { s/^0  *\(.*\)$/\1/; p; }
+}'`
+      fi
+      if test -z "$aix_libpath"; then
+        aix_libpath="/usr/lib:/lib"
+      fi
+      rm -f conftest.c conftest
+      # End _LT_AC_SYS_LIBPATH_AIX.
+      if test "$aix_use_runtimelinking" = yes; then
+        hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath"
+      else
+        if test "$host_cpu" = ia64; then
+          hardcode_libdir_flag_spec='${wl}-R $libdir:/usr/lib:/lib'
+        else
+          hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath"
+        fi
+      fi
+      ;;
+    amigaos*)
+      hardcode_libdir_flag_spec='-L$libdir'
+      hardcode_minus_L=yes
+      # see comment about different semantics on the GNU ld section
+      ld_shlibs=no
+      ;;
+    bsdi[45]*)
+      ;;
+    cygwin* | mingw* | pw32* | cegcc*)
+      # When not using gcc, we currently assume that we are using
+      # Microsoft Visual C++.
+      # hardcode_libdir_flag_spec is actually meaningless, as there is
+      # no search path for DLLs.
+      hardcode_libdir_flag_spec=' '
+      libext=lib
+      ;;
+    darwin* | rhapsody*)
+      hardcode_direct=no
+      if test "$GCC" = yes ; then
+        :
+      else
+        case $cc_basename in
+          xlc*)
+            ;;
+          *)
+            ld_shlibs=no
+            ;;
+        esac
+      fi
+      ;;
+    dgux*)
+      hardcode_libdir_flag_spec='-L$libdir'
+      ;;
+    freebsd1*)
+      ld_shlibs=no
+      ;;
+    freebsd2.2*)
+      hardcode_libdir_flag_spec='-R$libdir'
+      hardcode_direct=yes
+      ;;
+    freebsd2*)
+      hardcode_direct=yes
+      hardcode_minus_L=yes
+      ;;
+    freebsd* | dragonfly*)
+      hardcode_libdir_flag_spec='-R$libdir'
+      hardcode_direct=yes
+      ;;
+    hpux9*)
+      hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir'
+      hardcode_libdir_separator=:
+      hardcode_direct=yes
+      # hardcode_minus_L: Not really in the search PATH,
+      # but as the default location of the library.
+      hardcode_minus_L=yes
+      ;;
+    hpux10*)
+      if test "$with_gnu_ld" = no; then
+        hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir'
+        hardcode_libdir_separator=:
+        hardcode_direct=yes
+        # hardcode_minus_L: Not really in the search PATH,
+        # but as the default location of the library.
+        hardcode_minus_L=yes
+      fi
+      ;;
+    hpux11*)
+      if test "$with_gnu_ld" = no; then
+        hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir'
+        hardcode_libdir_separator=:
+        case $host_cpu in
+          hppa*64*|ia64*)
+            hardcode_direct=no
+            ;;
+          *)
+            hardcode_direct=yes
+            # hardcode_minus_L: Not really in the search PATH,
+            # but as the default location of the library.
+            hardcode_minus_L=yes
+            ;;
+        esac
+      fi
+      ;;
+    irix5* | irix6* | nonstopux*)
+      hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+      hardcode_libdir_separator=:
+      ;;
+    netbsd*)
+      hardcode_libdir_flag_spec='-R$libdir'
+      hardcode_direct=yes
+      ;;
+    newsos6)
+      hardcode_direct=yes
+      hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+      hardcode_libdir_separator=:
+      ;;
+    openbsd*)
+      if test -f /usr/libexec/ld.so; then
+        hardcode_direct=yes
+        if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test 
"$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+          hardcode_libdir_flag_spec='${wl}-rpath,$libdir'
+        else
+          case "$host_os" in
+            openbsd[01].* | openbsd2.[0-7] | openbsd2.[0-7].*)
+              hardcode_libdir_flag_spec='-R$libdir'
+              ;;
+            *)
+              hardcode_libdir_flag_spec='${wl}-rpath,$libdir'
+              ;;
+          esac
+        fi
+      else
+        ld_shlibs=no
+      fi
+      ;;
+    os2*)
+      hardcode_libdir_flag_spec='-L$libdir'
+      hardcode_minus_L=yes
+      ;;
+    osf3*)
+      hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+      hardcode_libdir_separator=:
+      ;;
+    osf4* | osf5*)
+      if test "$GCC" = yes; then
+        hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+      else
+        # Both cc and cxx compiler support -rpath directly
+        hardcode_libdir_flag_spec='-rpath $libdir'
+      fi
+      hardcode_libdir_separator=:
+      ;;
+    solaris*)
+      hardcode_libdir_flag_spec='-R$libdir'
+      ;;
+    sunos4*)
+      hardcode_libdir_flag_spec='-L$libdir'
+      hardcode_direct=yes
+      hardcode_minus_L=yes
+      ;;
+    sysv4)
+      case $host_vendor in
+        sni)
+          hardcode_direct=yes # is this really true???
+          ;;
+        siemens)
+          hardcode_direct=no
+          ;;
+        motorola)
+          hardcode_direct=no #Motorola manual says yes, but my tests say they 
lie
+          ;;
+      esac
+      ;;
+    sysv4.3*)
+      ;;
+    sysv4*MP*)
+      if test -d /usr/nec; then
+        ld_shlibs=yes
+      fi
+      ;;
+    sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | 
sco3.2v5.0.[024]*)
+      ;;
+    sysv5* | sco3.2v5* | sco5v6*)
+      hardcode_libdir_flag_spec='`test -z "$SCOABSPATH" && echo 
${wl}-R,$libdir`'
+      hardcode_libdir_separator=':'
+      ;;
+    uts4*)
+      hardcode_libdir_flag_spec='-L$libdir'
+      ;;
+    *)
+      ld_shlibs=no
+      ;;
+  esac
+fi
+
+# Check dynamic linker characteristics
+# Code taken from libtool.m4's _LT_SYS_DYNAMIC_LINKER.
+# Unlike libtool.m4, here we don't care about _all_ names of the library, but
+# only about the one the linker finds when passed -lNAME. This is the last
+# element of library_names_spec in libtool.m4, or possibly two of them if the
+# linker has special search rules.
+library_names_spec=      # the last element of library_names_spec in libtool.m4
+libname_spec='lib$name'
+case "$host_os" in
+  aix3*)
+    library_names_spec='$libname.a'
+    ;;
+  aix[4-9]*)
+    library_names_spec='$libname$shrext'
+    ;;
+  amigaos*)
+    library_names_spec='$libname.a'
+    ;;
+  beos*)
+    library_names_spec='$libname$shrext'
+    ;;
+  bsdi[45]*)
+    library_names_spec='$libname$shrext'
+    ;;
+  cygwin* | mingw* | pw32* | cegcc*)
+    shrext=.dll
+    library_names_spec='$libname.dll.a $libname.lib'
+    ;;
+  darwin* | rhapsody*)
+    shrext=.dylib
+    library_names_spec='$libname$shrext'
+    ;;
+  dgux*)
+    library_names_spec='$libname$shrext'
+    ;;
+  freebsd1*)
+    ;;
+  freebsd* | dragonfly*)
+    case "$host_os" in
+      freebsd[123]*)
+        library_names_spec='$libname$shrext$versuffix' ;;
+      *)
+        library_names_spec='$libname$shrext' ;;
+    esac
+    ;;
+  gnu*)
+    library_names_spec='$libname$shrext'
+    ;;
+  hpux9* | hpux10* | hpux11*)
+    case $host_cpu in
+      ia64*)
+        shrext=.so
+        ;;
+      hppa*64*)
+        shrext=.sl
+        ;;
+      *)
+        shrext=.sl
+        ;;
+    esac
+    library_names_spec='$libname$shrext'
+    ;;
+  interix[3-9]*)
+    library_names_spec='$libname$shrext'
+    ;;
+  irix5* | irix6* | nonstopux*)
+    library_names_spec='$libname$shrext'
+    case "$host_os" in
+      irix5* | nonstopux*)
+        libsuff= shlibsuff=
+        ;;
+      *)
+        case $LD in
+          *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") libsuff= shlibsuff= ;;
+          *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") libsuff=32 
shlibsuff=N32 ;;
+          *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") libsuff=64 shlibsuff=64 ;;
+          *) libsuff= shlibsuff= ;;
+        esac
+        ;;
+    esac
+    ;;
+  linux*oldld* | linux*aout* | linux*coff*)
+    ;;
+  linux* | k*bsd*-gnu)
+    library_names_spec='$libname$shrext'
+    ;;
+  knetbsd*-gnu)
+    library_names_spec='$libname$shrext'
+    ;;
+  netbsd*)
+    library_names_spec='$libname$shrext'
+    ;;
+  newsos6)
+    library_names_spec='$libname$shrext'
+    ;;
+  nto-qnx*)
+    library_names_spec='$libname$shrext'
+    ;;
+  openbsd*)
+    library_names_spec='$libname$shrext$versuffix'
+    ;;
+  os2*)
+    libname_spec='$name'
+    shrext=.dll
+    library_names_spec='$libname.a'
+    ;;
+  osf3* | osf4* | osf5*)
+    library_names_spec='$libname$shrext'
+    ;;
+  rdos*)
+    ;;
+  solaris*)
+    library_names_spec='$libname$shrext'
+    ;;
+  sunos4*)
+    library_names_spec='$libname$shrext$versuffix'
+    ;;
+  sysv4 | sysv4.3*)
+    library_names_spec='$libname$shrext'
+    ;;
+  sysv4*MP*)
+    library_names_spec='$libname$shrext'
+    ;;
+  sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
+    library_names_spec='$libname$shrext'
+    ;;
+  uts4*)
+    library_names_spec='$libname$shrext'
+    ;;
+esac
+
+sed_quote_subst='s/\(["`$\\]\)/\\\1/g'
+escaped_wl=`echo "X$wl" | sed -e 's/^X//' -e "$sed_quote_subst"`
+shlibext=`echo "$shrext" | sed -e 's,^\.,,'`
+escaped_libname_spec=`echo "X$libname_spec" | sed -e 's/^X//' -e 
"$sed_quote_subst"`
+escaped_library_names_spec=`echo "X$library_names_spec" | sed -e 's/^X//' -e 
"$sed_quote_subst"`
+escaped_hardcode_libdir_flag_spec=`echo "X$hardcode_libdir_flag_spec" | sed -e 
's/^X//' -e "$sed_quote_subst"`
+
+LC_ALL=C sed -e 's/^\([a-zA-Z0-9_]*\)=/acl_cv_\1=/' <<EOF
+
+# How to pass a linker flag through the compiler.
+wl="$escaped_wl"
+
+# Static library suffix (normally "a").
+libext="$libext"
+
+# Shared library suffix (normally "so").
+shlibext="$shlibext"
+
+# Format of library name prefix.
+libname_spec="$escaped_libname_spec"
+
+# Library names that the linker finds when passed -lNAME.
+library_names_spec="$escaped_library_names_spec"
+
+# Flag to hardcode \$libdir into a binary during linking.
+# This must work even if \$libdir does not exist.
+hardcode_libdir_flag_spec="$escaped_hardcode_libdir_flag_spec"
+
+# Whether we need a single -rpath flag with a separated argument.
+hardcode_libdir_separator="$hardcode_libdir_separator"
+
+# Set to yes if using DIR/libNAME.so during linking hardcodes DIR into the
+# resulting binary.
+hardcode_direct="$hardcode_direct"
+
+# Set to yes if using the -LDIR flag during linking hardcodes DIR into the
+# resulting binary.
+hardcode_minus_L="$hardcode_minus_L"
+
+EOF
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..5276c8b
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,221 @@
+# This file is part of GNUnet.
+# (C) 2001-2019 Christian Grothoff (and other contributing authors)
+#
+# GNUnet is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published
+# by the Free Software Foundation; either version 2, or (at your
+# option) any later version.
+#
+# GNUnet is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNUnet; see the file COPYING.  If not, write to the
+# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301, USA.
+#
+#
+# Process this file with autoconf to produce a configure script.
+#
+AC_PREREQ(2.61)
+AC_INIT([gnunet-secushare],[0.0.0],address@hidden)
+AM_INIT_AUTOMAKE([gnunet-secushare], [0.0.0])
+AM_CONFIG_HEADER(gnunet_secushare_config.h)
+
+AH_TOP([#define _GNU_SOURCE  1])
+
+AC_ISC_POSIX
+AC_PROG_AWK
+AC_PROG_CC
+
+AC_PROG_MKDIR_P
+AC_PROG_CPP
+AC_PROG_INSTALL
+AC_PROG_LN_S
+AC_PROG_MAKE_SET
+AC_LIBTOOL_WIN32_DLL
+AC_PROG_CC
+AM_PROG_CC_STDC
+AC_HEADER_STDC
+AC_CANONICAL_HOST
+
+# dynamic libraries/plugins
+AC_DISABLE_STATIC
+AC_PROG_LIBTOOL
+
+AC_SYS_LARGEFILE
+AC_FUNC_FSEEKO
+
+CFLAGS="-Wall $CFLAGS"
+# use '-fno-strict-aliasing', but only if the compiler can take it
+if gcc -fno-strict-aliasing -S -o /dev/null -xc /dev/null >/dev/null 2>&1;
+then
+ CFLAGS="-fno-strict-aliasing $CFLAGS"
+fi
+
+
+# Check system type
+case "$host_os" in
+*darwin* | *rhapsody* | *macosx*)
+     AC_DEFINE_UNQUOTED(OSX,1,[This is an OS X system])
+     CFLAGS="-no-cpp-precomp $CFLAGS"
+     LDFLAGS="-flat_namespace -undefined suppress $LDFLAGS"
+     ;;
+linux*)
+     AC_DEFINE_UNQUOTED(LINUX,1,[This is a Linux system])
+     ;;
+freebsd*)
+     AC_DEFINE_UNQUOTED(SOMEBSD,1,[This is a BSD system])
+     AC_DEFINE_UNQUOTED(FREEBSD,1,[This is a FreeBSD system])
+     ;;
+openbsd*)
+     AC_DEFINE_UNQUOTED(SOMEBSD,1,[This is a BSD system])
+     AC_DEFINE_UNQUOTED(OPENBSD,1,[This is an OpenBSD system])
+     ;;
+netbsd*)
+     AC_DEFINE_UNQUOTED(SOMEBSD,1,[This is a BSD system])
+     AC_DEFINE_UNQUOTED(NETBSD,1,[This is a NetBSD system])
+     ;;
+*solaris*)
+     AC_DEFINE_UNQUOTED(SOLARIS,1,[This is a Solaris system])
+     AC_DEFINE_UNQUOTED(_REENTRANT,1,[Need with solaris or errno doesnt work])
+     build_target="solaris"
+     ;;
+*arm-linux*)
+     AC_DEFINE_UNQUOTED(LINUX,1,[This is a Linux system])
+     ;;
+*cygwin*)
+     AC_DEFINE_UNQUOTED(CYGWIN,1,[This is a Cygwin system])
+     AC_DEFINE_UNQUOTED(WINDOWS,1,[This is a Windows system])
+     AC_CHECK_LIB(intl, gettext)
+     LDFLAGS="$LDFLAGS -no-undefined"
+     build_target="cygwin"
+     ;;
+*mingw*)
+     AC_DEFINE_UNQUOTED(MINGW,1,[This is a MinGW system])
+     AC_DEFINE_UNQUOTED(WINDOWS,1,[This is a Windows system])
+     AC_DEFINE_UNQUOTED(_WIN32,1,[This is a Windows system])
+     AC_CHECK_LIB(intl, gettext)
+     LDFLAGS="$LDFLAGS -no-undefined -Wl,--export-all-symbols -lws2_32"
+     CFLAGS="-mms-bitfields $CFLAGS"
+     build_target="mingw"
+     ;;
+*)
+     AC_MSG_RESULT(Unrecognised OS $host_os)
+     AC_DEFINE_UNQUOTED(OTHEROS,1,[Some strange OS])
+;;
+esac
+
+AM_CONDITIONAL(MINGW,   test "$build_target" = "mingw")
+
+# check for gettext
+AM_GNU_GETTEXT_VERSION([0.18.1])
+AM_GNU_GETTEXT([external])
+
+AC_CHECK_HEADERS([errno.h stdio.h unistd.h locale.h sys/stat.h sys/types.h 
langinfo.h libintl.h unistd.h stddef.h argz.h sys/socket.h netinet/in.h 
stdarg.h])
+
+# test for GNUnet core
+gnunet=0
+lookin=${prefix}
+backup_LDFLAGS="$LDFLAGS"
+backup_CPPFLAGS="$CPPFLAGS"
+GNUNET_LDFLAGS=""
+GNUNET_CPPFLAGS=""
+AC_MSG_CHECKING(for GNUnet core)
+AC_ARG_WITH(gnunet,
+   [  --with-gnunet=PFX       Base of GNUnet installation],
+   [AC_MSG_RESULT([$with_gnunet])
+    case $with_gnunet in
+      no)
+        lookin=""
+        ;;
+      yes)
+        lookin="${prefix}"
+        ;;
+      *)
+        lookin="$with_gnunet"
+        ;;
+    esac
+   ],
+   [
+     AC_MSG_RESULT([--with-gnunet not specified])
+     PKG_CHECK_MODULES([GNUNET], [gnunetutil >= 0.9.0], gnunet=1)
+   ]
+)
+
+if test "x$gnunet" == "x0" -a ! "x$lookin" == "x"
+then
+  AC_MSG_CHECKING(for GNUnet util library in $lookin)
+  GNUNET_LDFLAGS="-L${lookin}/lib"
+  GNUNET_CPPFLAGS="-I${lookin}/include"
+  LDFLAGS="$GNUNET_LDFLAGS $backup_LDFLAGS"
+  CPPFLAGS="$GNUNET_CPPFLAGS $backup_CPPFLAGS"
+  AC_CHECK_HEADERS([gnunet/gnunet_util_lib.h],
+    AC_CHECK_LIB([gnunetutil], [GNUNET_xfree_],
+      [
+        gnunet=1
+        EXT_LIB_PATH="-L${lookin}/lib $EXT_LIB_PATH"
+      ]
+    ),,[#include <gnunet/platform.h>]
+  )
+fi
+
+if test "x$gnunet" == "x0"
+then
+  AC_MSG_ERROR([gnunet-ext requires GNUnet])
+fi
+
+
+
+# Linker hardening options
+# Currently these options are ELF specific - you can't use this with MacOSX
+AC_ARG_ENABLE(linker-hardening,
+  AS_HELP_STRING(--enable-linker-hardening, enable linker security fixups),
+[if test x$enableval = xyes; then
+   LDFLAGS="$LDFLAGS -z relro -z now"
+fi])
+
+
+extra_logging=GNUNET_NO
+AC_ARG_ENABLE([logging],
+   AS_HELP_STRING([--enable-logging@<:@=value@:>@],[Enable logging calls. 
Possible values: yes,no,verbose,veryverbose ('yes' is the default)]),
+   [AS_IF([test "x$enableval" = "xyes"], [],
+          [test "x$enableval" = "xno"], 
[AC_DEFINE([GNUNET_CULL_LOGGING],[],[Define to cull all logging calls])],
+          [test "x$enableval" = "xverbose"], [extra_logging=GNUNET_YES]
+          [test "x$enableval" = "xveryverbose"], 
[extra_logging=\(GNUNET_YES+1\)])
+   ], [])
+AC_DEFINE_UNQUOTED([GNUNET_EXTRA_LOGGING],[$extra_logging],[1 if extra logging 
is enabled, 2 for very verbose extra logging, 0 otherwise])
+
+
+AC_SUBST(GNUNET_CPPFLAGS)
+AC_SUBST(GNUNET_LDFLAGS)
+LDFLAGS="$backup_LDFLAGS"
+CPPFLAGS="$backup_CPPFLAGS"
+
+AC_DEFINE_DIR([PACKAGE_DATA], [datarootdir], [The directory for installing 
read-only architecture-independent data])
+
+# Set PACKAGE_SOURCE_DIR in gnunet_ext_config.h.
+packagesrcdir=`cd $srcdir && pwd`
+AC_DEFINE_UNQUOTED(PACKAGE_SOURCE_DIR, "${packagesrcdir}", [source dir])
+
+AC_OUTPUT([ po/Makefile.in
+Makefile
+pkgconfig/Makefile
+src/Makefile
+src/include/Makefile
+src/multicast/Makefile
+src/multicast/multicast.conf
+src/psycutil/Makefile
+src/psyc/Makefile
+src/psyc/psyc.conf
+src/psycstore/Makefile
+src/psycstore/psycstore.conf
+src/social/Makefile
+src/social/social.conf
+pkgconfig/gnunetmulticast.pc
+pkgconfig/gnunetpsyc.pc
+pkgconfig/gnunetpsycstore.pc
+pkgconfig/gnunetsocial.pc
+])
diff --git a/m4/ac_define_dir.m4 b/m4/ac_define_dir.m4
new file mode 100644
index 0000000..f7e028f
--- /dev/null
+++ b/m4/ac_define_dir.m4
@@ -0,0 +1,35 @@
+dnl @synopsis AC_DEFINE_DIR(VARNAME, DIR [, DESCRIPTION])
+dnl
+dnl This macro _AC_DEFINEs VARNAME to the expansion of the DIR
+dnl variable, taking care of fixing up ${prefix} and such.
+dnl
+dnl VARNAME is offered as both a C preprocessor symbol, and an output
+dnl variable.
+dnl
+dnl Note that the 3 argument form is only supported with autoconf 2.13
+dnl and later (i.e. only where _AC_DEFINE supports 3 arguments).
+dnl
+dnl Examples:
+dnl
+dnl    AC_DEFINE_DIR(DATADIR, datadir)
+dnl    AC_DEFINE_DIR(PROG_PATH, bindir, [Location of installed binaries])
+dnl
+dnl @category Misc
+dnl @author Stepan Kasal <address@hidden>
+dnl @author Andreas Schwab <address@hidden>
+dnl @author Guido Draheim <address@hidden>
+dnl @author Alexandre Oliva
+dnl @version 2005-01-17
+dnl @license AllPermissive
+
+AC_DEFUN([AC_DEFINE_DIR], [
+  prefix_NONE=
+  exec_prefix_NONE=
+  test "x$prefix" = xNONE && prefix_NONE=yes && prefix=$ac_default_prefix
+  test "x$exec_prefix" = xNONE && exec_prefix_NONE=yes && exec_prefix=$prefix
+  eval ac_define_dir="\"[$]$2\""
+  AC_SUBST($1, "$ac_define_dir")
+  AC_DEFINE_UNQUOTED($1, "$ac_define_dir", [$3])
+  test "$prefix_NONE" && prefix=NONE
+  test "$exec_prefix_NONE" && exec_prefix=NONE
+])
diff --git a/m4/gettext.m4 b/m4/gettext.m4
new file mode 100644
index 0000000..f84e6a5
--- /dev/null
+++ b/m4/gettext.m4
@@ -0,0 +1,383 @@
+# gettext.m4 serial 63 (gettext-0.18)
+dnl Copyright (C) 1995-2010 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+dnl
+dnl This file can can be used in projects which are not available under
+dnl the GNU General Public License or the GNU Library General Public
+dnl License but which still want to provide support for the GNU gettext
+dnl functionality.
+dnl Please note that the actual code of the GNU gettext library is covered
+dnl by the GNU Library General Public License, and the rest of the GNU
+dnl gettext package package is covered by the GNU General Public License.
+dnl They are *not* in the public domain.
+
+dnl Authors:
+dnl   Ulrich Drepper <address@hidden>, 1995-2000.
+dnl   Bruno Haible <address@hidden>, 2000-2006, 2008-2010.
+
+dnl Macro to add for using GNU gettext.
+
+dnl Usage: AM_GNU_GETTEXT([INTLSYMBOL], [NEEDSYMBOL], [INTLDIR]).
+dnl INTLSYMBOL can be one of 'external', 'no-libtool', 'use-libtool'. The
+dnl    default (if it is not specified or empty) is 'no-libtool'.
+dnl    INTLSYMBOL should be 'external' for packages with no intl directory,
+dnl    and 'no-libtool' or 'use-libtool' for packages with an intl directory.
+dnl    If INTLSYMBOL is 'use-libtool', then a libtool library
+dnl    $(top_builddir)/intl/libintl.la will be created (shared and/or static,
+dnl    depending on --{enable,disable}-{shared,static} and on the presence of
+dnl    AM-DISABLE-SHARED). If INTLSYMBOL is 'no-libtool', a static library
+dnl    $(top_builddir)/intl/libintl.a will be created.
+dnl If NEEDSYMBOL is specified and is 'need-ngettext', then GNU gettext
+dnl    implementations (in libc or libintl) without the ngettext() function
+dnl    will be ignored.  If NEEDSYMBOL is specified and is
+dnl    'need-formatstring-macros', then GNU gettext implementations that don't
+dnl    support the ISO C 99 <inttypes.h> formatstring macros will be ignored.
+dnl INTLDIR is used to find the intl libraries.  If empty,
+dnl    the value `$(top_builddir)/intl/' is used.
+dnl
+dnl The result of the configuration is one of three cases:
+dnl 1) GNU gettext, as included in the intl subdirectory, will be compiled
+dnl    and used.
+dnl    Catalog format: GNU --> install in $(datadir)
+dnl    Catalog extension: .mo after installation, .gmo in source tree
+dnl 2) GNU gettext has been found in the system's C library.
+dnl    Catalog format: GNU --> install in $(datadir)
+dnl    Catalog extension: .mo after installation, .gmo in source tree
+dnl 3) No internationalization, always use English msgid.
+dnl    Catalog format: none
+dnl    Catalog extension: none
+dnl If INTLSYMBOL is 'external', only cases 2 and 3 can occur.
+dnl The use of .gmo is historical (it was needed to avoid overwriting the
+dnl GNU format catalogs when building on a platform with an X/Open gettext),
+dnl but we keep it in order not to force irrelevant filename changes on the
+dnl maintainers.
+dnl
+AC_DEFUN([AM_GNU_GETTEXT],
+[
+  dnl Argument checking.
+  ifelse([$1], [], , [ifelse([$1], [external], , [ifelse([$1], [no-libtool], , 
[ifelse([$1], [use-libtool], ,
+    [errprint([ERROR: invalid first argument to AM_GNU_GETTEXT
+])])])])])
+  ifelse(ifelse([$1], [], [old])[]ifelse([$1], [no-libtool], [old]), [old],
+    [AC_DIAGNOSE([obsolete], [Use of AM_GNU_GETTEXT without [external] 
argument is deprecated.])])
+  ifelse([$2], [], , [ifelse([$2], [need-ngettext], , [ifelse([$2], 
[need-formatstring-macros], ,
+    [errprint([ERROR: invalid second argument to AM_GNU_GETTEXT
+])])])])
+  define([gt_included_intl],
+    ifelse([$1], [external],
+      ifdef([AM_GNU_GETTEXT_][INTL_SUBDIR], [yes], [no]),
+      [yes]))
+  define([gt_libtool_suffix_prefix], ifelse([$1], [use-libtool], [l], []))
+  gt_NEEDS_INIT
+  AM_GNU_GETTEXT_NEED([$2])
+
+  AC_REQUIRE([AM_PO_SUBDIRS])dnl
+  ifelse(gt_included_intl, yes, [
+    AC_REQUIRE([AM_INTL_SUBDIR])dnl
+  ])
+
+  dnl Prerequisites of AC_LIB_LINKFLAGS_BODY.
+  AC_REQUIRE([AC_LIB_PREPARE_PREFIX])
+  AC_REQUIRE([AC_LIB_RPATH])
+
+  dnl Sometimes libintl requires libiconv, so first search for libiconv.
+  dnl Ideally we would do this search only after the
+  dnl      if test "$USE_NLS" = "yes"; then
+  dnl        if { eval "gt_val=\$$gt_func_gnugettext_libc"; test "$gt_val" != 
"yes"; }; then
+  dnl tests. But if configure.in invokes AM_ICONV after AM_GNU_GETTEXT
+  dnl the configure script would need to contain the same shell code
+  dnl again, outside any 'if'. There are two solutions:
+  dnl - Invoke AM_ICONV_LINKFLAGS_BODY here, outside any 'if'.
+  dnl - Control the expansions in more detail using AC_PROVIDE_IFELSE.
+  dnl Since AC_PROVIDE_IFELSE is only in autoconf >= 2.52 and not
+  dnl documented, we avoid it.
+  ifelse(gt_included_intl, yes, , [
+    AC_REQUIRE([AM_ICONV_LINKFLAGS_BODY])
+  ])
+
+  dnl Sometimes, on MacOS X, libintl requires linking with CoreFoundation.
+  gt_INTL_MACOSX
+
+  dnl Set USE_NLS.
+  AC_REQUIRE([AM_NLS])
+
+  ifelse(gt_included_intl, yes, [
+    BUILD_INCLUDED_LIBINTL=no
+    USE_INCLUDED_LIBINTL=no
+  ])
+  LIBINTL=
+  LTLIBINTL=
+  POSUB=
+
+  dnl Add a version number to the cache macros.
+  case " $gt_needs " in
+    *" need-formatstring-macros "*) gt_api_version=3 ;;
+    *" need-ngettext "*) gt_api_version=2 ;;
+    *) gt_api_version=1 ;;
+  esac
+  gt_func_gnugettext_libc="gt_cv_func_gnugettext${gt_api_version}_libc"
+  gt_func_gnugettext_libintl="gt_cv_func_gnugettext${gt_api_version}_libintl"
+
+  dnl If we use NLS figure out what method
+  if test "$USE_NLS" = "yes"; then
+    gt_use_preinstalled_gnugettext=no
+    ifelse(gt_included_intl, yes, [
+      AC_MSG_CHECKING([whether included gettext is requested])
+      AC_ARG_WITH([included-gettext],
+        [  --with-included-gettext use the GNU gettext library included here],
+        nls_cv_force_use_gnu_gettext=$withval,
+        nls_cv_force_use_gnu_gettext=no)
+      AC_MSG_RESULT([$nls_cv_force_use_gnu_gettext])
+
+      nls_cv_use_gnu_gettext="$nls_cv_force_use_gnu_gettext"
+      if test "$nls_cv_force_use_gnu_gettext" != "yes"; then
+    ])
+        dnl User does not insist on using GNU NLS library.  Figure out what
+        dnl to use.  If GNU gettext is available we use this.  Else we have
+        dnl to fall back to GNU NLS library.
+
+        if test $gt_api_version -ge 3; then
+          gt_revision_test_code='
+#ifndef __GNU_GETTEXT_SUPPORTED_REVISION
+#define __GNU_GETTEXT_SUPPORTED_REVISION(major) ((major) == 0 ? 0 : -1)
+#endif
+changequote(,)dnl
+typedef int array [2 * (__GNU_GETTEXT_SUPPORTED_REVISION(0) >= 1) - 1];
+changequote([,])dnl
+'
+        else
+          gt_revision_test_code=
+        fi
+        if test $gt_api_version -ge 2; then
+          gt_expression_test_code=' + * ngettext ("", "", 0)'
+        else
+          gt_expression_test_code=
+        fi
+
+        AC_CACHE_CHECK([for GNU gettext in libc], [$gt_func_gnugettext_libc],
+         [AC_TRY_LINK([#include <libintl.h>
+$gt_revision_test_code
+extern int _nl_msg_cat_cntr;
+extern int *_nl_domain_bindings;],
+            [bindtextdomain ("", "");
+return * gettext ("")$gt_expression_test_code + _nl_msg_cat_cntr + 
*_nl_domain_bindings],
+            [eval "$gt_func_gnugettext_libc=yes"],
+            [eval "$gt_func_gnugettext_libc=no"])])
+
+        if { eval "gt_val=\$$gt_func_gnugettext_libc"; test "$gt_val" != 
"yes"; }; then
+          dnl Sometimes libintl requires libiconv, so first search for 
libiconv.
+          ifelse(gt_included_intl, yes, , [
+            AM_ICONV_LINK
+          ])
+          dnl Search for libintl and define LIBINTL, LTLIBINTL and INCINTL
+          dnl accordingly. Don't use AC_LIB_LINKFLAGS_BODY([intl],[iconv])
+          dnl because that would add "-liconv" to LIBINTL and LTLIBINTL
+          dnl even if libiconv doesn't exist.
+          AC_LIB_LINKFLAGS_BODY([intl])
+          AC_CACHE_CHECK([for GNU gettext in libintl],
+            [$gt_func_gnugettext_libintl],
+           [gt_save_CPPFLAGS="$CPPFLAGS"
+            CPPFLAGS="$CPPFLAGS $INCINTL"
+            gt_save_LIBS="$LIBS"
+            LIBS="$LIBS $LIBINTL"
+            dnl Now see whether libintl exists and does not depend on libiconv.
+            AC_TRY_LINK([#include <libintl.h>
+$gt_revision_test_code
+extern int _nl_msg_cat_cntr;
+extern
+#ifdef __cplusplus
+"C"
+#endif
+const char *_nl_expand_alias (const char *);],
+              [bindtextdomain ("", "");
+return * gettext ("")$gt_expression_test_code + _nl_msg_cat_cntr + 
*_nl_expand_alias ("")],
+              [eval "$gt_func_gnugettext_libintl=yes"],
+              [eval "$gt_func_gnugettext_libintl=no"])
+            dnl Now see whether libintl exists and depends on libiconv.
+            if { eval "gt_val=\$$gt_func_gnugettext_libintl"; test "$gt_val" 
!= yes; } && test -n "$LIBICONV"; then
+              LIBS="$LIBS $LIBICONV"
+              AC_TRY_LINK([#include <libintl.h>
+$gt_revision_test_code
+extern int _nl_msg_cat_cntr;
+extern
+#ifdef __cplusplus
+"C"
+#endif
+const char *_nl_expand_alias (const char *);],
+                [bindtextdomain ("", "");
+return * gettext ("")$gt_expression_test_code + _nl_msg_cat_cntr + 
*_nl_expand_alias ("")],
+               [LIBINTL="$LIBINTL $LIBICONV"
+                LTLIBINTL="$LTLIBINTL $LTLIBICONV"
+                eval "$gt_func_gnugettext_libintl=yes"
+               ])
+            fi
+            CPPFLAGS="$gt_save_CPPFLAGS"
+            LIBS="$gt_save_LIBS"])
+        fi
+
+        dnl If an already present or preinstalled GNU gettext() is found,
+        dnl use it.  But if this macro is used in GNU gettext, and GNU
+        dnl gettext is already preinstalled in libintl, we update this
+        dnl libintl.  (Cf. the install rule in intl/Makefile.in.)
+        if { eval "gt_val=\$$gt_func_gnugettext_libc"; test "$gt_val" = "yes"; 
} \
+           || { { eval "gt_val=\$$gt_func_gnugettext_libintl"; test "$gt_val" 
= "yes"; } \
+                && test "$PACKAGE" != gettext-runtime \
+                && test "$PACKAGE" != gettext-tools; }; then
+          gt_use_preinstalled_gnugettext=yes
+        else
+          dnl Reset the values set by searching for libintl.
+          LIBINTL=
+          LTLIBINTL=
+          INCINTL=
+        fi
+
+    ifelse(gt_included_intl, yes, [
+        if test "$gt_use_preinstalled_gnugettext" != "yes"; then
+          dnl GNU gettext is not found in the C library.
+          dnl Fall back on included GNU gettext library.
+          nls_cv_use_gnu_gettext=yes
+        fi
+      fi
+
+      if test "$nls_cv_use_gnu_gettext" = "yes"; then
+        dnl Mark actions used to generate GNU NLS library.
+        BUILD_INCLUDED_LIBINTL=yes
+        USE_INCLUDED_LIBINTL=yes
+        
LIBINTL="ifelse([$3],[],\${top_builddir}/intl,[$3])/libintl.[]gt_libtool_suffix_prefix[]a
 $LIBICONV $LIBTHREAD"
+        
LTLIBINTL="ifelse([$3],[],\${top_builddir}/intl,[$3])/libintl.[]gt_libtool_suffix_prefix[]a
 $LTLIBICONV $LTLIBTHREAD"
+        LIBS=`echo " $LIBS " | sed -e 's/ -lintl / /' -e 's/^ //' -e 's/ $//'`
+      fi
+
+      CATOBJEXT=
+      if test "$gt_use_preinstalled_gnugettext" = "yes" \
+         || test "$nls_cv_use_gnu_gettext" = "yes"; then
+        dnl Mark actions to use GNU gettext tools.
+        CATOBJEXT=.gmo
+      fi
+    ])
+
+    if test -n "$INTL_MACOSX_LIBS"; then
+      if test "$gt_use_preinstalled_gnugettext" = "yes" \
+         || test "$nls_cv_use_gnu_gettext" = "yes"; then
+        dnl Some extra flags are needed during linking.
+        LIBINTL="$LIBINTL $INTL_MACOSX_LIBS"
+        LTLIBINTL="$LTLIBINTL $INTL_MACOSX_LIBS"
+      fi
+    fi
+
+    if test "$gt_use_preinstalled_gnugettext" = "yes" \
+       || test "$nls_cv_use_gnu_gettext" = "yes"; then
+      AC_DEFINE([ENABLE_NLS], [1],
+        [Define to 1 if translation of program messages to the user's native 
language
+   is requested.])
+    else
+      USE_NLS=no
+    fi
+  fi
+
+  AC_MSG_CHECKING([whether to use NLS])
+  AC_MSG_RESULT([$USE_NLS])
+  if test "$USE_NLS" = "yes"; then
+    AC_MSG_CHECKING([where the gettext function comes from])
+    if test "$gt_use_preinstalled_gnugettext" = "yes"; then
+      if { eval "gt_val=\$$gt_func_gnugettext_libintl"; test "$gt_val" = 
"yes"; }; then
+        gt_source="external libintl"
+      else
+        gt_source="libc"
+      fi
+    else
+      gt_source="included intl directory"
+    fi
+    AC_MSG_RESULT([$gt_source])
+  fi
+
+  if test "$USE_NLS" = "yes"; then
+
+    if test "$gt_use_preinstalled_gnugettext" = "yes"; then
+      if { eval "gt_val=\$$gt_func_gnugettext_libintl"; test "$gt_val" = 
"yes"; }; then
+        AC_MSG_CHECKING([how to link with libintl])
+        AC_MSG_RESULT([$LIBINTL])
+        AC_LIB_APPENDTOVAR([CPPFLAGS], [$INCINTL])
+      fi
+
+      dnl For backward compatibility. Some packages may be using this.
+      AC_DEFINE([HAVE_GETTEXT], [1],
+       [Define if the GNU gettext() function is already present or 
preinstalled.])
+      AC_DEFINE([HAVE_DCGETTEXT], [1],
+       [Define if the GNU dcgettext() function is already present or 
preinstalled.])
+    fi
+
+    dnl We need to process the po/ directory.
+    POSUB=po
+  fi
+
+  ifelse(gt_included_intl, yes, [
+    dnl If this is used in GNU gettext we have to set BUILD_INCLUDED_LIBINTL
+    dnl to 'yes' because some of the testsuite requires it.
+    if test "$PACKAGE" = gettext-runtime || test "$PACKAGE" = gettext-tools; 
then
+      BUILD_INCLUDED_LIBINTL=yes
+    fi
+
+    dnl Make all variables we use known to autoconf.
+    AC_SUBST([BUILD_INCLUDED_LIBINTL])
+    AC_SUBST([USE_INCLUDED_LIBINTL])
+    AC_SUBST([CATOBJEXT])
+
+    dnl For backward compatibility. Some configure.ins may be using this.
+    nls_cv_header_intl=
+    nls_cv_header_libgt=
+
+    dnl For backward compatibility. Some Makefiles may be using this.
+    DATADIRNAME=share
+    AC_SUBST([DATADIRNAME])
+
+    dnl For backward compatibility. Some Makefiles may be using this.
+    INSTOBJEXT=.mo
+    AC_SUBST([INSTOBJEXT])
+
+    dnl For backward compatibility. Some Makefiles may be using this.
+    GENCAT=gencat
+    AC_SUBST([GENCAT])
+
+    dnl For backward compatibility. Some Makefiles may be using this.
+    INTLOBJS=
+    if test "$USE_INCLUDED_LIBINTL" = yes; then
+      INTLOBJS="\$(GETTOBJS)"
+    fi
+    AC_SUBST([INTLOBJS])
+
+    dnl Enable libtool support if the surrounding package wishes it.
+    INTL_LIBTOOL_SUFFIX_PREFIX=gt_libtool_suffix_prefix
+    AC_SUBST([INTL_LIBTOOL_SUFFIX_PREFIX])
+  ])
+
+  dnl For backward compatibility. Some Makefiles may be using this.
+  INTLLIBS="$LIBINTL"
+  AC_SUBST([INTLLIBS])
+
+  dnl Make all documented variables known to autoconf.
+  AC_SUBST([LIBINTL])
+  AC_SUBST([LTLIBINTL])
+  AC_SUBST([POSUB])
+])
+
+
+dnl gt_NEEDS_INIT ensures that the gt_needs variable is initialized.
+m4_define([gt_NEEDS_INIT],
+[
+  m4_divert_text([DEFAULTS], [gt_needs=])
+  m4_define([gt_NEEDS_INIT], [])
+])
+
+
+dnl Usage: AM_GNU_GETTEXT_NEED([NEEDSYMBOL])
+AC_DEFUN([AM_GNU_GETTEXT_NEED],
+[
+  m4_divert_text([INIT_PREPARE], [gt_needs="$gt_needs $1"])
+])
+
+
+dnl Usage: AM_GNU_GETTEXT_VERSION([gettext-version])
+AC_DEFUN([AM_GNU_GETTEXT_VERSION], [])
diff --git a/m4/iconv.m4 b/m4/iconv.m4
new file mode 100644
index 0000000..e2041b9
--- /dev/null
+++ b/m4/iconv.m4
@@ -0,0 +1,214 @@
+# iconv.m4 serial 11 (gettext-0.18.1)
+dnl Copyright (C) 2000-2002, 2007-2010 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl From Bruno Haible.
+
+AC_DEFUN([AM_ICONV_LINKFLAGS_BODY],
+[
+  dnl Prerequisites of AC_LIB_LINKFLAGS_BODY.
+  AC_REQUIRE([AC_LIB_PREPARE_PREFIX])
+  AC_REQUIRE([AC_LIB_RPATH])
+
+  dnl Search for libiconv and define LIBICONV, LTLIBICONV and INCICONV
+  dnl accordingly.
+  AC_LIB_LINKFLAGS_BODY([iconv])
+])
+
+AC_DEFUN([AM_ICONV_LINK],
+[
+  dnl Some systems have iconv in libc, some have it in libiconv (OSF/1 and
+  dnl those with the standalone portable GNU libiconv installed).
+  AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
+
+  dnl Search for libiconv and define LIBICONV, LTLIBICONV and INCICONV
+  dnl accordingly.
+  AC_REQUIRE([AM_ICONV_LINKFLAGS_BODY])
+
+  dnl Add $INCICONV to CPPFLAGS before performing the following checks,
+  dnl because if the user has installed libiconv and not disabled its use
+  dnl via --without-libiconv-prefix, he wants to use it. The first
+  dnl AC_TRY_LINK will then fail, the second AC_TRY_LINK will succeed.
+  am_save_CPPFLAGS="$CPPFLAGS"
+  AC_LIB_APPENDTOVAR([CPPFLAGS], [$INCICONV])
+
+  AC_CACHE_CHECK([for iconv], [am_cv_func_iconv], [
+    am_cv_func_iconv="no, consider installing GNU libiconv"
+    am_cv_lib_iconv=no
+    AC_TRY_LINK([#include <stdlib.h>
+#include <iconv.h>],
+      [iconv_t cd = iconv_open("","");
+       iconv(cd,NULL,NULL,NULL,NULL);
+       iconv_close(cd);],
+      [am_cv_func_iconv=yes])
+    if test "$am_cv_func_iconv" != yes; then
+      am_save_LIBS="$LIBS"
+      LIBS="$LIBS $LIBICONV"
+      AC_TRY_LINK([#include <stdlib.h>
+#include <iconv.h>],
+        [iconv_t cd = iconv_open("","");
+         iconv(cd,NULL,NULL,NULL,NULL);
+         iconv_close(cd);],
+        [am_cv_lib_iconv=yes]
+        [am_cv_func_iconv=yes])
+      LIBS="$am_save_LIBS"
+    fi
+  ])
+  if test "$am_cv_func_iconv" = yes; then
+    AC_CACHE_CHECK([for working iconv], [am_cv_func_iconv_works], [
+      dnl This tests against bugs in AIX 5.1, HP-UX 11.11, Solaris 10.
+      am_save_LIBS="$LIBS"
+      if test $am_cv_lib_iconv = yes; then
+        LIBS="$LIBS $LIBICONV"
+      fi
+      AC_TRY_RUN([
+#include <iconv.h>
+#include <string.h>
+int main ()
+{
+  /* Test against AIX 5.1 bug: Failures are not distinguishable from successful
+     returns.  */
+  {
+    iconv_t cd_utf8_to_88591 = iconv_open ("ISO8859-1", "UTF-8");
+    if (cd_utf8_to_88591 != (iconv_t)(-1))
+      {
+        static const char input[] = "\342\202\254"; /* EURO SIGN */
+        char buf[10];
+        const char *inptr = input;
+        size_t inbytesleft = strlen (input);
+        char *outptr = buf;
+        size_t outbytesleft = sizeof (buf);
+        size_t res = iconv (cd_utf8_to_88591,
+                            (char **) &inptr, &inbytesleft,
+                            &outptr, &outbytesleft);
+        if (res == 0)
+          return 1;
+      }
+  }
+  /* Test against Solaris 10 bug: Failures are not distinguishable from
+     successful returns.  */
+  {
+    iconv_t cd_ascii_to_88591 = iconv_open ("ISO8859-1", "646");
+    if (cd_ascii_to_88591 != (iconv_t)(-1))
+      {
+        static const char input[] = "\263";
+        char buf[10];
+        const char *inptr = input;
+        size_t inbytesleft = strlen (input);
+        char *outptr = buf;
+        size_t outbytesleft = sizeof (buf);
+        size_t res = iconv (cd_ascii_to_88591,
+                            (char **) &inptr, &inbytesleft,
+                            &outptr, &outbytesleft);
+        if (res == 0)
+          return 1;
+      }
+  }
+#if 0 /* This bug could be worked around by the caller.  */
+  /* Test against HP-UX 11.11 bug: Positive return value instead of 0.  */
+  {
+    iconv_t cd_88591_to_utf8 = iconv_open ("utf8", "iso88591");
+    if (cd_88591_to_utf8 != (iconv_t)(-1))
+      {
+        static const char input[] = "\304rger mit b\366sen B\374bchen ohne 
Augenma\337";
+        char buf[50];
+        const char *inptr = input;
+        size_t inbytesleft = strlen (input);
+        char *outptr = buf;
+        size_t outbytesleft = sizeof (buf);
+        size_t res = iconv (cd_88591_to_utf8,
+                            (char **) &inptr, &inbytesleft,
+                            &outptr, &outbytesleft);
+        if ((int)res > 0)
+          return 1;
+      }
+  }
+#endif
+  /* Test against HP-UX 11.11 bug: No converter from EUC-JP to UTF-8 is
+     provided.  */
+  if (/* Try standardized names.  */
+      iconv_open ("UTF-8", "EUC-JP") == (iconv_t)(-1)
+      /* Try IRIX, OSF/1 names.  */
+      && iconv_open ("UTF-8", "eucJP") == (iconv_t)(-1)
+      /* Try AIX names.  */
+      && iconv_open ("UTF-8", "IBM-eucJP") == (iconv_t)(-1)
+      /* Try HP-UX names.  */
+      && iconv_open ("utf8", "eucJP") == (iconv_t)(-1))
+    return 1;
+  return 0;
+}], [am_cv_func_iconv_works=yes], [am_cv_func_iconv_works=no],
+        [case "$host_os" in
+           aix* | hpux*) am_cv_func_iconv_works="guessing no" ;;
+           *)            am_cv_func_iconv_works="guessing yes" ;;
+         esac])
+      LIBS="$am_save_LIBS"
+    ])
+    case "$am_cv_func_iconv_works" in
+      *no) am_func_iconv=no am_cv_lib_iconv=no ;;
+      *)   am_func_iconv=yes ;;
+    esac
+  else
+    am_func_iconv=no am_cv_lib_iconv=no
+  fi
+  if test "$am_func_iconv" = yes; then
+    AC_DEFINE([HAVE_ICONV], [1],
+      [Define if you have the iconv() function and it works.])
+  fi
+  if test "$am_cv_lib_iconv" = yes; then
+    AC_MSG_CHECKING([how to link with libiconv])
+    AC_MSG_RESULT([$LIBICONV])
+  else
+    dnl If $LIBICONV didn't lead to a usable library, we don't need $INCICONV
+    dnl either.
+    CPPFLAGS="$am_save_CPPFLAGS"
+    LIBICONV=
+    LTLIBICONV=
+  fi
+  AC_SUBST([LIBICONV])
+  AC_SUBST([LTLIBICONV])
+])
+
+dnl Define AM_ICONV using AC_DEFUN_ONCE for Autoconf >= 2.64, in order to
+dnl avoid warnings like
+dnl "warning: AC_REQUIRE: `AM_ICONV' was expanded before it was required".
+dnl This is tricky because of the way 'aclocal' is implemented:
+dnl - It requires defining an auxiliary macro whose name ends in AC_DEFUN.
+dnl   Otherwise aclocal's initial scan pass would miss the macro definition.
+dnl - It requires a line break inside the AC_DEFUN_ONCE and AC_DEFUN 
expansions.
+dnl   Otherwise aclocal would emit many "Use of uninitialized value $1"
+dnl   warnings.
+m4_define([gl_iconv_AC_DEFUN],
+  m4_version_prereq([2.64],
+    [[AC_DEFUN_ONCE(
+        [$1], [$2])]],
+    [[AC_DEFUN(
+        [$1], [$2])]]))
+gl_iconv_AC_DEFUN([AM_ICONV],
+[
+  AM_ICONV_LINK
+  if test "$am_cv_func_iconv" = yes; then
+    AC_MSG_CHECKING([for iconv declaration])
+    AC_CACHE_VAL([am_cv_proto_iconv], [
+      AC_TRY_COMPILE([
+#include <stdlib.h>
+#include <iconv.h>
+extern
+#ifdef __cplusplus
+"C"
+#endif
+#if defined(__STDC__) || defined(__cplusplus)
+size_t iconv (iconv_t cd, char * *inbuf, size_t *inbytesleft, char * *outbuf, 
size_t *outbytesleft);
+#else
+size_t iconv();
+#endif
+], [], [am_cv_proto_iconv_arg1=""], [am_cv_proto_iconv_arg1="const"])
+      am_cv_proto_iconv="extern size_t iconv (iconv_t cd, 
$am_cv_proto_iconv_arg1 char * *inbuf, size_t *inbytesleft, char * *outbuf, 
size_t *outbytesleft);"])
+    am_cv_proto_iconv=`echo "[$]am_cv_proto_iconv" | tr -s ' ' | sed -e 's/( 
/(/'`
+    AC_MSG_RESULT([
+         $am_cv_proto_iconv])
+    AC_DEFINE_UNQUOTED([ICONV_CONST], [$am_cv_proto_iconv_arg1],
+      [Define as const if the declaration of iconv() needs const.])
+  fi
+])
diff --git a/m4/lib-ld.m4 b/m4/lib-ld.m4
new file mode 100644
index 0000000..ebb3052
--- /dev/null
+++ b/m4/lib-ld.m4
@@ -0,0 +1,110 @@
+# lib-ld.m4 serial 4 (gettext-0.18)
+dnl Copyright (C) 1996-2003, 2009-2010 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl Subroutines of libtool.m4,
+dnl with replacements s/AC_/AC_LIB/ and s/lt_cv/acl_cv/ to avoid collision
+dnl with libtool.m4.
+
+dnl From libtool-1.4. Sets the variable with_gnu_ld to yes or no.
+AC_DEFUN([AC_LIB_PROG_LD_GNU],
+[AC_CACHE_CHECK([if the linker ($LD) is GNU ld], [acl_cv_prog_gnu_ld],
+[# I'd rather use --version here, but apparently some GNU ld's only accept -v.
+case `$LD -v 2>&1 </dev/null` in
+*GNU* | *'with BFD'*)
+  acl_cv_prog_gnu_ld=yes ;;
+*)
+  acl_cv_prog_gnu_ld=no ;;
+esac])
+with_gnu_ld=$acl_cv_prog_gnu_ld
+])
+
+dnl From libtool-1.4. Sets the variable LD.
+AC_DEFUN([AC_LIB_PROG_LD],
+[AC_ARG_WITH([gnu-ld],
+[  --with-gnu-ld           assume the C compiler uses GNU ld [default=no]],
+test "$withval" = no || with_gnu_ld=yes, with_gnu_ld=no)
+AC_REQUIRE([AC_PROG_CC])dnl
+AC_REQUIRE([AC_CANONICAL_HOST])dnl
+# Prepare PATH_SEPARATOR.
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+  echo "#! /bin/sh" >conf$$.sh
+  echo  "exit 0"   >>conf$$.sh
+  chmod +x conf$$.sh
+  if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+    PATH_SEPARATOR=';'
+  else
+    PATH_SEPARATOR=:
+  fi
+  rm -f conf$$.sh
+fi
+ac_prog=ld
+if test "$GCC" = yes; then
+  # Check if gcc -print-prog-name=ld gives a path.
+  AC_MSG_CHECKING([for ld used by GCC])
+  case $host in
+  *-*-mingw*)
+    # gcc leaves a trailing carriage return which upsets mingw
+    ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;;
+  *)
+    ac_prog=`($CC -print-prog-name=ld) 2>&5` ;;
+  esac
+  case $ac_prog in
+    # Accept absolute paths.
+    [[\\/]* | [A-Za-z]:[\\/]*)]
+      [re_direlt='/[^/][^/]*/\.\./']
+      # Canonicalize the path of ld
+      ac_prog=`echo $ac_prog| sed 's%\\\\%/%g'`
+      while echo $ac_prog | grep "$re_direlt" > /dev/null 2>&1; do
+        ac_prog=`echo $ac_prog| sed "s%$re_direlt%/%"`
+      done
+      test -z "$LD" && LD="$ac_prog"
+      ;;
+  "")
+    # If it fails, then pretend we aren't using GCC.
+    ac_prog=ld
+    ;;
+  *)
+    # If it is relative, then search for the first ld in PATH.
+    with_gnu_ld=unknown
+    ;;
+  esac
+elif test "$with_gnu_ld" = yes; then
+  AC_MSG_CHECKING([for GNU ld])
+else
+  AC_MSG_CHECKING([for non-GNU ld])
+fi
+AC_CACHE_VAL([acl_cv_path_LD],
+[if test -z "$LD"; then
+  IFS="${IFS=  }"; ac_save_ifs="$IFS"; IFS="${IFS}${PATH_SEPARATOR-:}"
+  for ac_dir in $PATH; do
+    test -z "$ac_dir" && ac_dir=.
+    if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then
+      acl_cv_path_LD="$ac_dir/$ac_prog"
+      # Check to see if the program is GNU ld.  I'd rather use --version,
+      # but apparently some GNU ld's only accept -v.
+      # Break only if it was the GNU/non-GNU ld that we prefer.
+      case `"$acl_cv_path_LD" -v 2>&1 < /dev/null` in
+      *GNU* | *'with BFD'*)
+        test "$with_gnu_ld" != no && break ;;
+      *)
+        test "$with_gnu_ld" != yes && break ;;
+      esac
+    fi
+  done
+  IFS="$ac_save_ifs"
+else
+  acl_cv_path_LD="$LD" # Let the user override the test with a path.
+fi])
+LD="$acl_cv_path_LD"
+if test -n "$LD"; then
+  AC_MSG_RESULT([$LD])
+else
+  AC_MSG_RESULT([no])
+fi
+test -z "$LD" && AC_MSG_ERROR([no acceptable ld found in \$PATH])
+AC_LIB_PROG_LD_GNU
+])
diff --git a/m4/lib-link.m4 b/m4/lib-link.m4
new file mode 100644
index 0000000..c73bd8e
--- /dev/null
+++ b/m4/lib-link.m4
@@ -0,0 +1,774 @@
+# lib-link.m4 serial 21 (gettext-0.18)
+dnl Copyright (C) 2001-2010 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl From Bruno Haible.
+
+AC_PREREQ([2.54])
+
+dnl AC_LIB_LINKFLAGS(name [, dependencies]) searches for libname and
+dnl the libraries corresponding to explicit and implicit dependencies.
+dnl Sets and AC_SUBSTs the LIB${NAME} and LTLIB${NAME} variables and
+dnl augments the CPPFLAGS variable.
+dnl Sets and AC_SUBSTs the LIB${NAME}_PREFIX variable to nonempty if libname
+dnl was found in ${LIB${NAME}_PREFIX}/$acl_libdirstem.
+AC_DEFUN([AC_LIB_LINKFLAGS],
+[
+  AC_REQUIRE([AC_LIB_PREPARE_PREFIX])
+  AC_REQUIRE([AC_LIB_RPATH])
+  pushdef([Name],[translit([$1],[./-], [___])])
+  pushdef([NAME],[translit([$1],[abcdefghijklmnopqrstuvwxyz./-],
+                                [ABCDEFGHIJKLMNOPQRSTUVWXYZ___])])
+  AC_CACHE_CHECK([how to link with lib[]$1], [ac_cv_lib[]Name[]_libs], [
+    AC_LIB_LINKFLAGS_BODY([$1], [$2])
+    ac_cv_lib[]Name[]_libs="$LIB[]NAME"
+    ac_cv_lib[]Name[]_ltlibs="$LTLIB[]NAME"
+    ac_cv_lib[]Name[]_cppflags="$INC[]NAME"
+    ac_cv_lib[]Name[]_prefix="$LIB[]NAME[]_PREFIX"
+  ])
+  LIB[]NAME="$ac_cv_lib[]Name[]_libs"
+  LTLIB[]NAME="$ac_cv_lib[]Name[]_ltlibs"
+  INC[]NAME="$ac_cv_lib[]Name[]_cppflags"
+  LIB[]NAME[]_PREFIX="$ac_cv_lib[]Name[]_prefix"
+  AC_LIB_APPENDTOVAR([CPPFLAGS], [$INC]NAME)
+  AC_SUBST([LIB]NAME)
+  AC_SUBST([LTLIB]NAME)
+  AC_SUBST([LIB]NAME[_PREFIX])
+  dnl Also set HAVE_LIB[]NAME so that AC_LIB_HAVE_LINKFLAGS can reuse the
+  dnl results of this search when this library appears as a dependency.
+  HAVE_LIB[]NAME=yes
+  popdef([NAME])
+  popdef([Name])
+])
+
+dnl AC_LIB_HAVE_LINKFLAGS(name, dependencies, includes, testcode, 
[missing-message])
+dnl searches for libname and the libraries corresponding to explicit and
+dnl implicit dependencies, together with the specified include files and
+dnl the ability to compile and link the specified testcode. The missing-message
+dnl defaults to 'no' and may contain additional hints for the user.
+dnl If found, it sets and AC_SUBSTs HAVE_LIB${NAME}=yes and the LIB${NAME}
+dnl and LTLIB${NAME} variables and augments the CPPFLAGS variable, and
+dnl #defines HAVE_LIB${NAME} to 1. Otherwise, it sets and AC_SUBSTs
+dnl HAVE_LIB${NAME}=no and LIB${NAME} and LTLIB${NAME} to empty.
+dnl Sets and AC_SUBSTs the LIB${NAME}_PREFIX variable to nonempty if libname
+dnl was found in ${LIB${NAME}_PREFIX}/$acl_libdirstem.
+AC_DEFUN([AC_LIB_HAVE_LINKFLAGS],
+[
+  AC_REQUIRE([AC_LIB_PREPARE_PREFIX])
+  AC_REQUIRE([AC_LIB_RPATH])
+  pushdef([Name],[translit([$1],[./-], [___])])
+  pushdef([NAME],[translit([$1],[abcdefghijklmnopqrstuvwxyz./-],
+                                [ABCDEFGHIJKLMNOPQRSTUVWXYZ___])])
+
+  dnl Search for lib[]Name and define LIB[]NAME, LTLIB[]NAME and INC[]NAME
+  dnl accordingly.
+  AC_LIB_LINKFLAGS_BODY([$1], [$2])
+
+  dnl Add $INC[]NAME to CPPFLAGS before performing the following checks,
+  dnl because if the user has installed lib[]Name and not disabled its use
+  dnl via --without-lib[]Name-prefix, he wants to use it.
+  ac_save_CPPFLAGS="$CPPFLAGS"
+  AC_LIB_APPENDTOVAR([CPPFLAGS], [$INC]NAME)
+
+  AC_CACHE_CHECK([for lib[]$1], [ac_cv_lib[]Name], [
+    ac_save_LIBS="$LIBS"
+    dnl If $LIB[]NAME contains some -l options, add it to the end of LIBS,
+    dnl because these -l options might require -L options that are present in
+    dnl LIBS. -l options benefit only from the -L options listed before it.
+    dnl Otherwise, add it to the front of LIBS, because it may be a static
+    dnl library that depends on another static library that is present in LIBS.
+    dnl Static libraries benefit only from the static libraries listed after
+    dnl it.
+    case " $LIB[]NAME" in
+      *" -l"*) LIBS="$LIBS $LIB[]NAME" ;;
+      *)       LIBS="$LIB[]NAME $LIBS" ;;
+    esac
+    AC_TRY_LINK([$3], [$4],
+      [ac_cv_lib[]Name=yes],
+      [ac_cv_lib[]Name='m4_if([$5], [], [no], [[$5]])'])
+    LIBS="$ac_save_LIBS"
+  ])
+  if test "$ac_cv_lib[]Name" = yes; then
+    HAVE_LIB[]NAME=yes
+    AC_DEFINE([HAVE_LIB]NAME, 1, [Define if you have the lib][$1 library.])
+    AC_MSG_CHECKING([how to link with lib[]$1])
+    AC_MSG_RESULT([$LIB[]NAME])
+  else
+    HAVE_LIB[]NAME=no
+    dnl If $LIB[]NAME didn't lead to a usable library, we don't need
+    dnl $INC[]NAME either.
+    CPPFLAGS="$ac_save_CPPFLAGS"
+    LIB[]NAME=
+    LTLIB[]NAME=
+    LIB[]NAME[]_PREFIX=
+  fi
+  AC_SUBST([HAVE_LIB]NAME)
+  AC_SUBST([LIB]NAME)
+  AC_SUBST([LTLIB]NAME)
+  AC_SUBST([LIB]NAME[_PREFIX])
+  popdef([NAME])
+  popdef([Name])
+])
+
+dnl Determine the platform dependent parameters needed to use rpath:
+dnl   acl_libext,
+dnl   acl_shlibext,
+dnl   acl_hardcode_libdir_flag_spec,
+dnl   acl_hardcode_libdir_separator,
+dnl   acl_hardcode_direct,
+dnl   acl_hardcode_minus_L.
+AC_DEFUN([AC_LIB_RPATH],
+[
+  dnl Tell automake >= 1.10 to complain if config.rpath is missing.
+  m4_ifdef([AC_REQUIRE_AUX_FILE], [AC_REQUIRE_AUX_FILE([config.rpath])])
+  AC_REQUIRE([AC_PROG_CC])                dnl we use $CC, $GCC, $LDFLAGS
+  AC_REQUIRE([AC_LIB_PROG_LD])            dnl we use $LD, $with_gnu_ld
+  AC_REQUIRE([AC_CANONICAL_HOST])         dnl we use $host
+  AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT]) dnl we use $ac_aux_dir
+  AC_CACHE_CHECK([for shared library run path origin], [acl_cv_rpath], [
+    CC="$CC" GCC="$GCC" LDFLAGS="$LDFLAGS" LD="$LD" with_gnu_ld="$with_gnu_ld" 
\
+    ${CONFIG_SHELL-/bin/sh} "$ac_aux_dir/config.rpath" "$host" > conftest.sh
+    . ./conftest.sh
+    rm -f ./conftest.sh
+    acl_cv_rpath=done
+  ])
+  wl="$acl_cv_wl"
+  acl_libext="$acl_cv_libext"
+  acl_shlibext="$acl_cv_shlibext"
+  acl_libname_spec="$acl_cv_libname_spec"
+  acl_library_names_spec="$acl_cv_library_names_spec"
+  acl_hardcode_libdir_flag_spec="$acl_cv_hardcode_libdir_flag_spec"
+  acl_hardcode_libdir_separator="$acl_cv_hardcode_libdir_separator"
+  acl_hardcode_direct="$acl_cv_hardcode_direct"
+  acl_hardcode_minus_L="$acl_cv_hardcode_minus_L"
+  dnl Determine whether the user wants rpath handling at all.
+  AC_ARG_ENABLE([rpath],
+    [  --disable-rpath         do not hardcode runtime library paths],
+    :, enable_rpath=yes)
+])
+
+dnl AC_LIB_FROMPACKAGE(name, package)
+dnl declares that libname comes from the given package. The configure file
+dnl will then not have a --with-libname-prefix option but a
+dnl --with-package-prefix option. Several libraries can come from the same
+dnl package. This declaration must occur before an AC_LIB_LINKFLAGS or similar
+dnl macro call that searches for libname.
+AC_DEFUN([AC_LIB_FROMPACKAGE],
+[
+  pushdef([NAME],[translit([$1],[abcdefghijklmnopqrstuvwxyz./-],
+                                [ABCDEFGHIJKLMNOPQRSTUVWXYZ___])])
+  define([acl_frompackage_]NAME, [$2])
+  popdef([NAME])
+  pushdef([PACK],[$2])
+  pushdef([PACKUP],[translit(PACK,[abcdefghijklmnopqrstuvwxyz./-],
+                                  [ABCDEFGHIJKLMNOPQRSTUVWXYZ___])])
+  define([acl_libsinpackage_]PACKUP,
+    m4_ifdef([acl_libsinpackage_]PACKUP, [acl_libsinpackage_]PACKUP[[, 
]],)[lib$1])
+  popdef([PACKUP])
+  popdef([PACK])
+])
+
+dnl AC_LIB_LINKFLAGS_BODY(name [, dependencies]) searches for libname and
+dnl the libraries corresponding to explicit and implicit dependencies.
+dnl Sets the LIB${NAME}, LTLIB${NAME} and INC${NAME} variables.
+dnl Also, sets the LIB${NAME}_PREFIX variable to nonempty if libname was found
+dnl in ${LIB${NAME}_PREFIX}/$acl_libdirstem.
+AC_DEFUN([AC_LIB_LINKFLAGS_BODY],
+[
+  AC_REQUIRE([AC_LIB_PREPARE_MULTILIB])
+  pushdef([NAME],[translit([$1],[abcdefghijklmnopqrstuvwxyz./-],
+                                [ABCDEFGHIJKLMNOPQRSTUVWXYZ___])])
+  pushdef([PACK],[m4_ifdef([acl_frompackage_]NAME, [acl_frompackage_]NAME, 
lib[$1])])
+  pushdef([PACKUP],[translit(PACK,[abcdefghijklmnopqrstuvwxyz./-],
+                                  [ABCDEFGHIJKLMNOPQRSTUVWXYZ___])])
+  pushdef([PACKLIBS],[m4_ifdef([acl_frompackage_]NAME, 
[acl_libsinpackage_]PACKUP, lib[$1])])
+  dnl Autoconf >= 2.61 supports dots in --with options.
+  
pushdef([P_A_C_K],[m4_if(m4_version_compare(m4_defn([m4_PACKAGE_VERSION]),[2.61]),[-1],[translit(PACK,[.],[_])],PACK)])
+  dnl By default, look in $includedir and $libdir.
+  use_additional=yes
+  AC_LIB_WITH_FINAL_PREFIX([
+    eval additional_includedir=\"$includedir\"
+    eval additional_libdir=\"$libdir\"
+  ])
+  AC_ARG_WITH(P_A_C_K[-prefix],
+[[  --with-]]P_A_C_K[[-prefix[=DIR]  search for ]PACKLIBS[ in DIR/include and 
DIR/lib
+  --without-]]P_A_C_K[[-prefix     don't search for ]PACKLIBS[ in includedir 
and libdir]],
+[
+    if test "X$withval" = "Xno"; then
+      use_additional=no
+    else
+      if test "X$withval" = "X"; then
+        AC_LIB_WITH_FINAL_PREFIX([
+          eval additional_includedir=\"$includedir\"
+          eval additional_libdir=\"$libdir\"
+        ])
+      else
+        additional_includedir="$withval/include"
+        additional_libdir="$withval/$acl_libdirstem"
+        if test "$acl_libdirstem2" != "$acl_libdirstem" \
+           && ! test -d "$withval/$acl_libdirstem"; then
+          additional_libdir="$withval/$acl_libdirstem2"
+        fi
+      fi
+    fi
+])
+  dnl Search the library and its dependencies in $additional_libdir and
+  dnl $LDFLAGS. Using breadth-first-seach.
+  LIB[]NAME=
+  LTLIB[]NAME=
+  INC[]NAME=
+  LIB[]NAME[]_PREFIX=
+  dnl HAVE_LIB${NAME} is an indicator that LIB${NAME}, LTLIB${NAME} have been
+  dnl computed. So it has to be reset here.
+  HAVE_LIB[]NAME=
+  rpathdirs=
+  ltrpathdirs=
+  names_already_handled=
+  names_next_round='$1 $2'
+  while test -n "$names_next_round"; do
+    names_this_round="$names_next_round"
+    names_next_round=
+    for name in $names_this_round; do
+      already_handled=
+      for n in $names_already_handled; do
+        if test "$n" = "$name"; then
+          already_handled=yes
+          break
+        fi
+      done
+      if test -z "$already_handled"; then
+        names_already_handled="$names_already_handled $name"
+        dnl See if it was already located by an earlier AC_LIB_LINKFLAGS
+        dnl or AC_LIB_HAVE_LINKFLAGS call.
+        uppername=`echo "$name" | sed -e 
'y|abcdefghijklmnopqrstuvwxyz./-|ABCDEFGHIJKLMNOPQRSTUVWXYZ___|'`
+        eval value=\"\$HAVE_LIB$uppername\"
+        if test -n "$value"; then
+          if test "$value" = yes; then
+            eval value=\"\$LIB$uppername\"
+            test -z "$value" || LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$value"
+            eval value=\"\$LTLIB$uppername\"
+            test -z "$value" || LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ 
}$value"
+          else
+            dnl An earlier call to AC_LIB_HAVE_LINKFLAGS has determined
+            dnl that this library doesn't exist. So just drop it.
+            :
+          fi
+        else
+          dnl Search the library lib$name in $additional_libdir and $LDFLAGS
+          dnl and the already constructed $LIBNAME/$LTLIBNAME.
+          found_dir=
+          found_la=
+          found_so=
+          found_a=
+          eval libname=\"$acl_libname_spec\"    # typically: libname=lib$name
+          if test -n "$acl_shlibext"; then
+            shrext=".$acl_shlibext"             # typically: shrext=.so
+          else
+            shrext=
+          fi
+          if test $use_additional = yes; then
+            dir="$additional_libdir"
+            dnl The same code as in the loop below:
+            dnl First look for a shared library.
+            if test -n "$acl_shlibext"; then
+              if test -f "$dir/$libname$shrext"; then
+                found_dir="$dir"
+                found_so="$dir/$libname$shrext"
+              else
+                if test "$acl_library_names_spec" = 
'$libname$shrext$versuffix'; then
+                  ver=`(cd "$dir" && \
+                        for f in "$libname$shrext".*; do echo "$f"; done \
+                        | sed -e "s,^$libname$shrext\\\\.,," \
+                        | sort -t '.' -n -r -k1,1 -k2,2 -k3,3 -k4,4 -k5,5 \
+                        | sed 1q ) 2>/dev/null`
+                  if test -n "$ver" && test -f "$dir/$libname$shrext.$ver"; 
then
+                    found_dir="$dir"
+                    found_so="$dir/$libname$shrext.$ver"
+                  fi
+                else
+                  eval library_names=\"$acl_library_names_spec\"
+                  for f in $library_names; do
+                    if test -f "$dir/$f"; then
+                      found_dir="$dir"
+                      found_so="$dir/$f"
+                      break
+                    fi
+                  done
+                fi
+              fi
+            fi
+            dnl Then look for a static library.
+            if test "X$found_dir" = "X"; then
+              if test -f "$dir/$libname.$acl_libext"; then
+                found_dir="$dir"
+                found_a="$dir/$libname.$acl_libext"
+              fi
+            fi
+            if test "X$found_dir" != "X"; then
+              if test -f "$dir/$libname.la"; then
+                found_la="$dir/$libname.la"
+              fi
+            fi
+          fi
+          if test "X$found_dir" = "X"; then
+            for x in $LDFLAGS $LTLIB[]NAME; do
+              AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+              case "$x" in
+                -L*)
+                  dir=`echo "X$x" | sed -e 's/^X-L//'`
+                  dnl First look for a shared library.
+                  if test -n "$acl_shlibext"; then
+                    if test -f "$dir/$libname$shrext"; then
+                      found_dir="$dir"
+                      found_so="$dir/$libname$shrext"
+                    else
+                      if test "$acl_library_names_spec" = 
'$libname$shrext$versuffix'; then
+                        ver=`(cd "$dir" && \
+                              for f in "$libname$shrext".*; do echo "$f"; done 
\
+                              | sed -e "s,^$libname$shrext\\\\.,," \
+                              | sort -t '.' -n -r -k1,1 -k2,2 -k3,3 -k4,4 
-k5,5 \
+                              | sed 1q ) 2>/dev/null`
+                        if test -n "$ver" && test -f 
"$dir/$libname$shrext.$ver"; then
+                          found_dir="$dir"
+                          found_so="$dir/$libname$shrext.$ver"
+                        fi
+                      else
+                        eval library_names=\"$acl_library_names_spec\"
+                        for f in $library_names; do
+                          if test -f "$dir/$f"; then
+                            found_dir="$dir"
+                            found_so="$dir/$f"
+                            break
+                          fi
+                        done
+                      fi
+                    fi
+                  fi
+                  dnl Then look for a static library.
+                  if test "X$found_dir" = "X"; then
+                    if test -f "$dir/$libname.$acl_libext"; then
+                      found_dir="$dir"
+                      found_a="$dir/$libname.$acl_libext"
+                    fi
+                  fi
+                  if test "X$found_dir" != "X"; then
+                    if test -f "$dir/$libname.la"; then
+                      found_la="$dir/$libname.la"
+                    fi
+                  fi
+                  ;;
+              esac
+              if test "X$found_dir" != "X"; then
+                break
+              fi
+            done
+          fi
+          if test "X$found_dir" != "X"; then
+            dnl Found the library.
+            LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-L$found_dir -l$name"
+            if test "X$found_so" != "X"; then
+              dnl Linking with a shared library. We attempt to hardcode its
+              dnl directory into the executable's runpath, unless it's the
+              dnl standard /usr/lib.
+              if test "$enable_rpath" = no \
+                 || test "X$found_dir" = "X/usr/$acl_libdirstem" \
+                 || test "X$found_dir" = "X/usr/$acl_libdirstem2"; then
+                dnl No hardcoding is needed.
+                LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so"
+              else
+                dnl Use an explicit option to hardcode DIR into the resulting
+                dnl binary.
+                dnl Potentially add DIR to ltrpathdirs.
+                dnl The ltrpathdirs will be appended to $LTLIBNAME at the end.
+                haveit=
+                for x in $ltrpathdirs; do
+                  if test "X$x" = "X$found_dir"; then
+                    haveit=yes
+                    break
+                  fi
+                done
+                if test -z "$haveit"; then
+                  ltrpathdirs="$ltrpathdirs $found_dir"
+                fi
+                dnl The hardcoding into $LIBNAME is system dependent.
+                if test "$acl_hardcode_direct" = yes; then
+                  dnl Using DIR/libNAME.so during linking hardcodes DIR into 
the
+                  dnl resulting binary.
+                  LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so"
+                else
+                  if test -n "$acl_hardcode_libdir_flag_spec" && test 
"$acl_hardcode_minus_L" = no; then
+                    dnl Use an explicit option to hardcode DIR into the 
resulting
+                    dnl binary.
+                    LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so"
+                    dnl Potentially add DIR to rpathdirs.
+                    dnl The rpathdirs will be appended to $LIBNAME at the end.
+                    haveit=
+                    for x in $rpathdirs; do
+                      if test "X$x" = "X$found_dir"; then
+                        haveit=yes
+                        break
+                      fi
+                    done
+                    if test -z "$haveit"; then
+                      rpathdirs="$rpathdirs $found_dir"
+                    fi
+                  else
+                    dnl Rely on "-L$found_dir".
+                    dnl But don't add it if it's already contained in the 
LDFLAGS
+                    dnl or the already constructed $LIBNAME
+                    haveit=
+                    for x in $LDFLAGS $LIB[]NAME; do
+                      AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+                      if test "X$x" = "X-L$found_dir"; then
+                        haveit=yes
+                        break
+                      fi
+                    done
+                    if test -z "$haveit"; then
+                      LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-L$found_dir"
+                    fi
+                    if test "$acl_hardcode_minus_L" != no; then
+                      dnl FIXME: Not sure whether we should use
+                      dnl "-L$found_dir -l$name" or "-L$found_dir $found_so"
+                      dnl here.
+                      LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so"
+                    else
+                      dnl We cannot use $acl_hardcode_runpath_var and 
LD_RUN_PATH
+                      dnl here, because this doesn't fit in flags passed to the
+                      dnl compiler. So give up. No hardcoding. This affects 
only
+                      dnl very old systems.
+                      dnl FIXME: Not sure whether we should use
+                      dnl "-L$found_dir -l$name" or "-L$found_dir $found_so"
+                      dnl here.
+                      LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-l$name"
+                    fi
+                  fi
+                fi
+              fi
+            else
+              if test "X$found_a" != "X"; then
+                dnl Linking with a static library.
+                LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_a"
+              else
+                dnl We shouldn't come here, but anyway it's good to have a
+                dnl fallback.
+                LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-L$found_dir -l$name"
+              fi
+            fi
+            dnl Assume the include files are nearby.
+            additional_includedir=
+            case "$found_dir" in
+              */$acl_libdirstem | */$acl_libdirstem/)
+                basedir=`echo "X$found_dir" | sed -e 's,^X,,' -e 
"s,/$acl_libdirstem/"'*$,,'`
+                if test "$name" = '$1'; then
+                  LIB[]NAME[]_PREFIX="$basedir"
+                fi
+                additional_includedir="$basedir/include"
+                ;;
+              */$acl_libdirstem2 | */$acl_libdirstem2/)
+                basedir=`echo "X$found_dir" | sed -e 's,^X,,' -e 
"s,/$acl_libdirstem2/"'*$,,'`
+                if test "$name" = '$1'; then
+                  LIB[]NAME[]_PREFIX="$basedir"
+                fi
+                additional_includedir="$basedir/include"
+                ;;
+            esac
+            if test "X$additional_includedir" != "X"; then
+              dnl Potentially add $additional_includedir to $INCNAME.
+              dnl But don't add it
+              dnl   1. if it's the standard /usr/include,
+              dnl   2. if it's /usr/local/include and we are using GCC on 
Linux,
+              dnl   3. if it's already present in $CPPFLAGS or the already
+              dnl      constructed $INCNAME,
+              dnl   4. if it doesn't exist as a directory.
+              if test "X$additional_includedir" != "X/usr/include"; then
+                haveit=
+                if test "X$additional_includedir" = "X/usr/local/include"; then
+                  if test -n "$GCC"; then
+                    case $host_os in
+                      linux* | gnu* | k*bsd*-gnu) haveit=yes;;
+                    esac
+                  fi
+                fi
+                if test -z "$haveit"; then
+                  for x in $CPPFLAGS $INC[]NAME; do
+                    AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+                    if test "X$x" = "X-I$additional_includedir"; then
+                      haveit=yes
+                      break
+                    fi
+                  done
+                  if test -z "$haveit"; then
+                    if test -d "$additional_includedir"; then
+                      dnl Really add $additional_includedir to $INCNAME.
+                      INC[]NAME="${INC[]NAME}${INC[]NAME:+ 
}-I$additional_includedir"
+                    fi
+                  fi
+                fi
+              fi
+            fi
+            dnl Look for dependencies.
+            if test -n "$found_la"; then
+              dnl Read the .la file. It defines the variables
+              dnl dlname, library_names, old_library, dependency_libs, current,
+              dnl age, revision, installed, dlopen, dlpreopen, libdir.
+              save_libdir="$libdir"
+              case "$found_la" in
+                */* | *\\*) . "$found_la" ;;
+                *) . "./$found_la" ;;
+              esac
+              libdir="$save_libdir"
+              dnl We use only dependency_libs.
+              for dep in $dependency_libs; do
+                case "$dep" in
+                  -L*)
+                    additional_libdir=`echo "X$dep" | sed -e 's/^X-L//'`
+                    dnl Potentially add $additional_libdir to $LIBNAME and 
$LTLIBNAME.
+                    dnl But don't add it
+                    dnl   1. if it's the standard /usr/lib,
+                    dnl   2. if it's /usr/local/lib and we are using GCC on 
Linux,
+                    dnl   3. if it's already present in $LDFLAGS or the already
+                    dnl      constructed $LIBNAME,
+                    dnl   4. if it doesn't exist as a directory.
+                    if test "X$additional_libdir" != "X/usr/$acl_libdirstem" \
+                       && test "X$additional_libdir" != 
"X/usr/$acl_libdirstem2"; then
+                      haveit=
+                      if test "X$additional_libdir" = 
"X/usr/local/$acl_libdirstem" \
+                         || test "X$additional_libdir" = 
"X/usr/local/$acl_libdirstem2"; then
+                        if test -n "$GCC"; then
+                          case $host_os in
+                            linux* | gnu* | k*bsd*-gnu) haveit=yes;;
+                          esac
+                        fi
+                      fi
+                      if test -z "$haveit"; then
+                        haveit=
+                        for x in $LDFLAGS $LIB[]NAME; do
+                          AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+                          if test "X$x" = "X-L$additional_libdir"; then
+                            haveit=yes
+                            break
+                          fi
+                        done
+                        if test -z "$haveit"; then
+                          if test -d "$additional_libdir"; then
+                            dnl Really add $additional_libdir to $LIBNAME.
+                            LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ 
}-L$additional_libdir"
+                          fi
+                        fi
+                        haveit=
+                        for x in $LDFLAGS $LTLIB[]NAME; do
+                          AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+                          if test "X$x" = "X-L$additional_libdir"; then
+                            haveit=yes
+                            break
+                          fi
+                        done
+                        if test -z "$haveit"; then
+                          if test -d "$additional_libdir"; then
+                            dnl Really add $additional_libdir to $LTLIBNAME.
+                            LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ 
}-L$additional_libdir"
+                          fi
+                        fi
+                      fi
+                    fi
+                    ;;
+                  -R*)
+                    dir=`echo "X$dep" | sed -e 's/^X-R//'`
+                    if test "$enable_rpath" != no; then
+                      dnl Potentially add DIR to rpathdirs.
+                      dnl The rpathdirs will be appended to $LIBNAME at the 
end.
+                      haveit=
+                      for x in $rpathdirs; do
+                        if test "X$x" = "X$dir"; then
+                          haveit=yes
+                          break
+                        fi
+                      done
+                      if test -z "$haveit"; then
+                        rpathdirs="$rpathdirs $dir"
+                      fi
+                      dnl Potentially add DIR to ltrpathdirs.
+                      dnl The ltrpathdirs will be appended to $LTLIBNAME at 
the end.
+                      haveit=
+                      for x in $ltrpathdirs; do
+                        if test "X$x" = "X$dir"; then
+                          haveit=yes
+                          break
+                        fi
+                      done
+                      if test -z "$haveit"; then
+                        ltrpathdirs="$ltrpathdirs $dir"
+                      fi
+                    fi
+                    ;;
+                  -l*)
+                    dnl Handle this in the next round.
+                    names_next_round="$names_next_round "`echo "X$dep" | sed 
-e 's/^X-l//'`
+                    ;;
+                  *.la)
+                    dnl Handle this in the next round. Throw away the .la's
+                    dnl directory; it is already contained in a preceding -L
+                    dnl option.
+                    names_next_round="$names_next_round "`echo "X$dep" | sed 
-e 's,^X.*/,,' -e 's,^lib,,' -e 's,\.la$,,'`
+                    ;;
+                  *)
+                    dnl Most likely an immediate library name.
+                    LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$dep"
+                    LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }$dep"
+                    ;;
+                esac
+              done
+            fi
+          else
+            dnl Didn't find the library; assume it is in the system directories
+            dnl known to the linker and runtime loader. (All the system
+            dnl directories known to the linker should also be known to the
+            dnl runtime loader, otherwise the system is severely 
misconfigured.)
+            LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-l$name"
+            LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-l$name"
+          fi
+        fi
+      fi
+    done
+  done
+  if test "X$rpathdirs" != "X"; then
+    if test -n "$acl_hardcode_libdir_separator"; then
+      dnl Weird platform: only the last -rpath option counts, the user must
+      dnl pass all path elements in one option. We can arrange that for a
+      dnl single library, but not when more than one $LIBNAMEs are used.
+      alldirs=
+      for found_dir in $rpathdirs; do
+        
alldirs="${alldirs}${alldirs:+$acl_hardcode_libdir_separator}$found_dir"
+      done
+      dnl Note: acl_hardcode_libdir_flag_spec uses $libdir and $wl.
+      acl_save_libdir="$libdir"
+      libdir="$alldirs"
+      eval flag=\"$acl_hardcode_libdir_flag_spec\"
+      libdir="$acl_save_libdir"
+      LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$flag"
+    else
+      dnl The -rpath options are cumulative.
+      for found_dir in $rpathdirs; do
+        acl_save_libdir="$libdir"
+        libdir="$found_dir"
+        eval flag=\"$acl_hardcode_libdir_flag_spec\"
+        libdir="$acl_save_libdir"
+        LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$flag"
+      done
+    fi
+  fi
+  if test "X$ltrpathdirs" != "X"; then
+    dnl When using libtool, the option that works for both libraries and
+    dnl executables is -R. The -R options are cumulative.
+    for found_dir in $ltrpathdirs; do
+      LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-R$found_dir"
+    done
+  fi
+  popdef([P_A_C_K])
+  popdef([PACKLIBS])
+  popdef([PACKUP])
+  popdef([PACK])
+  popdef([NAME])
+])
+
+dnl AC_LIB_APPENDTOVAR(VAR, CONTENTS) appends the elements of CONTENTS to VAR,
+dnl unless already present in VAR.
+dnl Works only for CPPFLAGS, not for LIB* variables because that sometimes
+dnl contains two or three consecutive elements that belong together.
+AC_DEFUN([AC_LIB_APPENDTOVAR],
+[
+  for element in [$2]; do
+    haveit=
+    for x in $[$1]; do
+      AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+      if test "X$x" = "X$element"; then
+        haveit=yes
+        break
+      fi
+    done
+    if test -z "$haveit"; then
+      [$1]="${[$1]}${[$1]:+ }$element"
+    fi
+  done
+])
+
+dnl For those cases where a variable contains several -L and -l options
+dnl referring to unknown libraries and directories, this macro determines the
+dnl necessary additional linker options for the runtime path.
+dnl AC_LIB_LINKFLAGS_FROM_LIBS([LDADDVAR], [LIBSVALUE], [USE-LIBTOOL])
+dnl sets LDADDVAR to linker options needed together with LIBSVALUE.
+dnl If USE-LIBTOOL evaluates to non-empty, linking with libtool is assumed,
+dnl otherwise linking without libtool is assumed.
+AC_DEFUN([AC_LIB_LINKFLAGS_FROM_LIBS],
+[
+  AC_REQUIRE([AC_LIB_RPATH])
+  AC_REQUIRE([AC_LIB_PREPARE_MULTILIB])
+  $1=
+  if test "$enable_rpath" != no; then
+    if test -n "$acl_hardcode_libdir_flag_spec" && test 
"$acl_hardcode_minus_L" = no; then
+      dnl Use an explicit option to hardcode directories into the resulting
+      dnl binary.
+      rpathdirs=
+      next=
+      for opt in $2; do
+        if test -n "$next"; then
+          dir="$next"
+          dnl No need to hardcode the standard /usr/lib.
+          if test "X$dir" != "X/usr/$acl_libdirstem" \
+             && test "X$dir" != "X/usr/$acl_libdirstem2"; then
+            rpathdirs="$rpathdirs $dir"
+          fi
+          next=
+        else
+          case $opt in
+            -L) next=yes ;;
+            -L*) dir=`echo "X$opt" | sed -e 's,^X-L,,'`
+                 dnl No need to hardcode the standard /usr/lib.
+                 if test "X$dir" != "X/usr/$acl_libdirstem" \
+                    && test "X$dir" != "X/usr/$acl_libdirstem2"; then
+                   rpathdirs="$rpathdirs $dir"
+                 fi
+                 next= ;;
+            *) next= ;;
+          esac
+        fi
+      done
+      if test "X$rpathdirs" != "X"; then
+        if test -n ""$3""; then
+          dnl libtool is used for linking. Use -R options.
+          for dir in $rpathdirs; do
+            $1="${$1}${$1:+ }-R$dir"
+          done
+        else
+          dnl The linker is used for linking directly.
+          if test -n "$acl_hardcode_libdir_separator"; then
+            dnl Weird platform: only the last -rpath option counts, the user
+            dnl must pass all path elements in one option.
+            alldirs=
+            for dir in $rpathdirs; do
+              
alldirs="${alldirs}${alldirs:+$acl_hardcode_libdir_separator}$dir"
+            done
+            acl_save_libdir="$libdir"
+            libdir="$alldirs"
+            eval flag=\"$acl_hardcode_libdir_flag_spec\"
+            libdir="$acl_save_libdir"
+            $1="$flag"
+          else
+            dnl The -rpath options are cumulative.
+            for dir in $rpathdirs; do
+              acl_save_libdir="$libdir"
+              libdir="$dir"
+              eval flag=\"$acl_hardcode_libdir_flag_spec\"
+              libdir="$acl_save_libdir"
+              $1="${$1}${$1:+ }$flag"
+            done
+          fi
+        fi
+      fi
+    fi
+  fi
+  AC_SUBST([$1])
+])
diff --git a/m4/lib-prefix.m4 b/m4/lib-prefix.m4
new file mode 100644
index 0000000..1601cea
--- /dev/null
+++ b/m4/lib-prefix.m4
@@ -0,0 +1,224 @@
+# lib-prefix.m4 serial 7 (gettext-0.18)
+dnl Copyright (C) 2001-2005, 2008-2010 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl From Bruno Haible.
+
+dnl AC_LIB_ARG_WITH is synonymous to AC_ARG_WITH in autoconf-2.13, and
+dnl similar to AC_ARG_WITH in autoconf 2.52...2.57 except that is doesn't
+dnl require excessive bracketing.
+ifdef([AC_HELP_STRING],
+[AC_DEFUN([AC_LIB_ARG_WITH], [AC_ARG_WITH([$1],[[$2]],[$3],[$4])])],
+[AC_DEFUN([AC_][LIB_ARG_WITH], [AC_ARG_WITH([$1],[$2],[$3],[$4])])])
+
+dnl AC_LIB_PREFIX adds to the CPPFLAGS and LDFLAGS the flags that are needed
+dnl to access previously installed libraries. The basic assumption is that
+dnl a user will want packages to use other packages he previously installed
+dnl with the same --prefix option.
+dnl This macro is not needed if only AC_LIB_LINKFLAGS is used to locate
+dnl libraries, but is otherwise very convenient.
+AC_DEFUN([AC_LIB_PREFIX],
+[
+  AC_BEFORE([$0], [AC_LIB_LINKFLAGS])
+  AC_REQUIRE([AC_PROG_CC])
+  AC_REQUIRE([AC_CANONICAL_HOST])
+  AC_REQUIRE([AC_LIB_PREPARE_MULTILIB])
+  AC_REQUIRE([AC_LIB_PREPARE_PREFIX])
+  dnl By default, look in $includedir and $libdir.
+  use_additional=yes
+  AC_LIB_WITH_FINAL_PREFIX([
+    eval additional_includedir=\"$includedir\"
+    eval additional_libdir=\"$libdir\"
+  ])
+  AC_LIB_ARG_WITH([lib-prefix],
+[  --with-lib-prefix[=DIR] search for libraries in DIR/include and DIR/lib
+  --without-lib-prefix    don't search for libraries in includedir and libdir],
+[
+    if test "X$withval" = "Xno"; then
+      use_additional=no
+    else
+      if test "X$withval" = "X"; then
+        AC_LIB_WITH_FINAL_PREFIX([
+          eval additional_includedir=\"$includedir\"
+          eval additional_libdir=\"$libdir\"
+        ])
+      else
+        additional_includedir="$withval/include"
+        additional_libdir="$withval/$acl_libdirstem"
+      fi
+    fi
+])
+  if test $use_additional = yes; then
+    dnl Potentially add $additional_includedir to $CPPFLAGS.
+    dnl But don't add it
+    dnl   1. if it's the standard /usr/include,
+    dnl   2. if it's already present in $CPPFLAGS,
+    dnl   3. if it's /usr/local/include and we are using GCC on Linux,
+    dnl   4. if it doesn't exist as a directory.
+    if test "X$additional_includedir" != "X/usr/include"; then
+      haveit=
+      for x in $CPPFLAGS; do
+        AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+        if test "X$x" = "X-I$additional_includedir"; then
+          haveit=yes
+          break
+        fi
+      done
+      if test -z "$haveit"; then
+        if test "X$additional_includedir" = "X/usr/local/include"; then
+          if test -n "$GCC"; then
+            case $host_os in
+              linux* | gnu* | k*bsd*-gnu) haveit=yes;;
+            esac
+          fi
+        fi
+        if test -z "$haveit"; then
+          if test -d "$additional_includedir"; then
+            dnl Really add $additional_includedir to $CPPFLAGS.
+            CPPFLAGS="${CPPFLAGS}${CPPFLAGS:+ }-I$additional_includedir"
+          fi
+        fi
+      fi
+    fi
+    dnl Potentially add $additional_libdir to $LDFLAGS.
+    dnl But don't add it
+    dnl   1. if it's the standard /usr/lib,
+    dnl   2. if it's already present in $LDFLAGS,
+    dnl   3. if it's /usr/local/lib and we are using GCC on Linux,
+    dnl   4. if it doesn't exist as a directory.
+    if test "X$additional_libdir" != "X/usr/$acl_libdirstem"; then
+      haveit=
+      for x in $LDFLAGS; do
+        AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+        if test "X$x" = "X-L$additional_libdir"; then
+          haveit=yes
+          break
+        fi
+      done
+      if test -z "$haveit"; then
+        if test "X$additional_libdir" = "X/usr/local/$acl_libdirstem"; then
+          if test -n "$GCC"; then
+            case $host_os in
+              linux*) haveit=yes;;
+            esac
+          fi
+        fi
+        if test -z "$haveit"; then
+          if test -d "$additional_libdir"; then
+            dnl Really add $additional_libdir to $LDFLAGS.
+            LDFLAGS="${LDFLAGS}${LDFLAGS:+ }-L$additional_libdir"
+          fi
+        fi
+      fi
+    fi
+  fi
+])
+
+dnl AC_LIB_PREPARE_PREFIX creates variables acl_final_prefix,
+dnl acl_final_exec_prefix, containing the values to which $prefix and
+dnl $exec_prefix will expand at the end of the configure script.
+AC_DEFUN([AC_LIB_PREPARE_PREFIX],
+[
+  dnl Unfortunately, prefix and exec_prefix get only finally determined
+  dnl at the end of configure.
+  if test "X$prefix" = "XNONE"; then
+    acl_final_prefix="$ac_default_prefix"
+  else
+    acl_final_prefix="$prefix"
+  fi
+  if test "X$exec_prefix" = "XNONE"; then
+    acl_final_exec_prefix='${prefix}'
+  else
+    acl_final_exec_prefix="$exec_prefix"
+  fi
+  acl_save_prefix="$prefix"
+  prefix="$acl_final_prefix"
+  eval acl_final_exec_prefix=\"$acl_final_exec_prefix\"
+  prefix="$acl_save_prefix"
+])
+
+dnl AC_LIB_WITH_FINAL_PREFIX([statement]) evaluates statement, with the
+dnl variables prefix and exec_prefix bound to the values they will have
+dnl at the end of the configure script.
+AC_DEFUN([AC_LIB_WITH_FINAL_PREFIX],
+[
+  acl_save_prefix="$prefix"
+  prefix="$acl_final_prefix"
+  acl_save_exec_prefix="$exec_prefix"
+  exec_prefix="$acl_final_exec_prefix"
+  $1
+  exec_prefix="$acl_save_exec_prefix"
+  prefix="$acl_save_prefix"
+])
+
+dnl AC_LIB_PREPARE_MULTILIB creates
+dnl - a variable acl_libdirstem, containing the basename of the libdir, either
+dnl   "lib" or "lib64" or "lib/64",
+dnl - a variable acl_libdirstem2, as a secondary possible value for
+dnl   acl_libdirstem, either the same as acl_libdirstem or "lib/sparcv9" or
+dnl   "lib/amd64".
+AC_DEFUN([AC_LIB_PREPARE_MULTILIB],
+[
+  dnl There is no formal standard regarding lib and lib64.
+  dnl On glibc systems, the current practice is that on a system supporting
+  dnl 32-bit and 64-bit instruction sets or ABIs, 64-bit libraries go under
+  dnl $prefix/lib64 and 32-bit libraries go under $prefix/lib. We determine
+  dnl the compiler's default mode by looking at the compiler's library search
+  dnl path. If at least one of its elements ends in /lib64 or points to a
+  dnl directory whose absolute pathname ends in /lib64, we assume a 64-bit ABI.
+  dnl Otherwise we use the default, namely "lib".
+  dnl On Solaris systems, the current practice is that on a system supporting
+  dnl 32-bit and 64-bit instruction sets or ABIs, 64-bit libraries go under
+  dnl $prefix/lib/64 (which is a symlink to either $prefix/lib/sparcv9 or
+  dnl $prefix/lib/amd64) and 32-bit libraries go under $prefix/lib.
+  AC_REQUIRE([AC_CANONICAL_HOST])
+  acl_libdirstem=lib
+  acl_libdirstem2=
+  case "$host_os" in
+    solaris*)
+      dnl See Solaris 10 Software Developer Collection > Solaris 64-bit 
Developer's Guide > The Development Environment
+      dnl <http://docs.sun.com/app/docs/doc/816-5138/dev-env?l=en&a=view>.
+      dnl "Portable Makefiles should refer to any library directories using 
the 64 symbolic link."
+      dnl But we want to recognize the sparcv9 or amd64 subdirectory also if 
the
+      dnl symlink is missing, so we set acl_libdirstem2 too.
+      AC_CACHE_CHECK([for 64-bit host], [gl_cv_solaris_64bit],
+        [AC_EGREP_CPP([sixtyfour bits], [
+#ifdef _LP64
+sixtyfour bits
+#endif
+           ], [gl_cv_solaris_64bit=yes], [gl_cv_solaris_64bit=no])
+        ])
+      if test $gl_cv_solaris_64bit = yes; then
+        acl_libdirstem=lib/64
+        case "$host_cpu" in
+          sparc*)        acl_libdirstem2=lib/sparcv9 ;;
+          i*86 | x86_64) acl_libdirstem2=lib/amd64 ;;
+        esac
+      fi
+      ;;
+    *)
+      searchpath=`(LC_ALL=C $CC -print-search-dirs) 2>/dev/null | sed -n -e 
's,^libraries: ,,p' | sed -e 's,^=,,'`
+      if test -n "$searchpath"; then
+        acl_save_IFS="${IFS=   }"; IFS=":"
+        for searchdir in $searchpath; do
+          if test -d "$searchdir"; then
+            case "$searchdir" in
+              */lib64/ | */lib64 ) acl_libdirstem=lib64 ;;
+              */../ | */.. )
+                # Better ignore directories of this form. They are misleading.
+                ;;
+              *) searchdir=`cd "$searchdir" && pwd`
+                 case "$searchdir" in
+                   */lib64 ) acl_libdirstem=lib64 ;;
+                 esac ;;
+            esac
+          fi
+        done
+        IFS="$acl_save_IFS"
+      fi
+      ;;
+  esac
+  test -n "$acl_libdirstem2" || acl_libdirstem2="$acl_libdirstem"
+])
diff --git a/m4/libtool.m4 b/m4/libtool.m4
new file mode 100644
index 0000000..ee80844
--- /dev/null
+++ b/m4/libtool.m4
@@ -0,0 +1,8387 @@
+# libtool.m4 - Configure libtool for the host system. -*-Autoconf-*-
+#
+#   Copyright (C) 1996-2001, 2003-2015 Free Software Foundation, Inc.
+#   Written by Gordon Matzigkeit, 1996
+#
+# This file is free software; the Free Software Foundation gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+
+m4_define([_LT_COPYING], [dnl
+# Copyright (C) 2014 Free Software Foundation, Inc.
+# This is free software; see the source for copying conditions.  There is NO
+# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+# GNU Libtool is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of of the License, or
+# (at your option) any later version.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program or library that is built
+# using GNU Libtool, you may include this file under the  same
+# distribution terms that you use for the rest of that program.
+#
+# GNU Libtool is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+])
+
+# serial 58 LT_INIT
+
+
+# LT_PREREQ(VERSION)
+# ------------------
+# Complain and exit if this libtool version is less that VERSION.
+m4_defun([LT_PREREQ],
+[m4_if(m4_version_compare(m4_defn([LT_PACKAGE_VERSION]), [$1]), -1,
+       [m4_default([$3],
+                  [m4_fatal([Libtool version $1 or higher is required],
+                            63)])],
+       [$2])])
+
+
+# _LT_CHECK_BUILDDIR
+# ------------------
+# Complain if the absolute build directory name contains unusual characters
+m4_defun([_LT_CHECK_BUILDDIR],
+[case `pwd` in
+  *\ * | *\    *)
+    AC_MSG_WARN([Libtool does not cope well with whitespace in `pwd`]) ;;
+esac
+])
+
+
+# LT_INIT([OPTIONS])
+# ------------------
+AC_DEFUN([LT_INIT],
+[AC_PREREQ([2.62])dnl We use AC_PATH_PROGS_FEATURE_CHECK
+AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl
+AC_BEFORE([$0], [LT_LANG])dnl
+AC_BEFORE([$0], [LT_OUTPUT])dnl
+AC_BEFORE([$0], [LTDL_INIT])dnl
+m4_require([_LT_CHECK_BUILDDIR])dnl
+
+dnl Autoconf doesn't catch unexpanded LT_ macros by default:
+m4_pattern_forbid([^_?LT_[A-Z_]+$])dnl
+m4_pattern_allow([^(_LT_EOF|LT_DLGLOBAL|LT_DLLAZY_OR_NOW|LT_MULTI_MODULE)$])dnl
+dnl aclocal doesn't pull ltoptions.m4, ltsugar.m4, or ltversion.m4
+dnl unless we require an AC_DEFUNed macro:
+AC_REQUIRE([LTOPTIONS_VERSION])dnl
+AC_REQUIRE([LTSUGAR_VERSION])dnl
+AC_REQUIRE([LTVERSION_VERSION])dnl
+AC_REQUIRE([LTOBSOLETE_VERSION])dnl
+m4_require([_LT_PROG_LTMAIN])dnl
+
+_LT_SHELL_INIT([SHELL=${CONFIG_SHELL-/bin/sh}])
+
+dnl Parse OPTIONS
+_LT_SET_OPTIONS([$0], [$1])
+
+# This can be used to rebuild libtool when needed
+LIBTOOL_DEPS=$ltmain
+
+# Always use our own libtool.
+LIBTOOL='$(SHELL) $(top_builddir)/libtool'
+AC_SUBST(LIBTOOL)dnl
+
+_LT_SETUP
+
+# Only expand once:
+m4_define([LT_INIT])
+])# LT_INIT
+
+# Old names:
+AU_ALIAS([AC_PROG_LIBTOOL], [LT_INIT])
+AU_ALIAS([AM_PROG_LIBTOOL], [LT_INIT])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_PROG_LIBTOOL], [])
+dnl AC_DEFUN([AM_PROG_LIBTOOL], [])
+
+
+# _LT_PREPARE_CC_BASENAME
+# -----------------------
+m4_defun([_LT_PREPARE_CC_BASENAME], [
+# Calculate cc_basename.  Skip known compiler wrappers and cross-prefix.
+func_cc_basename ()
+{
+    for cc_temp in @S|@*""; do
+      case $cc_temp in
+        compile | *[[\\/]]compile | ccache | *[[\\/]]ccache ) ;;
+        distcc | *[[\\/]]distcc | purify | *[[\\/]]purify ) ;;
+        \-*) ;;
+        *) break;;
+      esac
+    done
+    func_cc_basename_result=`$ECHO "$cc_temp" | $SED "s%.*/%%; 
s%^$host_alias-%%"`
+}
+])# _LT_PREPARE_CC_BASENAME
+
+
+# _LT_CC_BASENAME(CC)
+# -------------------
+# It would be clearer to call AC_REQUIREs from _LT_PREPARE_CC_BASENAME,
+# but that macro is also expanded into generated libtool script, which
+# arranges for $SED and $ECHO to be set by different means.
+m4_defun([_LT_CC_BASENAME],
+[m4_require([_LT_PREPARE_CC_BASENAME])dnl
+AC_REQUIRE([_LT_DECL_SED])dnl
+AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH])dnl
+func_cc_basename $1
+cc_basename=$func_cc_basename_result
+])
+
+
+# _LT_FILEUTILS_DEFAULTS
+# ----------------------
+# It is okay to use these file commands and assume they have been set
+# sensibly after 'm4_require([_LT_FILEUTILS_DEFAULTS])'.
+m4_defun([_LT_FILEUTILS_DEFAULTS],
+[: ${CP="cp -f"}
+: ${MV="mv -f"}
+: ${RM="rm -f"}
+])# _LT_FILEUTILS_DEFAULTS
+
+
+# _LT_SETUP
+# ---------
+m4_defun([_LT_SETUP],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+AC_REQUIRE([AC_CANONICAL_BUILD])dnl
+AC_REQUIRE([_LT_PREPARE_SED_QUOTE_VARS])dnl
+AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH])dnl
+
+_LT_DECL([], [PATH_SEPARATOR], [1], [The PATH separator for the build 
system])dnl
+dnl
+_LT_DECL([], [host_alias], [0], [The host system])dnl
+_LT_DECL([], [host], [0])dnl
+_LT_DECL([], [host_os], [0])dnl
+dnl
+_LT_DECL([], [build_alias], [0], [The build system])dnl
+_LT_DECL([], [build], [0])dnl
+_LT_DECL([], [build_os], [0])dnl
+dnl
+AC_REQUIRE([AC_PROG_CC])dnl
+AC_REQUIRE([LT_PATH_LD])dnl
+AC_REQUIRE([LT_PATH_NM])dnl
+dnl
+AC_REQUIRE([AC_PROG_LN_S])dnl
+test -z "$LN_S" && LN_S="ln -s"
+_LT_DECL([], [LN_S], [1], [Whether we need soft or hard links])dnl
+dnl
+AC_REQUIRE([LT_CMD_MAX_LEN])dnl
+_LT_DECL([objext], [ac_objext], [0], [Object file suffix (normally "o")])dnl
+_LT_DECL([], [exeext], [0], [Executable file suffix (normally "")])dnl
+dnl
+m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_CHECK_SHELL_FEATURES])dnl
+m4_require([_LT_PATH_CONVERSION_FUNCTIONS])dnl
+m4_require([_LT_CMD_RELOAD])dnl
+m4_require([_LT_CHECK_MAGIC_METHOD])dnl
+m4_require([_LT_CHECK_SHAREDLIB_FROM_LINKLIB])dnl
+m4_require([_LT_CMD_OLD_ARCHIVE])dnl
+m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl
+m4_require([_LT_WITH_SYSROOT])dnl
+m4_require([_LT_CMD_TRUNCATE])dnl
+
+_LT_CONFIG_LIBTOOL_INIT([
+# See if we are running on zsh, and set the options that allow our
+# commands through without removal of \ escapes INIT.
+if test -n "\${ZSH_VERSION+set}"; then
+   setopt NO_GLOB_SUBST
+fi
+])
+if test -n "${ZSH_VERSION+set}"; then
+   setopt NO_GLOB_SUBST
+fi
+
+_LT_CHECK_OBJDIR
+
+m4_require([_LT_TAG_COMPILER])dnl
+
+case $host_os in
+aix3*)
+  # AIX sometimes has problems with the GCC collect2 program.  For some
+  # reason, if we set the COLLECT_NAMES environment variable, the problems
+  # vanish in a puff of smoke.
+  if test set != "${COLLECT_NAMES+set}"; then
+    COLLECT_NAMES=
+    export COLLECT_NAMES
+  fi
+  ;;
+esac
+
+# Global variables:
+ofile=libtool
+can_build_shared=yes
+
+# All known linkers require a '.a' archive for static linking (except MSVC,
+# which needs '.lib').
+libext=a
+
+with_gnu_ld=$lt_cv_prog_gnu_ld
+
+old_CC=$CC
+old_CFLAGS=$CFLAGS
+
+# Set sane defaults for various variables
+test -z "$CC" && CC=cc
+test -z "$LTCC" && LTCC=$CC
+test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS
+test -z "$LD" && LD=ld
+test -z "$ac_objext" && ac_objext=o
+
+_LT_CC_BASENAME([$compiler])
+
+# Only perform the check for file, if the check method requires it
+test -z "$MAGIC_CMD" && MAGIC_CMD=file
+case $deplibs_check_method in
+file_magic*)
+  if test "$file_magic_cmd" = '$MAGIC_CMD'; then
+    _LT_PATH_MAGIC
+  fi
+  ;;
+esac
+
+# Use C for the default configuration in the libtool script
+LT_SUPPORTED_TAG([CC])
+_LT_LANG_C_CONFIG
+_LT_LANG_DEFAULT_CONFIG
+_LT_CONFIG_COMMANDS
+])# _LT_SETUP
+
+
+# _LT_PREPARE_SED_QUOTE_VARS
+# --------------------------
+# Define a few sed substitution that help us do robust quoting.
+m4_defun([_LT_PREPARE_SED_QUOTE_VARS],
+[# Backslashify metacharacters that are still active within
+# double-quoted strings.
+sed_quote_subst='s/\([["`$\\]]\)/\\\1/g'
+
+# Same as above, but do not quote variable references.
+double_quote_subst='s/\([["`\\]]\)/\\\1/g'
+
+# Sed substitution to delay expansion of an escaped shell variable in a
+# double_quote_subst'ed string.
+delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g'
+
+# Sed substitution to delay expansion of an escaped single quote.
+delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g'
+
+# Sed substitution to avoid accidental globbing in evaled expressions
+no_glob_subst='s/\*/\\\*/g'
+])
+
+# _LT_PROG_LTMAIN
+# ---------------
+# Note that this code is called both from 'configure', and 'config.status'
+# now that we use AC_CONFIG_COMMANDS to generate libtool.  Notably,
+# 'config.status' has no value for ac_aux_dir unless we are using Automake,
+# so we pass a copy along to make sure it has a sensible value anyway.
+m4_defun([_LT_PROG_LTMAIN],
+[m4_ifdef([AC_REQUIRE_AUX_FILE], [AC_REQUIRE_AUX_FILE([ltmain.sh])])dnl
+_LT_CONFIG_LIBTOOL_INIT([ac_aux_dir='$ac_aux_dir'])
+ltmain=$ac_aux_dir/ltmain.sh
+])# _LT_PROG_LTMAIN
+
+
+## ------------------------------------- ##
+## Accumulate code for creating libtool. ##
+## ------------------------------------- ##
+
+# So that we can recreate a full libtool script including additional
+# tags, we accumulate the chunks of code to send to AC_CONFIG_COMMANDS
+# in macros and then make a single call at the end using the 'libtool'
+# label.
+
+
+# _LT_CONFIG_LIBTOOL_INIT([INIT-COMMANDS])
+# ----------------------------------------
+# Register INIT-COMMANDS to be passed to AC_CONFIG_COMMANDS later.
+m4_define([_LT_CONFIG_LIBTOOL_INIT],
+[m4_ifval([$1],
+          [m4_append([_LT_OUTPUT_LIBTOOL_INIT],
+                     [$1
+])])])
+
+# Initialize.
+m4_define([_LT_OUTPUT_LIBTOOL_INIT])
+
+
+# _LT_CONFIG_LIBTOOL([COMMANDS])
+# ------------------------------
+# Register COMMANDS to be passed to AC_CONFIG_COMMANDS later.
+m4_define([_LT_CONFIG_LIBTOOL],
+[m4_ifval([$1],
+          [m4_append([_LT_OUTPUT_LIBTOOL_COMMANDS],
+                     [$1
+])])])
+
+# Initialize.
+m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS])
+
+
+# _LT_CONFIG_SAVE_COMMANDS([COMMANDS], [INIT_COMMANDS])
+# -----------------------------------------------------
+m4_defun([_LT_CONFIG_SAVE_COMMANDS],
+[_LT_CONFIG_LIBTOOL([$1])
+_LT_CONFIG_LIBTOOL_INIT([$2])
+])
+
+
+# _LT_FORMAT_COMMENT([COMMENT])
+# -----------------------------
+# Add leading comment marks to the start of each line, and a trailing
+# full-stop to the whole comment if one is not present already.
+m4_define([_LT_FORMAT_COMMENT],
+[m4_ifval([$1], [
+m4_bpatsubst([m4_bpatsubst([$1], [^ *], [# ])],
+              [['`$\]], [\\\&])]m4_bmatch([$1], [[!?.]$], [], [.])
+)])
+
+
+
+## ------------------------ ##
+## FIXME: Eliminate VARNAME ##
+## ------------------------ ##
+
+
+# _LT_DECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION], [IS-TAGGED?])
+# -------------------------------------------------------------------
+# CONFIGNAME is the name given to the value in the libtool script.
+# VARNAME is the (base) name used in the configure script.
+# VALUE may be 0, 1 or 2 for a computed quote escaped value based on
+# VARNAME.  Any other value will be used directly.
+m4_define([_LT_DECL],
+[lt_if_append_uniq([lt_decl_varnames], [$2], [, ],
+    [lt_dict_add_subkey([lt_decl_dict], [$2], [libtool_name],
+       [m4_ifval([$1], [$1], [$2])])
+    lt_dict_add_subkey([lt_decl_dict], [$2], [value], [$3])
+    m4_ifval([$4],
+       [lt_dict_add_subkey([lt_decl_dict], [$2], [description], [$4])])
+    lt_dict_add_subkey([lt_decl_dict], [$2],
+       [tagged?], [m4_ifval([$5], [yes], [no])])])
+])
+
+
+# _LT_TAGDECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION])
+# --------------------------------------------------------
+m4_define([_LT_TAGDECL], [_LT_DECL([$1], [$2], [$3], [$4], [yes])])
+
+
+# lt_decl_tag_varnames([SEPARATOR], [VARNAME1...])
+# ------------------------------------------------
+m4_define([lt_decl_tag_varnames],
+[_lt_decl_filter([tagged?], [yes], $@)])
+
+
+# _lt_decl_filter(SUBKEY, VALUE, [SEPARATOR], [VARNAME1..])
+# ---------------------------------------------------------
+m4_define([_lt_decl_filter],
+[m4_case([$#],
+  [0], [m4_fatal([$0: too few arguments: $#])],
+  [1], [m4_fatal([$0: too few arguments: $#: $1])],
+  [2], [lt_dict_filter([lt_decl_dict], [$1], [$2], [], lt_decl_varnames)],
+  [3], [lt_dict_filter([lt_decl_dict], [$1], [$2], [$3], lt_decl_varnames)],
+  [lt_dict_filter([lt_decl_dict], $@)])[]dnl
+])
+
+
+# lt_decl_quote_varnames([SEPARATOR], [VARNAME1...])
+# --------------------------------------------------
+m4_define([lt_decl_quote_varnames],
+[_lt_decl_filter([value], [1], $@)])
+
+
+# lt_decl_dquote_varnames([SEPARATOR], [VARNAME1...])
+# ---------------------------------------------------
+m4_define([lt_decl_dquote_varnames],
+[_lt_decl_filter([value], [2], $@)])
+
+
+# lt_decl_varnames_tagged([SEPARATOR], [VARNAME1...])
+# ---------------------------------------------------
+m4_define([lt_decl_varnames_tagged],
+[m4_assert([$# <= 2])dnl
+_$0(m4_quote(m4_default([$1], [[, ]])),
+    m4_ifval([$2], [[$2]], [m4_dquote(lt_decl_tag_varnames)]),
+    m4_split(m4_normalize(m4_quote(_LT_TAGS)), [ ]))])
+m4_define([_lt_decl_varnames_tagged],
+[m4_ifval([$3], [lt_combine([$1], [$2], [_], $3)])])
+
+
+# lt_decl_all_varnames([SEPARATOR], [VARNAME1...])
+# ------------------------------------------------
+m4_define([lt_decl_all_varnames],
+[_$0(m4_quote(m4_default([$1], [[, ]])),
+     m4_if([$2], [],
+          m4_quote(lt_decl_varnames),
+       m4_quote(m4_shift($@))))[]dnl
+])
+m4_define([_lt_decl_all_varnames],
+[lt_join($@, lt_decl_varnames_tagged([$1],
+                       lt_decl_tag_varnames([[, ]], m4_shift($@))))dnl
+])
+
+
+# _LT_CONFIG_STATUS_DECLARE([VARNAME])
+# ------------------------------------
+# Quote a variable value, and forward it to 'config.status' so that its
+# declaration there will have the same value as in 'configure'.  VARNAME
+# must have a single quote delimited value for this to work.
+m4_define([_LT_CONFIG_STATUS_DECLARE],
+[$1='`$ECHO "$][$1" | $SED "$delay_single_quote_subst"`'])
+
+
+# _LT_CONFIG_STATUS_DECLARATIONS
+# ------------------------------
+# We delimit libtool config variables with single quotes, so when
+# we write them to config.status, we have to be sure to quote all
+# embedded single quotes properly.  In configure, this macro expands
+# each variable declared with _LT_DECL (and _LT_TAGDECL) into:
+#
+#    <var>='`$ECHO "$<var>" | $SED "$delay_single_quote_subst"`'
+m4_defun([_LT_CONFIG_STATUS_DECLARATIONS],
+[m4_foreach([_lt_var], m4_quote(lt_decl_all_varnames),
+    [m4_n([_LT_CONFIG_STATUS_DECLARE(_lt_var)])])])
+
+
+# _LT_LIBTOOL_TAGS
+# ----------------
+# Output comment and list of tags supported by the script
+m4_defun([_LT_LIBTOOL_TAGS],
+[_LT_FORMAT_COMMENT([The names of the tagged configurations supported by this 
script])dnl
+available_tags='_LT_TAGS'dnl
+])
+
+
+# _LT_LIBTOOL_DECLARE(VARNAME, [TAG])
+# -----------------------------------
+# Extract the dictionary values for VARNAME (optionally with TAG) and
+# expand to a commented shell variable setting:
+#
+#    # Some comment about what VAR is for.
+#    visible_name=$lt_internal_name
+m4_define([_LT_LIBTOOL_DECLARE],
+[_LT_FORMAT_COMMENT(m4_quote(lt_dict_fetch([lt_decl_dict], [$1],
+                                          [description])))[]dnl
+m4_pushdef([_libtool_name],
+    m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [libtool_name])))[]dnl
+m4_case(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [value])),
+    [0], [_libtool_name=[$]$1],
+    [1], [_libtool_name=$lt_[]$1],
+    [2], [_libtool_name=$lt_[]$1],
+    [_libtool_name=lt_dict_fetch([lt_decl_dict], [$1], [value])])[]dnl
+m4_ifval([$2], [_$2])[]m4_popdef([_libtool_name])[]dnl
+])
+
+
+# _LT_LIBTOOL_CONFIG_VARS
+# -----------------------
+# Produce commented declarations of non-tagged libtool config variables
+# suitable for insertion in the LIBTOOL CONFIG section of the 'libtool'
+# script.  Tagged libtool config variables (even for the LIBTOOL CONFIG
+# section) are produced by _LT_LIBTOOL_TAG_VARS.
+m4_defun([_LT_LIBTOOL_CONFIG_VARS],
+[m4_foreach([_lt_var],
+    m4_quote(_lt_decl_filter([tagged?], [no], [], lt_decl_varnames)),
+    [m4_n([_LT_LIBTOOL_DECLARE(_lt_var)])])])
+
+
+# _LT_LIBTOOL_TAG_VARS(TAG)
+# -------------------------
+m4_define([_LT_LIBTOOL_TAG_VARS],
+[m4_foreach([_lt_var], m4_quote(lt_decl_tag_varnames),
+    [m4_n([_LT_LIBTOOL_DECLARE(_lt_var, [$1])])])])
+
+
+# _LT_TAGVAR(VARNAME, [TAGNAME])
+# ------------------------------
+m4_define([_LT_TAGVAR], [m4_ifval([$2], [$1_$2], [$1])])
+
+
+# _LT_CONFIG_COMMANDS
+# -------------------
+# Send accumulated output to $CONFIG_STATUS.  Thanks to the lists of
+# variables for single and double quote escaping we saved from calls
+# to _LT_DECL, we can put quote escaped variables declarations
+# into 'config.status', and then the shell code to quote escape them in
+# for loops in 'config.status'.  Finally, any additional code accumulated
+# from calls to _LT_CONFIG_LIBTOOL_INIT is expanded.
+m4_defun([_LT_CONFIG_COMMANDS],
+[AC_PROVIDE_IFELSE([LT_OUTPUT],
+       dnl If the libtool generation code has been placed in $CONFIG_LT,
+       dnl instead of duplicating it all over again into config.status,
+       dnl then we will have config.status run $CONFIG_LT later, so it
+       dnl needs to know what name is stored there:
+        [AC_CONFIG_COMMANDS([libtool],
+            [$SHELL $CONFIG_LT || AS_EXIT(1)], [CONFIG_LT='$CONFIG_LT'])],
+    dnl If the libtool generation code is destined for config.status,
+    dnl expand the accumulated commands and init code now:
+    [AC_CONFIG_COMMANDS([libtool],
+        [_LT_OUTPUT_LIBTOOL_COMMANDS], [_LT_OUTPUT_LIBTOOL_COMMANDS_INIT])])
+])#_LT_CONFIG_COMMANDS
+
+
+# Initialize.
+m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS_INIT],
+[
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+sed_quote_subst='$sed_quote_subst'
+double_quote_subst='$double_quote_subst'
+delay_variable_subst='$delay_variable_subst'
+_LT_CONFIG_STATUS_DECLARATIONS
+LTCC='$LTCC'
+LTCFLAGS='$LTCFLAGS'
+compiler='$compiler_DEFAULT'
+
+# A function that is used when there is no print builtin or printf.
+func_fallback_echo ()
+{
+  eval 'cat <<_LTECHO_EOF
+\$[]1
+_LTECHO_EOF'
+}
+
+# Quote evaled strings.
+for var in lt_decl_all_varnames([[ \
+]], lt_decl_quote_varnames); do
+    case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in
+    *[[\\\\\\\`\\"\\\$]]*)
+      eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED 
\\"\\\$sed_quote_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes
+      ;;
+    *)
+      eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\""
+      ;;
+    esac
+done
+
+# Double-quote double-evaled strings.
+for var in lt_decl_all_varnames([[ \
+]], lt_decl_dquote_varnames); do
+    case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in
+    *[[\\\\\\\`\\"\\\$]]*)
+      eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e 
\\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e 
\\"\\\$delay_variable_subst\\"\\\`\\\\\\"" ## exclude from 
sc_prohibit_nested_quotes
+      ;;
+    *)
+      eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\""
+      ;;
+    esac
+done
+
+_LT_OUTPUT_LIBTOOL_INIT
+])
+
+# _LT_GENERATED_FILE_INIT(FILE, [COMMENT])
+# ------------------------------------
+# Generate a child script FILE with all initialization necessary to
+# reuse the environment learned by the parent script, and make the
+# file executable.  If COMMENT is supplied, it is inserted after the
+# '#!' sequence but before initialization text begins.  After this
+# macro, additional text can be appended to FILE to form the body of
+# the child script.  The macro ends with non-zero status if the
+# file could not be fully written (such as if the disk is full).
+m4_ifdef([AS_INIT_GENERATED],
+[m4_defun([_LT_GENERATED_FILE_INIT],[AS_INIT_GENERATED($@)])],
+[m4_defun([_LT_GENERATED_FILE_INIT],
+[m4_require([AS_PREPARE])]dnl
+[m4_pushdef([AS_MESSAGE_LOG_FD])]dnl
+[lt_write_fail=0
+cat >$1 <<_ASEOF || lt_write_fail=1
+#! $SHELL
+# Generated by $as_me.
+$2
+SHELL=\${CONFIG_SHELL-$SHELL}
+export SHELL
+_ASEOF
+cat >>$1 <<\_ASEOF || lt_write_fail=1
+AS_SHELL_SANITIZE
+_AS_PREPARE
+exec AS_MESSAGE_FD>&1
+_ASEOF
+test 0 = "$lt_write_fail" && chmod +x $1[]dnl
+m4_popdef([AS_MESSAGE_LOG_FD])])])# _LT_GENERATED_FILE_INIT
+
+# LT_OUTPUT
+# ---------
+# This macro allows early generation of the libtool script (before
+# AC_OUTPUT is called), incase it is used in configure for compilation
+# tests.
+AC_DEFUN([LT_OUTPUT],
+[: ${CONFIG_LT=./config.lt}
+AC_MSG_NOTICE([creating $CONFIG_LT])
+_LT_GENERATED_FILE_INIT(["$CONFIG_LT"],
+[# Run this file to recreate a libtool stub with the current configuration.])
+
+cat >>"$CONFIG_LT" <<\_LTEOF
+lt_cl_silent=false
+exec AS_MESSAGE_LOG_FD>>config.log
+{
+  echo
+  AS_BOX([Running $as_me.])
+} >&AS_MESSAGE_LOG_FD
+
+lt_cl_help="\
+'$as_me' creates a local libtool stub from the current configuration,
+for use in further configure time tests before the real libtool is
+generated.
+
+Usage: $[0] [[OPTIONS]]
+
+  -h, --help      print this help, then exit
+  -V, --version   print version number, then exit
+  -q, --quiet     do not print progress messages
+  -d, --debug     don't remove temporary files
+
+Report bugs to <address@hidden>."
+
+lt_cl_version="\
+m4_ifset([AC_PACKAGE_NAME], [AC_PACKAGE_NAME ])config.lt[]dnl
+m4_ifset([AC_PACKAGE_VERSION], [ AC_PACKAGE_VERSION])
+configured by $[0], generated by m4_PACKAGE_STRING.
+
+Copyright (C) 2011 Free Software Foundation, Inc.
+This config.lt script is free software; the Free Software Foundation
+gives unlimited permision to copy, distribute and modify it."
+
+while test 0 != $[#]
+do
+  case $[1] in
+    --version | --v* | -V )
+      echo "$lt_cl_version"; exit 0 ;;
+    --help | --h* | -h )
+      echo "$lt_cl_help"; exit 0 ;;
+    --debug | --d* | -d )
+      debug=: ;;
+    --quiet | --q* | --silent | --s* | -q )
+      lt_cl_silent=: ;;
+
+    -*) AC_MSG_ERROR([unrecognized option: $[1]
+Try '$[0] --help' for more information.]) ;;
+
+    *) AC_MSG_ERROR([unrecognized argument: $[1]
+Try '$[0] --help' for more information.]) ;;
+  esac
+  shift
+done
+
+if $lt_cl_silent; then
+  exec AS_MESSAGE_FD>/dev/null
+fi
+_LTEOF
+
+cat >>"$CONFIG_LT" <<_LTEOF
+_LT_OUTPUT_LIBTOOL_COMMANDS_INIT
+_LTEOF
+
+cat >>"$CONFIG_LT" <<\_LTEOF
+AC_MSG_NOTICE([creating $ofile])
+_LT_OUTPUT_LIBTOOL_COMMANDS
+AS_EXIT(0)
+_LTEOF
+chmod +x "$CONFIG_LT"
+
+# configure is writing to config.log, but config.lt does its own redirection,
+# appending to config.log, which fails on DOS, as config.log is still kept
+# open by configure.  Here we exec the FD to /dev/null, effectively closing
+# config.log, so it can be properly (re)opened and appended to by config.lt.
+lt_cl_success=:
+test yes = "$silent" &&
+  lt_config_lt_args="$lt_config_lt_args --quiet"
+exec AS_MESSAGE_LOG_FD>/dev/null
+$SHELL "$CONFIG_LT" $lt_config_lt_args || lt_cl_success=false
+exec AS_MESSAGE_LOG_FD>>config.log
+$lt_cl_success || AS_EXIT(1)
+])# LT_OUTPUT
+
+
+# _LT_CONFIG(TAG)
+# ---------------
+# If TAG is the built-in tag, create an initial libtool script with a
+# default configuration from the untagged config vars.  Otherwise add code
+# to config.status for appending the configuration named by TAG from the
+# matching tagged config vars.
+m4_defun([_LT_CONFIG],
+[m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+_LT_CONFIG_SAVE_COMMANDS([
+  m4_define([_LT_TAG], m4_if([$1], [], [C], [$1]))dnl
+  m4_if(_LT_TAG, [C], [
+    # See if we are running on zsh, and set the options that allow our
+    # commands through without removal of \ escapes.
+    if test -n "${ZSH_VERSION+set}"; then
+      setopt NO_GLOB_SUBST
+    fi
+
+    cfgfile=${ofile}T
+    trap "$RM \"$cfgfile\"; exit 1" 1 2 15
+    $RM "$cfgfile"
+
+    cat <<_LT_EOF >> "$cfgfile"
+#! $SHELL
+# Generated automatically by $as_me ($PACKAGE) $VERSION
+# NOTE: Changes made to this file will be lost: look at ltmain.sh.
+
+# Provide generalized library-building support services.
+# Written by Gordon Matzigkeit, 1996
+
+_LT_COPYING
+_LT_LIBTOOL_TAGS
+
+# Configured defaults for sys_lib_dlsearch_path munging.
+: \${LT_SYS_LIBRARY_PATH="$configure_time_lt_sys_library_path"}
+
+# ### BEGIN LIBTOOL CONFIG
+_LT_LIBTOOL_CONFIG_VARS
+_LT_LIBTOOL_TAG_VARS
+# ### END LIBTOOL CONFIG
+
+_LT_EOF
+
+    cat <<'_LT_EOF' >> "$cfgfile"
+
+# ### BEGIN FUNCTIONS SHARED WITH CONFIGURE
+
+_LT_PREPARE_MUNGE_PATH_LIST
+_LT_PREPARE_CC_BASENAME
+
+# ### END FUNCTIONS SHARED WITH CONFIGURE
+
+_LT_EOF
+
+  case $host_os in
+  aix3*)
+    cat <<\_LT_EOF >> "$cfgfile"
+# AIX sometimes has problems with the GCC collect2 program.  For some
+# reason, if we set the COLLECT_NAMES environment variable, the problems
+# vanish in a puff of smoke.
+if test set != "${COLLECT_NAMES+set}"; then
+  COLLECT_NAMES=
+  export COLLECT_NAMES
+fi
+_LT_EOF
+    ;;
+  esac
+
+  _LT_PROG_LTMAIN
+
+  # We use sed instead of cat because bash on DJGPP gets confused if
+  # if finds mixed CR/LF and LF-only lines.  Since sed operates in
+  # text mode, it properly converts lines to CR/LF.  This bash problem
+  # is reportedly fixed, but why not run on old versions too?
+  sed '$q' "$ltmain" >> "$cfgfile" \
+     || (rm -f "$cfgfile"; exit 1)
+
+   mv -f "$cfgfile" "$ofile" ||
+    (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile")
+  chmod +x "$ofile"
+],
+[cat <<_LT_EOF >> "$ofile"
+
+dnl Unfortunately we have to use $1 here, since _LT_TAG is not expanded
+dnl in a comment (ie after a #).
+# ### BEGIN LIBTOOL TAG CONFIG: $1
+_LT_LIBTOOL_TAG_VARS(_LT_TAG)
+# ### END LIBTOOL TAG CONFIG: $1
+_LT_EOF
+])dnl /m4_if
+],
+[m4_if([$1], [], [
+    PACKAGE='$PACKAGE'
+    VERSION='$VERSION'
+    RM='$RM'
+    ofile='$ofile'], [])
+])dnl /_LT_CONFIG_SAVE_COMMANDS
+])# _LT_CONFIG
+
+
+# LT_SUPPORTED_TAG(TAG)
+# ---------------------
+# Trace this macro to discover what tags are supported by the libtool
+# --tag option, using:
+#    autoconf --trace 'LT_SUPPORTED_TAG:$1'
+AC_DEFUN([LT_SUPPORTED_TAG], [])
+
+
+# C support is built-in for now
+m4_define([_LT_LANG_C_enabled], [])
+m4_define([_LT_TAGS], [])
+
+
+# LT_LANG(LANG)
+# -------------
+# Enable libtool support for the given language if not already enabled.
+AC_DEFUN([LT_LANG],
+[AC_BEFORE([$0], [LT_OUTPUT])dnl
+m4_case([$1],
+  [C],                 [_LT_LANG(C)],
+  [C++],               [_LT_LANG(CXX)],
+  [Go],                        [_LT_LANG(GO)],
+  [Java],              [_LT_LANG(GCJ)],
+  [Fortran 77],                [_LT_LANG(F77)],
+  [Fortran],           [_LT_LANG(FC)],
+  [Windows Resource],  [_LT_LANG(RC)],
+  [m4_ifdef([_LT_LANG_]$1[_CONFIG],
+    [_LT_LANG($1)],
+    [m4_fatal([$0: unsupported language: "$1"])])])dnl
+])# LT_LANG
+
+
+# _LT_LANG(LANGNAME)
+# ------------------
+m4_defun([_LT_LANG],
+[m4_ifdef([_LT_LANG_]$1[_enabled], [],
+  [LT_SUPPORTED_TAG([$1])dnl
+  m4_append([_LT_TAGS], [$1 ])dnl
+  m4_define([_LT_LANG_]$1[_enabled], [])dnl
+  _LT_LANG_$1_CONFIG($1)])dnl
+])# _LT_LANG
+
+
+m4_ifndef([AC_PROG_GO], [
+############################################################
+# NOTE: This macro has been submitted for inclusion into   #
+#  GNU Autoconf as AC_PROG_GO.  When it is available in    #
+#  a released version of Autoconf we should remove this    #
+#  macro and use it instead.                               #
+############################################################
+m4_defun([AC_PROG_GO],
+[AC_LANG_PUSH(Go)dnl
+AC_ARG_VAR([GOC],     [Go compiler command])dnl
+AC_ARG_VAR([GOFLAGS], [Go compiler flags])dnl
+_AC_ARG_VAR_LDFLAGS()dnl
+AC_CHECK_TOOL(GOC, gccgo)
+if test -z "$GOC"; then
+  if test -n "$ac_tool_prefix"; then
+    AC_CHECK_PROG(GOC, [${ac_tool_prefix}gccgo], [${ac_tool_prefix}gccgo])
+  fi
+fi
+if test -z "$GOC"; then
+  AC_CHECK_PROG(GOC, gccgo, gccgo, false)
+fi
+])#m4_defun
+])#m4_ifndef
+
+
+# _LT_LANG_DEFAULT_CONFIG
+# -----------------------
+m4_defun([_LT_LANG_DEFAULT_CONFIG],
+[AC_PROVIDE_IFELSE([AC_PROG_CXX],
+  [LT_LANG(CXX)],
+  [m4_define([AC_PROG_CXX], defn([AC_PROG_CXX])[LT_LANG(CXX)])])
+
+AC_PROVIDE_IFELSE([AC_PROG_F77],
+  [LT_LANG(F77)],
+  [m4_define([AC_PROG_F77], defn([AC_PROG_F77])[LT_LANG(F77)])])
+
+AC_PROVIDE_IFELSE([AC_PROG_FC],
+  [LT_LANG(FC)],
+  [m4_define([AC_PROG_FC], defn([AC_PROG_FC])[LT_LANG(FC)])])
+
+dnl The call to [A][M_PROG_GCJ] is quoted like that to stop aclocal
+dnl pulling things in needlessly.
+AC_PROVIDE_IFELSE([AC_PROG_GCJ],
+  [LT_LANG(GCJ)],
+  [AC_PROVIDE_IFELSE([A][M_PROG_GCJ],
+    [LT_LANG(GCJ)],
+    [AC_PROVIDE_IFELSE([LT_PROG_GCJ],
+      [LT_LANG(GCJ)],
+      [m4_ifdef([AC_PROG_GCJ],
+       [m4_define([AC_PROG_GCJ], defn([AC_PROG_GCJ])[LT_LANG(GCJ)])])
+       m4_ifdef([A][M_PROG_GCJ],
+       [m4_define([A][M_PROG_GCJ], defn([A][M_PROG_GCJ])[LT_LANG(GCJ)])])
+       m4_ifdef([LT_PROG_GCJ],
+       [m4_define([LT_PROG_GCJ], defn([LT_PROG_GCJ])[LT_LANG(GCJ)])])])])])
+
+AC_PROVIDE_IFELSE([AC_PROG_GO],
+  [LT_LANG(GO)],
+  [m4_define([AC_PROG_GO], defn([AC_PROG_GO])[LT_LANG(GO)])])
+
+AC_PROVIDE_IFELSE([LT_PROG_RC],
+  [LT_LANG(RC)],
+  [m4_define([LT_PROG_RC], defn([LT_PROG_RC])[LT_LANG(RC)])])
+])# _LT_LANG_DEFAULT_CONFIG
+
+# Obsolete macros:
+AU_DEFUN([AC_LIBTOOL_CXX], [LT_LANG(C++)])
+AU_DEFUN([AC_LIBTOOL_F77], [LT_LANG(Fortran 77)])
+AU_DEFUN([AC_LIBTOOL_FC], [LT_LANG(Fortran)])
+AU_DEFUN([AC_LIBTOOL_GCJ], [LT_LANG(Java)])
+AU_DEFUN([AC_LIBTOOL_RC], [LT_LANG(Windows Resource)])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_CXX], [])
+dnl AC_DEFUN([AC_LIBTOOL_F77], [])
+dnl AC_DEFUN([AC_LIBTOOL_FC], [])
+dnl AC_DEFUN([AC_LIBTOOL_GCJ], [])
+dnl AC_DEFUN([AC_LIBTOOL_RC], [])
+
+
+# _LT_TAG_COMPILER
+# ----------------
+m4_defun([_LT_TAG_COMPILER],
+[AC_REQUIRE([AC_PROG_CC])dnl
+
+_LT_DECL([LTCC], [CC], [1], [A C compiler])dnl
+_LT_DECL([LTCFLAGS], [CFLAGS], [1], [LTCC compiler flags])dnl
+_LT_TAGDECL([CC], [compiler], [1], [A language specific compiler])dnl
+_LT_TAGDECL([with_gcc], [GCC], [0], [Is the compiler the GNU compiler?])dnl
+
+# If no C compiler was specified, use CC.
+LTCC=${LTCC-"$CC"}
+
+# If no C compiler flags were specified, use CFLAGS.
+LTCFLAGS=${LTCFLAGS-"$CFLAGS"}
+
+# Allow CC to be a program name with arguments.
+compiler=$CC
+])# _LT_TAG_COMPILER
+
+
+# _LT_COMPILER_BOILERPLATE
+# ------------------------
+# Check for compiler boilerplate output or warnings with
+# the simple compiler test code.
+m4_defun([_LT_COMPILER_BOILERPLATE],
+[m4_require([_LT_DECL_SED])dnl
+ac_outfile=conftest.$ac_objext
+echo "$lt_simple_compile_test_code" >conftest.$ac_ext
+eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err
+_lt_compiler_boilerplate=`cat conftest.err`
+$RM conftest*
+])# _LT_COMPILER_BOILERPLATE
+
+
+# _LT_LINKER_BOILERPLATE
+# ----------------------
+# Check for linker boilerplate output or warnings with
+# the simple link test code.
+m4_defun([_LT_LINKER_BOILERPLATE],
+[m4_require([_LT_DECL_SED])dnl
+ac_outfile=conftest.$ac_objext
+echo "$lt_simple_link_test_code" >conftest.$ac_ext
+eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err
+_lt_linker_boilerplate=`cat conftest.err`
+$RM -r conftest*
+])# _LT_LINKER_BOILERPLATE
+
+# _LT_REQUIRED_DARWIN_CHECKS
+# -------------------------
+m4_defun_once([_LT_REQUIRED_DARWIN_CHECKS],[
+  case $host_os in
+    rhapsody* | darwin*)
+    AC_CHECK_TOOL([DSYMUTIL], [dsymutil], [:])
+    AC_CHECK_TOOL([NMEDIT], [nmedit], [:])
+    AC_CHECK_TOOL([LIPO], [lipo], [:])
+    AC_CHECK_TOOL([OTOOL], [otool], [:])
+    AC_CHECK_TOOL([OTOOL64], [otool64], [:])
+    _LT_DECL([], [DSYMUTIL], [1],
+      [Tool to manipulate archived DWARF debug symbol files on Mac OS X])
+    _LT_DECL([], [NMEDIT], [1],
+      [Tool to change global to local symbols on Mac OS X])
+    _LT_DECL([], [LIPO], [1],
+      [Tool to manipulate fat objects and archives on Mac OS X])
+    _LT_DECL([], [OTOOL], [1],
+      [ldd/readelf like tool for Mach-O binaries on Mac OS X])
+    _LT_DECL([], [OTOOL64], [1],
+      [ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4])
+
+    AC_CACHE_CHECK([for -single_module linker 
flag],[lt_cv_apple_cc_single_mod],
+      [lt_cv_apple_cc_single_mod=no
+      if test -z "$LT_MULTI_MODULE"; then
+       # By default we will add the -single_module flag. You can override
+       # by either setting the environment variable LT_MULTI_MODULE
+       # non-empty at configure time, or by adding -multi_module to the
+       # link flags.
+       rm -rf libconftest.dylib*
+       echo "int foo(void){return 1;}" > conftest.c
+       echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \
+-dynamiclib -Wl,-single_module conftest.c" >&AS_MESSAGE_LOG_FD
+       $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \
+         -dynamiclib -Wl,-single_module conftest.c 2>conftest.err
+        _lt_result=$?
+       # If there is a non-empty error log, and "single_module"
+       # appears in it, assume the flag caused a linker warning
+        if test -s conftest.err && $GREP single_module conftest.err; then
+         cat conftest.err >&AS_MESSAGE_LOG_FD
+       # Otherwise, if the output was created with a 0 exit code from
+       # the compiler, it worked.
+       elif test -f libconftest.dylib && test 0 = "$_lt_result"; then
+         lt_cv_apple_cc_single_mod=yes
+       else
+         cat conftest.err >&AS_MESSAGE_LOG_FD
+       fi
+       rm -rf libconftest.dylib*
+       rm -f conftest.*
+      fi])
+
+    AC_CACHE_CHECK([for -exported_symbols_list linker flag],
+      [lt_cv_ld_exported_symbols_list],
+      [lt_cv_ld_exported_symbols_list=no
+      save_LDFLAGS=$LDFLAGS
+      echo "_main" > conftest.sym
+      LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym"
+      AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])],
+       [lt_cv_ld_exported_symbols_list=yes],
+       [lt_cv_ld_exported_symbols_list=no])
+       LDFLAGS=$save_LDFLAGS
+    ])
+
+    AC_CACHE_CHECK([for -force_load linker flag],[lt_cv_ld_force_load],
+      [lt_cv_ld_force_load=no
+      cat > conftest.c << _LT_EOF
+int forced_loaded() { return 2;}
+_LT_EOF
+      echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&AS_MESSAGE_LOG_FD
+      $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&AS_MESSAGE_LOG_FD
+      echo "$AR cru libconftest.a conftest.o" >&AS_MESSAGE_LOG_FD
+      $AR cru libconftest.a conftest.o 2>&AS_MESSAGE_LOG_FD
+      echo "$RANLIB libconftest.a" >&AS_MESSAGE_LOG_FD
+      $RANLIB libconftest.a 2>&AS_MESSAGE_LOG_FD
+      cat > conftest.c << _LT_EOF
+int main() { return 0;}
+_LT_EOF
+      echo "$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c 
-Wl,-force_load,./libconftest.a" >&AS_MESSAGE_LOG_FD
+      $LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c 
-Wl,-force_load,./libconftest.a 2>conftest.err
+      _lt_result=$?
+      if test -s conftest.err && $GREP force_load conftest.err; then
+       cat conftest.err >&AS_MESSAGE_LOG_FD
+      elif test -f conftest && test 0 = "$_lt_result" && $GREP forced_load 
conftest >/dev/null 2>&1; then
+       lt_cv_ld_force_load=yes
+      else
+       cat conftest.err >&AS_MESSAGE_LOG_FD
+      fi
+        rm -f conftest.err libconftest.a conftest conftest.c
+        rm -rf conftest.dSYM
+    ])
+    case $host_os in
+    rhapsody* | darwin1.[[012]])
+      _lt_dar_allow_undefined='$wl-undefined ${wl}suppress' ;;
+    darwin1.*)
+      _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' 
;;
+    darwin*) # darwin 5.x on
+      # if running on 10.5 or later, the deployment target defaults
+      # to the OS version, if on x86, and 10.4, the deployment
+      # target defaults to 10.4. Don't you love it?
+      case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in
+       10.0,*86*-darwin8*|10.0,*-darwin[[91]]*)
+         _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;;
+       10.[[012]][[,.]]*)
+         _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined 
${wl}suppress' ;;
+       10.*)
+         _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;;
+      esac
+    ;;
+  esac
+    if test yes = "$lt_cv_apple_cc_single_mod"; then
+      _lt_dar_single_mod='$single_module'
+    fi
+    if test yes = "$lt_cv_ld_exported_symbols_list"; then
+      _lt_dar_export_syms=' 
$wl-exported_symbols_list,$output_objdir/$libname-symbols.expsym'
+    else
+      _lt_dar_export_syms='~$NMEDIT -s $output_objdir/$libname-symbols.expsym 
$lib'
+    fi
+    if test : != "$DSYMUTIL" && test no = "$lt_cv_ld_force_load"; then
+      _lt_dsymutil='~$DSYMUTIL $lib || :'
+    else
+      _lt_dsymutil=
+    fi
+    ;;
+  esac
+])
+
+
+# _LT_DARWIN_LINKER_FEATURES([TAG])
+# ---------------------------------
+# Checks for linker and compiler features on darwin
+m4_defun([_LT_DARWIN_LINKER_FEATURES],
+[
+  m4_require([_LT_REQUIRED_DARWIN_CHECKS])
+  _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+  _LT_TAGVAR(hardcode_direct, $1)=no
+  _LT_TAGVAR(hardcode_automatic, $1)=yes
+  _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported
+  if test yes = "$lt_cv_ld_force_load"; then
+    _LT_TAGVAR(whole_archive_flag_spec, $1)='`for conv in $convenience\"\"; do 
test  -n \"$conv\" && new_convenience=\"$new_convenience 
$wl-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`'
+    m4_case([$1], [F77], [_LT_TAGVAR(compiler_needs_object, $1)=yes],
+                  [FC],  [_LT_TAGVAR(compiler_needs_object, $1)=yes])
+  else
+    _LT_TAGVAR(whole_archive_flag_spec, $1)=''
+  fi
+  _LT_TAGVAR(link_all_deplibs, $1)=yes
+  _LT_TAGVAR(allow_undefined_flag, $1)=$_lt_dar_allow_undefined
+  case $cc_basename in
+     ifort*|nagfor*) _lt_dar_can_shared=yes ;;
+     *) _lt_dar_can_shared=$GCC ;;
+  esac
+  if test yes = "$_lt_dar_can_shared"; then
+    output_verbose_link_cmd=func_echo_all
+    _LT_TAGVAR(archive_cmds, $1)="\$CC -dynamiclib \$allow_undefined_flag -o 
\$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname 
\$verstring $_lt_dar_single_mod$_lt_dsymutil"
+    _LT_TAGVAR(module_cmds, $1)="\$CC \$allow_undefined_flag -o \$lib -bundle 
\$libobjs \$deplibs \$compiler_flags$_lt_dsymutil"
+    _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's|^|_|' < \$export_symbols > 
\$output_objdir/\$libname-symbols.expsym~\$CC -dynamiclib 
\$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags 
-install_name \$rpath/\$soname \$verstring 
$_lt_dar_single_mod$_lt_dar_export_syms$_lt_dsymutil"
+    _LT_TAGVAR(module_expsym_cmds, $1)="sed -e 's|^|_|' < \$export_symbols > 
\$output_objdir/\$libname-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib 
-bundle \$libobjs \$deplibs \$compiler_flags$_lt_dar_export_syms$_lt_dsymutil"
+    m4_if([$1], [CXX],
+[   if test yes != "$lt_cv_apple_cc_single_mod"; then
+      _LT_TAGVAR(archive_cmds, $1)="\$CC -r -keep_private_externs -nostdlib -o 
\$lib-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib 
\$lib-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname 
\$verstring$_lt_dsymutil"
+      _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's|^|_|' < \$export_symbols > 
\$output_objdir/\$libname-symbols.expsym~\$CC -r -keep_private_externs 
-nostdlib -o \$lib-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag 
-o \$lib \$lib-master.o \$deplibs \$compiler_flags -install_name 
\$rpath/\$soname \$verstring$_lt_dar_export_syms$_lt_dsymutil"
+    fi
+],[])
+  else
+  _LT_TAGVAR(ld_shlibs, $1)=no
+  fi
+])
+
+# _LT_SYS_MODULE_PATH_AIX([TAGNAME])
+# ----------------------------------
+# Links a minimal program and checks the executable
+# for the system default hardcoded library path. In most cases,
+# this is /usr/lib:/lib, but when the MPI compilers are used
+# the location of the communication and MPI libs are included too.
+# If we don't find anything, use the default library path according
+# to the aix ld manual.
+# Store the results from the different compilers for each TAGNAME.
+# Allow to override them for all tags through lt_cv_aix_libpath.
+m4_defun([_LT_SYS_MODULE_PATH_AIX],
+[m4_require([_LT_DECL_SED])dnl
+if test set = "${lt_cv_aix_libpath+set}"; then
+  aix_libpath=$lt_cv_aix_libpath
+else
+  AC_CACHE_VAL([_LT_TAGVAR([lt_cv_aix_libpath_], [$1])],
+  [AC_LINK_IFELSE([AC_LANG_PROGRAM],[
+  lt_aix_libpath_sed='[
+      /Import File Strings/,/^$/ {
+         /^0/ {
+             s/^0  *\([^ ]*\) *$/\1/
+             p
+         }
+      }]'
+  _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=`dump -H conftest$ac_exeext 
2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+  # Check for a 64-bit object if we didn't find anything.
+  if test -z "$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])"; then
+    _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=`dump -HX64 conftest$ac_exeext 
2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+  fi],[])
+  if test -z "$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])"; then
+    _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=/usr/lib:/lib
+  fi
+  ])
+  aix_libpath=$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])
+fi
+])# _LT_SYS_MODULE_PATH_AIX
+
+
+# _LT_SHELL_INIT(ARG)
+# -------------------
+m4_define([_LT_SHELL_INIT],
+[m4_divert_text([M4SH-INIT], [$1
+])])# _LT_SHELL_INIT
+
+
+
+# _LT_PROG_ECHO_BACKSLASH
+# -----------------------
+# Find how we can fake an echo command that does not interpret backslash.
+# In particular, with Autoconf 2.60 or later we add some code to the start
+# of the generated configure script that will find a shell with a builtin
+# printf (that we can use as an echo command).
+m4_defun([_LT_PROG_ECHO_BACKSLASH],
+[ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO
+ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO
+
+AC_MSG_CHECKING([how to print strings])
+# Test print first, because it will be a builtin if present.
+if test "X`( print -r -- -n ) 2>/dev/null`" = X-n && \
+   test "X`print -r -- $ECHO 2>/dev/null`" = "X$ECHO"; then
+  ECHO='print -r --'
+elif test "X`printf %s $ECHO 2>/dev/null`" = "X$ECHO"; then
+  ECHO='printf %s\n'
+else
+  # Use this function as a fallback that always works.
+  func_fallback_echo ()
+  {
+    eval 'cat <<_LTECHO_EOF
+$[]1
+_LTECHO_EOF'
+  }
+  ECHO='func_fallback_echo'
+fi
+
+# func_echo_all arg...
+# Invoke $ECHO with all args, space-separated.
+func_echo_all ()
+{
+    $ECHO "$*"
+}
+
+case $ECHO in
+  printf*) AC_MSG_RESULT([printf]) ;;
+  print*) AC_MSG_RESULT([print -r]) ;;
+  *) AC_MSG_RESULT([cat]) ;;
+esac
+
+m4_ifdef([_AS_DETECT_SUGGESTED],
+[_AS_DETECT_SUGGESTED([
+  test -n "${ZSH_VERSION+set}${BASH_VERSION+set}" || (
+    
ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+    ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO
+    ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO
+    PATH=/empty FPATH=/empty; export PATH FPATH
+    test "X`printf %s $ECHO`" = "X$ECHO" \
+      || test "X`print -r -- $ECHO`" = "X$ECHO" )])])
+
+_LT_DECL([], [SHELL], [1], [Shell to use when invoking shell scripts])
+_LT_DECL([], [ECHO], [1], [An echo program that protects backslashes])
+])# _LT_PROG_ECHO_BACKSLASH
+
+
+# _LT_WITH_SYSROOT
+# ----------------
+AC_DEFUN([_LT_WITH_SYSROOT],
+[AC_MSG_CHECKING([for sysroot])
+AC_ARG_WITH([sysroot],
+[AS_HELP_STRING([--with-sysroot@<:@=DIR@:>@],
+  [Search for dependent libraries within DIR (or the compiler's sysroot
+   if not specified).])],
+[], [with_sysroot=no])
+
+dnl lt_sysroot will always be passed unquoted.  We quote it here
+dnl in case the user passed a directory name.
+lt_sysroot=
+case $with_sysroot in #(
+ yes)
+   if test yes = "$GCC"; then
+     lt_sysroot=`$CC --print-sysroot 2>/dev/null`
+   fi
+   ;; #(
+ /*)
+   lt_sysroot=`echo "$with_sysroot" | sed -e "$sed_quote_subst"`
+   ;; #(
+ no|'')
+   ;; #(
+ *)
+   AC_MSG_RESULT([$with_sysroot])
+   AC_MSG_ERROR([The sysroot must be an absolute path.])
+   ;;
+esac
+
+ AC_MSG_RESULT([${lt_sysroot:-no}])
+_LT_DECL([], [lt_sysroot], [0], [The root where to search for ]dnl
+[dependent libraries, and where our libraries should be installed.])])
+
+# _LT_ENABLE_LOCK
+# ---------------
+m4_defun([_LT_ENABLE_LOCK],
+[AC_ARG_ENABLE([libtool-lock],
+  [AS_HELP_STRING([--disable-libtool-lock],
+    [avoid locking (might break parallel builds)])])
+test no = "$enable_libtool_lock" || enable_libtool_lock=yes
+
+# Some flags need to be propagated to the compiler or linker for good
+# libtool support.
+case $host in
+ia64-*-hpux*)
+  # Find out what ABI is being produced by ac_compile, and set mode
+  # options accordingly.
+  echo 'int i;' > conftest.$ac_ext
+  if AC_TRY_EVAL(ac_compile); then
+    case `/usr/bin/file conftest.$ac_objext` in
+      *ELF-32*)
+       HPUX_IA64_MODE=32
+       ;;
+      *ELF-64*)
+       HPUX_IA64_MODE=64
+       ;;
+    esac
+  fi
+  rm -rf conftest*
+  ;;
+*-*-irix6*)
+  # Find out what ABI is being produced by ac_compile, and set linker
+  # options accordingly.
+  echo '[#]line '$LINENO' "configure"' > conftest.$ac_ext
+  if AC_TRY_EVAL(ac_compile); then
+    if test yes = "$lt_cv_prog_gnu_ld"; then
+      case `/usr/bin/file conftest.$ac_objext` in
+       *32-bit*)
+         LD="${LD-ld} -melf32bsmip"
+         ;;
+       *N32*)
+         LD="${LD-ld} -melf32bmipn32"
+         ;;
+       *64-bit*)
+         LD="${LD-ld} -melf64bmip"
+       ;;
+      esac
+    else
+      case `/usr/bin/file conftest.$ac_objext` in
+       *32-bit*)
+         LD="${LD-ld} -32"
+         ;;
+       *N32*)
+         LD="${LD-ld} -n32"
+         ;;
+       *64-bit*)
+         LD="${LD-ld} -64"
+         ;;
+      esac
+    fi
+  fi
+  rm -rf conftest*
+  ;;
+
+mips64*-*linux*)
+  # Find out what ABI is being produced by ac_compile, and set linker
+  # options accordingly.
+  echo '[#]line '$LINENO' "configure"' > conftest.$ac_ext
+  if AC_TRY_EVAL(ac_compile); then
+    emul=elf
+    case `/usr/bin/file conftest.$ac_objext` in
+      *32-bit*)
+       emul="${emul}32"
+       ;;
+      *64-bit*)
+       emul="${emul}64"
+       ;;
+    esac
+    case `/usr/bin/file conftest.$ac_objext` in
+      *MSB*)
+       emul="${emul}btsmip"
+       ;;
+      *LSB*)
+       emul="${emul}ltsmip"
+       ;;
+    esac
+    case `/usr/bin/file conftest.$ac_objext` in
+      *N32*)
+       emul="${emul}n32"
+       ;;
+    esac
+    LD="${LD-ld} -m $emul"
+  fi
+  rm -rf conftest*
+  ;;
+
+x86_64-*kfreebsd*-gnu|x86_64-*linux*|powerpc*-*linux*| \
+s390*-*linux*|s390*-*tpf*|sparc*-*linux*)
+  # Find out what ABI is being produced by ac_compile, and set linker
+  # options accordingly.  Note that the listed cases only cover the
+  # situations where additional linker options are needed (such as when
+  # doing 32-bit compilation for a host where ld defaults to 64-bit, or
+  # vice versa); the common cases where no linker options are needed do
+  # not appear in the list.
+  echo 'int i;' > conftest.$ac_ext
+  if AC_TRY_EVAL(ac_compile); then
+    case `/usr/bin/file conftest.o` in
+      *32-bit*)
+       case $host in
+         x86_64-*kfreebsd*-gnu)
+           LD="${LD-ld} -m elf_i386_fbsd"
+           ;;
+         x86_64-*linux*)
+           case `/usr/bin/file conftest.o` in
+             *x86-64*)
+               LD="${LD-ld} -m elf32_x86_64"
+               ;;
+             *)
+               LD="${LD-ld} -m elf_i386"
+               ;;
+           esac
+           ;;
+         powerpc64le-*linux*)
+           LD="${LD-ld} -m elf32lppclinux"
+           ;;
+         powerpc64-*linux*)
+           LD="${LD-ld} -m elf32ppclinux"
+           ;;
+         s390x-*linux*)
+           LD="${LD-ld} -m elf_s390"
+           ;;
+         sparc64-*linux*)
+           LD="${LD-ld} -m elf32_sparc"
+           ;;
+       esac
+       ;;
+      *64-bit*)
+       case $host in
+         x86_64-*kfreebsd*-gnu)
+           LD="${LD-ld} -m elf_x86_64_fbsd"
+           ;;
+         x86_64-*linux*)
+           LD="${LD-ld} -m elf_x86_64"
+           ;;
+         powerpcle-*linux*)
+           LD="${LD-ld} -m elf64lppc"
+           ;;
+         powerpc-*linux*)
+           LD="${LD-ld} -m elf64ppc"
+           ;;
+         s390*-*linux*|s390*-*tpf*)
+           LD="${LD-ld} -m elf64_s390"
+           ;;
+         sparc*-*linux*)
+           LD="${LD-ld} -m elf64_sparc"
+           ;;
+       esac
+       ;;
+    esac
+  fi
+  rm -rf conftest*
+  ;;
+
+*-*-sco3.2v5*)
+  # On SCO OpenServer 5, we need -belf to get full-featured binaries.
+  SAVE_CFLAGS=$CFLAGS
+  CFLAGS="$CFLAGS -belf"
+  AC_CACHE_CHECK([whether the C compiler needs -belf], lt_cv_cc_needs_belf,
+    [AC_LANG_PUSH(C)
+     
AC_LINK_IFELSE([AC_LANG_PROGRAM([[]],[[]])],[lt_cv_cc_needs_belf=yes],[lt_cv_cc_needs_belf=no])
+     AC_LANG_POP])
+  if test yes != "$lt_cv_cc_needs_belf"; then
+    # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf
+    CFLAGS=$SAVE_CFLAGS
+  fi
+  ;;
+*-*solaris*)
+  # Find out what ABI is being produced by ac_compile, and set linker
+  # options accordingly.
+  echo 'int i;' > conftest.$ac_ext
+  if AC_TRY_EVAL(ac_compile); then
+    case `/usr/bin/file conftest.o` in
+    *64-bit*)
+      case $lt_cv_prog_gnu_ld in
+      yes*)
+        case $host in
+        i?86-*-solaris*|x86_64-*-solaris*)
+          LD="${LD-ld} -m elf_x86_64"
+          ;;
+        sparc*-*-solaris*)
+          LD="${LD-ld} -m elf64_sparc"
+          ;;
+        esac
+        # GNU ld 2.21 introduced _sol2 emulations.  Use them if available.
+        if ${LD-ld} -V | grep _sol2 >/dev/null 2>&1; then
+          LD=${LD-ld}_sol2
+        fi
+        ;;
+      *)
+       if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then
+         LD="${LD-ld} -64"
+       fi
+       ;;
+      esac
+      ;;
+    esac
+  fi
+  rm -rf conftest*
+  ;;
+esac
+
+need_locks=$enable_libtool_lock
+])# _LT_ENABLE_LOCK
+
+
+# _LT_PROG_AR
+# -----------
+m4_defun([_LT_PROG_AR],
+[AC_CHECK_TOOLS(AR, [ar], false)
+: ${AR=ar}
+: ${AR_FLAGS=cru}
+_LT_DECL([], [AR], [1], [The archiver])
+_LT_DECL([], [AR_FLAGS], [1], [Flags to create an archive])
+
+AC_CACHE_CHECK([for archiver @FILE support], [lt_cv_ar_at_file],
+  [lt_cv_ar_at_file=no
+   AC_COMPILE_IFELSE([AC_LANG_PROGRAM],
+     [echo conftest.$ac_objext > conftest.lst
+      lt_ar_try='$AR $AR_FLAGS libconftest.a @conftest.lst >&AS_MESSAGE_LOG_FD'
+      AC_TRY_EVAL([lt_ar_try])
+      if test 0 -eq "$ac_status"; then
+       # Ensure the archiver fails upon bogus file names.
+       rm -f conftest.$ac_objext libconftest.a
+       AC_TRY_EVAL([lt_ar_try])
+       if test 0 -ne "$ac_status"; then
+          lt_cv_ar_at_file=@
+        fi
+      fi
+      rm -f conftest.* libconftest.a
+     ])
+  ])
+
+if test no = "$lt_cv_ar_at_file"; then
+  archiver_list_spec=
+else
+  archiver_list_spec=$lt_cv_ar_at_file
+fi
+_LT_DECL([], [archiver_list_spec], [1],
+  [How to feed a file listing to the archiver])
+])# _LT_PROG_AR
+
+
+# _LT_CMD_OLD_ARCHIVE
+# -------------------
+m4_defun([_LT_CMD_OLD_ARCHIVE],
+[_LT_PROG_AR
+
+AC_CHECK_TOOL(STRIP, strip, :)
+test -z "$STRIP" && STRIP=:
+_LT_DECL([], [STRIP], [1], [A symbol stripping program])
+
+AC_CHECK_TOOL(RANLIB, ranlib, :)
+test -z "$RANLIB" && RANLIB=:
+_LT_DECL([], [RANLIB], [1],
+    [Commands used to install an old-style archive])
+
+# Determine commands to create old-style static archives.
+old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs'
+old_postinstall_cmds='chmod 644 $oldlib'
+old_postuninstall_cmds=
+
+if test -n "$RANLIB"; then
+  case $host_os in
+  bitrig* | openbsd*)
+    old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$tool_oldlib"
+    ;;
+  *)
+    old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$tool_oldlib"
+    ;;
+  esac
+  old_archive_cmds="$old_archive_cmds~\$RANLIB \$tool_oldlib"
+fi
+
+case $host_os in
+  darwin*)
+    lock_old_archive_extraction=yes ;;
+  *)
+    lock_old_archive_extraction=no ;;
+esac
+_LT_DECL([], [old_postinstall_cmds], [2])
+_LT_DECL([], [old_postuninstall_cmds], [2])
+_LT_TAGDECL([], [old_archive_cmds], [2],
+    [Commands used to build an old-style archive])
+_LT_DECL([], [lock_old_archive_extraction], [0],
+    [Whether to use a lock for old archive extraction])
+])# _LT_CMD_OLD_ARCHIVE
+
+
+# _LT_COMPILER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS,
+#              [OUTPUT-FILE], [ACTION-SUCCESS], [ACTION-FAILURE])
+# ----------------------------------------------------------------
+# Check whether the given compiler option works
+AC_DEFUN([_LT_COMPILER_OPTION],
+[m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_DECL_SED])dnl
+AC_CACHE_CHECK([$1], [$2],
+  [$2=no
+   m4_if([$4], , [ac_outfile=conftest.$ac_objext], [ac_outfile=$4])
+   echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+   lt_compiler_flag="$3"  ## exclude from sc_useless_quotes_in_assignment
+   # Insert the option either (1) after the last *FLAGS variable, or
+   # (2) before a word containing "conftest.", or (3) at the end.
+   # Note that $ac_compile itself does not contain backslashes and begins
+   # with a dollar sign (not a hyphen), so the echo should work correctly.
+   # The option is referenced via a variable to avoid confusing sed.
+   lt_compile=`echo "$ac_compile" | $SED \
+   -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+   -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \
+   -e 's:$: $lt_compiler_flag:'`
+   (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&AS_MESSAGE_LOG_FD)
+   (eval "$lt_compile" 2>conftest.err)
+   ac_status=$?
+   cat conftest.err >&AS_MESSAGE_LOG_FD
+   echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD
+   if (exit $ac_status) && test -s "$ac_outfile"; then
+     # The compiler can only warn and ignore the option if not recognized
+     # So say no if there are warnings other than the usual output.
+     $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp
+     $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+     if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; 
then
+       $2=yes
+     fi
+   fi
+   $RM conftest*
+])
+
+if test yes = "[$]$2"; then
+    m4_if([$5], , :, [$5])
+else
+    m4_if([$6], , :, [$6])
+fi
+])# _LT_COMPILER_OPTION
+
+# Old name:
+AU_ALIAS([AC_LIBTOOL_COMPILER_OPTION], [_LT_COMPILER_OPTION])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_COMPILER_OPTION], [])
+
+
+# _LT_LINKER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS,
+#                  [ACTION-SUCCESS], [ACTION-FAILURE])
+# ----------------------------------------------------
+# Check whether the given linker option works
+AC_DEFUN([_LT_LINKER_OPTION],
+[m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_DECL_SED])dnl
+AC_CACHE_CHECK([$1], [$2],
+  [$2=no
+   save_LDFLAGS=$LDFLAGS
+   LDFLAGS="$LDFLAGS $3"
+   echo "$lt_simple_link_test_code" > conftest.$ac_ext
+   if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then
+     # The linker can only warn and ignore the option if not recognized
+     # So say no if there are warnings
+     if test -s conftest.err; then
+       # Append any errors to the config.log.
+       cat conftest.err 1>&AS_MESSAGE_LOG_FD
+       $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp
+       $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+       if diff conftest.exp conftest.er2 >/dev/null; then
+         $2=yes
+       fi
+     else
+       $2=yes
+     fi
+   fi
+   $RM -r conftest*
+   LDFLAGS=$save_LDFLAGS
+])
+
+if test yes = "[$]$2"; then
+    m4_if([$4], , :, [$4])
+else
+    m4_if([$5], , :, [$5])
+fi
+])# _LT_LINKER_OPTION
+
+# Old name:
+AU_ALIAS([AC_LIBTOOL_LINKER_OPTION], [_LT_LINKER_OPTION])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_LINKER_OPTION], [])
+
+
+# LT_CMD_MAX_LEN
+#---------------
+AC_DEFUN([LT_CMD_MAX_LEN],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+# find the maximum length of command line arguments
+AC_MSG_CHECKING([the maximum length of command line arguments])
+AC_CACHE_VAL([lt_cv_sys_max_cmd_len], [dnl
+  i=0
+  teststring=ABCD
+
+  case $build_os in
+  msdosdjgpp*)
+    # On DJGPP, this test can blow up pretty badly due to problems in libc
+    # (any single argument exceeding 2000 bytes causes a buffer overrun
+    # during glob expansion).  Even if it were fixed, the result of this
+    # check would be larger than it should be.
+    lt_cv_sys_max_cmd_len=12288;    # 12K is about right
+    ;;
+
+  gnu*)
+    # Under GNU Hurd, this test is not required because there is
+    # no limit to the length of command line arguments.
+    # Libtool will interpret -1 as no limit whatsoever
+    lt_cv_sys_max_cmd_len=-1;
+    ;;
+
+  cygwin* | mingw* | cegcc*)
+    # On Win9x/ME, this test blows up -- it succeeds, but takes
+    # about 5 minutes as the teststring grows exponentially.
+    # Worse, since 9x/ME are not pre-emptively multitasking,
+    # you end up with a "frozen" computer, even though with patience
+    # the test eventually succeeds (with a max line length of 256k).
+    # Instead, let's just punt: use the minimum linelength reported by
+    # all of the supported platforms: 8192 (on NT/2K/XP).
+    lt_cv_sys_max_cmd_len=8192;
+    ;;
+
+  mint*)
+    # On MiNT this can take a long time and run out of memory.
+    lt_cv_sys_max_cmd_len=8192;
+    ;;
+
+  amigaos*)
+    # On AmigaOS with pdksh, this test takes hours, literally.
+    # So we just punt and use a minimum line length of 8192.
+    lt_cv_sys_max_cmd_len=8192;
+    ;;
+
+  bitrig* | darwin* | dragonfly* | freebsd* | netbsd* | openbsd*)
+    # This has been around since 386BSD, at least.  Likely further.
+    if test -x /sbin/sysctl; then
+      lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax`
+    elif test -x /usr/sbin/sysctl; then
+      lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax`
+    else
+      lt_cv_sys_max_cmd_len=65536      # usable default for all BSDs
+    fi
+    # And add a safety zone
+    lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4`
+    lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3`
+    ;;
+
+  interix*)
+    # We know the value 262144 and hardcode it with a safety zone (like BSD)
+    lt_cv_sys_max_cmd_len=196608
+    ;;
+
+  os2*)
+    # The test takes a long time on OS/2.
+    lt_cv_sys_max_cmd_len=8192
+    ;;
+
+  osf*)
+    # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure
+    # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not
+    # nice to cause kernel panics so lets avoid the loop below.
+    # First set a reasonable default.
+    lt_cv_sys_max_cmd_len=16384
+    #
+    if test -x /sbin/sysconfig; then
+      case `/sbin/sysconfig -q proc exec_disable_arg_limit` in
+        *1*) lt_cv_sys_max_cmd_len=-1 ;;
+      esac
+    fi
+    ;;
+  sco3.2v5*)
+    lt_cv_sys_max_cmd_len=102400
+    ;;
+  sysv5* | sco5v6* | sysv4.2uw2*)
+    kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null`
+    if test -n "$kargmax"; then
+      lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[[        ]]//'`
+    else
+      lt_cv_sys_max_cmd_len=32768
+    fi
+    ;;
+  *)
+    lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null`
+    if test -n "$lt_cv_sys_max_cmd_len" && \
+       test undefined != "$lt_cv_sys_max_cmd_len"; then
+      lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4`
+      lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3`
+    else
+      # Make teststring a little bigger before we do anything with it.
+      # a 1K string should be a reasonable start.
+      for i in 1 2 3 4 5 6 7 8; do
+        teststring=$teststring$teststring
+      done
+      SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}}
+      # If test is not a shell built-in, we'll probably end up computing a
+      # maximum length that is only half of the actual maximum length, but
+      # we can't tell.
+      while { test X`env echo "$teststring$teststring" 2>/dev/null` \
+                = "X$teststring$teststring"; } >/dev/null 2>&1 &&
+             test 17 != "$i" # 1/2 MB should be enough
+      do
+        i=`expr $i + 1`
+        teststring=$teststring$teststring
+      done
+      # Only check the string length outside the loop.
+      lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1`
+      teststring=
+      # Add a significant safety factor because C++ compilers can tack on
+      # massive amounts of additional arguments before passing them to the
+      # linker.  It appears as though 1/2 is a usable value.
+      lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2`
+    fi
+    ;;
+  esac
+])
+if test -n "$lt_cv_sys_max_cmd_len"; then
+  AC_MSG_RESULT($lt_cv_sys_max_cmd_len)
+else
+  AC_MSG_RESULT(none)
+fi
+max_cmd_len=$lt_cv_sys_max_cmd_len
+_LT_DECL([], [max_cmd_len], [0],
+    [What is the maximum length of a command?])
+])# LT_CMD_MAX_LEN
+
+# Old name:
+AU_ALIAS([AC_LIBTOOL_SYS_MAX_CMD_LEN], [LT_CMD_MAX_LEN])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_SYS_MAX_CMD_LEN], [])
+
+
+# _LT_HEADER_DLFCN
+# ----------------
+m4_defun([_LT_HEADER_DLFCN],
+[AC_CHECK_HEADERS([dlfcn.h], [], [], [AC_INCLUDES_DEFAULT])dnl
+])# _LT_HEADER_DLFCN
+
+
+# _LT_TRY_DLOPEN_SELF (ACTION-IF-TRUE, ACTION-IF-TRUE-W-USCORE,
+#                      ACTION-IF-FALSE, ACTION-IF-CROSS-COMPILING)
+# ----------------------------------------------------------------
+m4_defun([_LT_TRY_DLOPEN_SELF],
+[m4_require([_LT_HEADER_DLFCN])dnl
+if test yes = "$cross_compiling"; then :
+  [$4]
+else
+  lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
+  lt_status=$lt_dlunknown
+  cat > conftest.$ac_ext <<_LT_EOF
+[#line $LINENO "configure"
+#include "confdefs.h"
+
+#if HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#include <stdio.h>
+
+#ifdef RTLD_GLOBAL
+#  define LT_DLGLOBAL          RTLD_GLOBAL
+#else
+#  ifdef DL_GLOBAL
+#    define LT_DLGLOBAL                DL_GLOBAL
+#  else
+#    define LT_DLGLOBAL                0
+#  endif
+#endif
+
+/* We may have to define LT_DLLAZY_OR_NOW in the command line if we
+   find out it does not work in some platform. */
+#ifndef LT_DLLAZY_OR_NOW
+#  ifdef RTLD_LAZY
+#    define LT_DLLAZY_OR_NOW           RTLD_LAZY
+#  else
+#    ifdef DL_LAZY
+#      define LT_DLLAZY_OR_NOW         DL_LAZY
+#    else
+#      ifdef RTLD_NOW
+#        define LT_DLLAZY_OR_NOW       RTLD_NOW
+#      else
+#        ifdef DL_NOW
+#          define LT_DLLAZY_OR_NOW     DL_NOW
+#        else
+#          define LT_DLLAZY_OR_NOW     0
+#        endif
+#      endif
+#    endif
+#  endif
+#endif
+
+/* When -fvisibility=hidden is used, assume the code has been annotated
+   correspondingly for the symbols needed.  */
+#if defined __GNUC__ && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || 
(__GNUC__ > 3))
+int fnord () __attribute__((visibility("default")));
+#endif
+
+int fnord () { return 42; }
+int main ()
+{
+  void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW);
+  int status = $lt_dlunknown;
+
+  if (self)
+    {
+      if (dlsym (self,"fnord"))       status = $lt_dlno_uscore;
+      else
+        {
+         if (dlsym( self,"_fnord"))  status = $lt_dlneed_uscore;
+          else puts (dlerror ());
+       }
+      /* dlclose (self); */
+    }
+  else
+    puts (dlerror ());
+
+  return status;
+}]
+_LT_EOF
+  if AC_TRY_EVAL(ac_link) && test -s "conftest$ac_exeext" 2>/dev/null; then
+    (./conftest; exit; ) >&AS_MESSAGE_LOG_FD 2>/dev/null
+    lt_status=$?
+    case x$lt_status in
+      x$lt_dlno_uscore) $1 ;;
+      x$lt_dlneed_uscore) $2 ;;
+      x$lt_dlunknown|x*) $3 ;;
+    esac
+  else :
+    # compilation failed
+    $3
+  fi
+fi
+rm -fr conftest*
+])# _LT_TRY_DLOPEN_SELF
+
+
+# LT_SYS_DLOPEN_SELF
+# ------------------
+AC_DEFUN([LT_SYS_DLOPEN_SELF],
+[m4_require([_LT_HEADER_DLFCN])dnl
+if test yes != "$enable_dlopen"; then
+  enable_dlopen=unknown
+  enable_dlopen_self=unknown
+  enable_dlopen_self_static=unknown
+else
+  lt_cv_dlopen=no
+  lt_cv_dlopen_libs=
+
+  case $host_os in
+  beos*)
+    lt_cv_dlopen=load_add_on
+    lt_cv_dlopen_libs=
+    lt_cv_dlopen_self=yes
+    ;;
+
+  mingw* | pw32* | cegcc*)
+    lt_cv_dlopen=LoadLibrary
+    lt_cv_dlopen_libs=
+    ;;
+
+  cygwin*)
+    lt_cv_dlopen=dlopen
+    lt_cv_dlopen_libs=
+    ;;
+
+  darwin*)
+    # if libdl is installed we need to link against it
+    AC_CHECK_LIB([dl], [dlopen],
+               [lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl],[
+    lt_cv_dlopen=dyld
+    lt_cv_dlopen_libs=
+    lt_cv_dlopen_self=yes
+    ])
+    ;;
+
+  tpf*)
+    # Don't try to run any link tests for TPF.  We know it's impossible
+    # because TPF is a cross-compiler, and we know how we open DSOs.
+    lt_cv_dlopen=dlopen
+    lt_cv_dlopen_libs=
+    lt_cv_dlopen_self=no
+    ;;
+
+  *)
+    AC_CHECK_FUNC([shl_load],
+         [lt_cv_dlopen=shl_load],
+      [AC_CHECK_LIB([dld], [shl_load],
+           [lt_cv_dlopen=shl_load lt_cv_dlopen_libs=-ldld],
+       [AC_CHECK_FUNC([dlopen],
+             [lt_cv_dlopen=dlopen],
+         [AC_CHECK_LIB([dl], [dlopen],
+               [lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl],
+           [AC_CHECK_LIB([svld], [dlopen],
+                 [lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-lsvld],
+             [AC_CHECK_LIB([dld], [dld_link],
+                   [lt_cv_dlopen=dld_link lt_cv_dlopen_libs=-ldld])
+             ])
+           ])
+         ])
+       ])
+      ])
+    ;;
+  esac
+
+  if test no = "$lt_cv_dlopen"; then
+    enable_dlopen=no
+  else
+    enable_dlopen=yes
+  fi
+
+  case $lt_cv_dlopen in
+  dlopen)
+    save_CPPFLAGS=$CPPFLAGS
+    test yes = "$ac_cv_header_dlfcn_h" && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H"
+
+    save_LDFLAGS=$LDFLAGS
+    wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS 
$export_dynamic_flag_spec\"
+
+    save_LIBS=$LIBS
+    LIBS="$lt_cv_dlopen_libs $LIBS"
+
+    AC_CACHE_CHECK([whether a program can dlopen itself],
+         lt_cv_dlopen_self, [dnl
+         _LT_TRY_DLOPEN_SELF(
+           lt_cv_dlopen_self=yes, lt_cv_dlopen_self=yes,
+           lt_cv_dlopen_self=no, lt_cv_dlopen_self=cross)
+    ])
+
+    if test yes = "$lt_cv_dlopen_self"; then
+      wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS 
$lt_prog_compiler_static\"
+      AC_CACHE_CHECK([whether a statically linked program can dlopen itself],
+         lt_cv_dlopen_self_static, [dnl
+         _LT_TRY_DLOPEN_SELF(
+           lt_cv_dlopen_self_static=yes, lt_cv_dlopen_self_static=yes,
+           lt_cv_dlopen_self_static=no,  lt_cv_dlopen_self_static=cross)
+      ])
+    fi
+
+    CPPFLAGS=$save_CPPFLAGS
+    LDFLAGS=$save_LDFLAGS
+    LIBS=$save_LIBS
+    ;;
+  esac
+
+  case $lt_cv_dlopen_self in
+  yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;;
+  *) enable_dlopen_self=unknown ;;
+  esac
+
+  case $lt_cv_dlopen_self_static in
+  yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;;
+  *) enable_dlopen_self_static=unknown ;;
+  esac
+fi
+_LT_DECL([dlopen_support], [enable_dlopen], [0],
+        [Whether dlopen is supported])
+_LT_DECL([dlopen_self], [enable_dlopen_self], [0],
+        [Whether dlopen of programs is supported])
+_LT_DECL([dlopen_self_static], [enable_dlopen_self_static], [0],
+        [Whether dlopen of statically linked programs is supported])
+])# LT_SYS_DLOPEN_SELF
+
+# Old name:
+AU_ALIAS([AC_LIBTOOL_DLOPEN_SELF], [LT_SYS_DLOPEN_SELF])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_DLOPEN_SELF], [])
+
+
+# _LT_COMPILER_C_O([TAGNAME])
+# ---------------------------
+# Check to see if options -c and -o are simultaneously supported by compiler.
+# This macro does not hard code the compiler like AC_PROG_CC_C_O.
+m4_defun([_LT_COMPILER_C_O],
+[m4_require([_LT_DECL_SED])dnl
+m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_TAG_COMPILER])dnl
+AC_CACHE_CHECK([if $compiler supports -c -o file.$ac_objext],
+  [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)],
+  [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=no
+   $RM -r conftest 2>/dev/null
+   mkdir conftest
+   cd conftest
+   mkdir out
+   echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+   lt_compiler_flag="-o out/conftest2.$ac_objext"
+   # Insert the option either (1) after the last *FLAGS variable, or
+   # (2) before a word containing "conftest.", or (3) at the end.
+   # Note that $ac_compile itself does not contain backslashes and begins
+   # with a dollar sign (not a hyphen), so the echo should work correctly.
+   lt_compile=`echo "$ac_compile" | $SED \
+   -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+   -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \
+   -e 's:$: $lt_compiler_flag:'`
+   (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&AS_MESSAGE_LOG_FD)
+   (eval "$lt_compile" 2>out/conftest.err)
+   ac_status=$?
+   cat out/conftest.err >&AS_MESSAGE_LOG_FD
+   echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD
+   if (exit $ac_status) && test -s out/conftest2.$ac_objext
+   then
+     # The compiler can only warn and ignore the option if not recognized
+     # So say no if there are warnings
+     $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp
+     $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2
+     if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 
>/dev/null; then
+       _LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes
+     fi
+   fi
+   chmod u+w . 2>&AS_MESSAGE_LOG_FD
+   $RM conftest*
+   # SGI C++ compiler will create directory out/ii_files/ for
+   # template instantiation
+   test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files
+   $RM out/* && rmdir out
+   cd ..
+   $RM -r conftest
+   $RM conftest*
+])
+_LT_TAGDECL([compiler_c_o], [lt_cv_prog_compiler_c_o], [1],
+       [Does compiler simultaneously support -c and -o options?])
+])# _LT_COMPILER_C_O
+
+
+# _LT_COMPILER_FILE_LOCKS([TAGNAME])
+# ----------------------------------
+# Check to see if we can do hard links to lock some files if needed
+m4_defun([_LT_COMPILER_FILE_LOCKS],
+[m4_require([_LT_ENABLE_LOCK])dnl
+m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+_LT_COMPILER_C_O([$1])
+
+hard_links=nottested
+if test no = "$_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)" && test no != 
"$need_locks"; then
+  # do not overwrite the value of need_locks provided by the user
+  AC_MSG_CHECKING([if we can lock with hard links])
+  hard_links=yes
+  $RM conftest*
+  ln conftest.a conftest.b 2>/dev/null && hard_links=no
+  touch conftest.a
+  ln conftest.a conftest.b 2>&5 || hard_links=no
+  ln conftest.a conftest.b 2>/dev/null && hard_links=no
+  AC_MSG_RESULT([$hard_links])
+  if test no = "$hard_links"; then
+    AC_MSG_WARN(['$CC' does not support '-c -o', so 'make -j' may be unsafe])
+    need_locks=warn
+  fi
+else
+  need_locks=no
+fi
+_LT_DECL([], [need_locks], [1], [Must we lock files when doing compilation?])
+])# _LT_COMPILER_FILE_LOCKS
+
+
+# _LT_CHECK_OBJDIR
+# ----------------
+m4_defun([_LT_CHECK_OBJDIR],
+[AC_CACHE_CHECK([for objdir], [lt_cv_objdir],
+[rm -f .libs 2>/dev/null
+mkdir .libs 2>/dev/null
+if test -d .libs; then
+  lt_cv_objdir=.libs
+else
+  # MS-DOS does not allow filenames that begin with a dot.
+  lt_cv_objdir=_libs
+fi
+rmdir .libs 2>/dev/null])
+objdir=$lt_cv_objdir
+_LT_DECL([], [objdir], [0],
+         [The name of the directory that contains temporary libtool files])dnl
+m4_pattern_allow([LT_OBJDIR])dnl
+AC_DEFINE_UNQUOTED([LT_OBJDIR], "$lt_cv_objdir/",
+  [Define to the sub-directory where libtool stores uninstalled libraries.])
+])# _LT_CHECK_OBJDIR
+
+
+# _LT_LINKER_HARDCODE_LIBPATH([TAGNAME])
+# --------------------------------------
+# Check hardcoding attributes.
+m4_defun([_LT_LINKER_HARDCODE_LIBPATH],
+[AC_MSG_CHECKING([how to hardcode library paths into programs])
+_LT_TAGVAR(hardcode_action, $1)=
+if test -n "$_LT_TAGVAR(hardcode_libdir_flag_spec, $1)" ||
+   test -n "$_LT_TAGVAR(runpath_var, $1)" ||
+   test yes = "$_LT_TAGVAR(hardcode_automatic, $1)"; then
+
+  # We can hardcode non-existent directories.
+  if test no != "$_LT_TAGVAR(hardcode_direct, $1)" &&
+     # If the only mechanism to avoid hardcoding is shlibpath_var, we
+     # have to relink, otherwise we might link with an installed library
+     # when we should be linking with a yet-to-be-installed one
+     ## test no != "$_LT_TAGVAR(hardcode_shlibpath_var, $1)" &&
+     test no != "$_LT_TAGVAR(hardcode_minus_L, $1)"; then
+    # Linking always hardcodes the temporary library directory.
+    _LT_TAGVAR(hardcode_action, $1)=relink
+  else
+    # We can link without hardcoding, and we can hardcode nonexisting dirs.
+    _LT_TAGVAR(hardcode_action, $1)=immediate
+  fi
+else
+  # We cannot hardcode anything, or else we can only hardcode existing
+  # directories.
+  _LT_TAGVAR(hardcode_action, $1)=unsupported
+fi
+AC_MSG_RESULT([$_LT_TAGVAR(hardcode_action, $1)])
+
+if test relink = "$_LT_TAGVAR(hardcode_action, $1)" ||
+   test yes = "$_LT_TAGVAR(inherit_rpath, $1)"; then
+  # Fast installation is not supported
+  enable_fast_install=no
+elif test yes = "$shlibpath_overrides_runpath" ||
+     test no = "$enable_shared"; then
+  # Fast installation is not necessary
+  enable_fast_install=needless
+fi
+_LT_TAGDECL([], [hardcode_action], [0],
+    [How to hardcode a shared library path into an executable])
+])# _LT_LINKER_HARDCODE_LIBPATH
+
+
+# _LT_CMD_STRIPLIB
+# ----------------
+m4_defun([_LT_CMD_STRIPLIB],
+[m4_require([_LT_DECL_EGREP])
+striplib=
+old_striplib=
+AC_MSG_CHECKING([whether stripping libraries is possible])
+if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then
+  test -z "$old_striplib" && old_striplib="$STRIP --strip-debug"
+  test -z "$striplib" && striplib="$STRIP --strip-unneeded"
+  AC_MSG_RESULT([yes])
+else
+# FIXME - insert some real tests, host_os isn't really good enough
+  case $host_os in
+  darwin*)
+    if test -n "$STRIP"; then
+      striplib="$STRIP -x"
+      old_striplib="$STRIP -S"
+      AC_MSG_RESULT([yes])
+    else
+      AC_MSG_RESULT([no])
+    fi
+    ;;
+  *)
+    AC_MSG_RESULT([no])
+    ;;
+  esac
+fi
+_LT_DECL([], [old_striplib], [1], [Commands to strip libraries])
+_LT_DECL([], [striplib], [1])
+])# _LT_CMD_STRIPLIB
+
+
+# _LT_PREPARE_MUNGE_PATH_LIST
+# ---------------------------
+# Make sure func_munge_path_list() is defined correctly.
+m4_defun([_LT_PREPARE_MUNGE_PATH_LIST],
+[[# func_munge_path_list VARIABLE PATH
+# -----------------------------------
+# VARIABLE is name of variable containing _space_ separated list of
+# directories to be munged by the contents of PATH, which is string
+# having a format:
+# "DIR[:DIR]:"
+#       string "DIR[ DIR]" will be prepended to VARIABLE
+# ":DIR[:DIR]"
+#       string "DIR[ DIR]" will be appended to VARIABLE
+# "DIRP[:DIRP]::[DIRA:]DIRA"
+#       string "DIRP[ DIRP]" will be prepended to VARIABLE and string
+#       "DIRA[ DIRA]" will be appended to VARIABLE
+# "DIR[:DIR]"
+#       VARIABLE will be replaced by "DIR[ DIR]"
+func_munge_path_list ()
+{
+    case address@hidden|@2 in
+    x)
+        ;;
+    *:)
+        eval @S|@1=\"`$ECHO @S|@2 | $SED 's/:/ /g'` address@hidden|@@S|@1\"
+        ;;
+    x:*)
+        eval @S|@1=\"address@hidden|@@S|@1 `$ECHO @S|@2 | $SED 's/:/ /g'`\"
+        ;;
+    *::*)
+        eval @S|@1=\"address@hidden|@@S|@1\ `$ECHO @S|@2 | $SED -e 's/.*:://' 
-e 's/:/ /g'`\"
+        eval @S|@1=\"`$ECHO @S|@2 | $SED -e 's/::.*//' -e 's/:/ /g'`\ 
address@hidden|@@S|@1\"
+        ;;
+    *)
+        eval @S|@1=\"`$ECHO @S|@2 | $SED 's/:/ /g'`\"
+        ;;
+    esac
+}
+]])# _LT_PREPARE_PATH_LIST
+
+
+# _LT_SYS_DYNAMIC_LINKER([TAG])
+# -----------------------------
+# PORTME Fill in your ld.so characteristics
+m4_defun([_LT_SYS_DYNAMIC_LINKER],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+m4_require([_LT_DECL_EGREP])dnl
+m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_DECL_OBJDUMP])dnl
+m4_require([_LT_DECL_SED])dnl
+m4_require([_LT_CHECK_SHELL_FEATURES])dnl
+m4_require([_LT_PREPARE_MUNGE_PATH_LIST])dnl
+AC_MSG_CHECKING([dynamic linker characteristics])
+m4_if([$1],
+       [], [
+if test yes = "$GCC"; then
+  case $host_os in
+    darwin*) lt_awk_arg='/^libraries:/,/LR/' ;;
+    *) lt_awk_arg='/^libraries:/' ;;
+  esac
+  case $host_os in
+    mingw* | cegcc*) lt_sed_strip_eq='s|=\([[A-Za-z]]:\)|\1|g' ;;
+    *) lt_sed_strip_eq='s|=/|/|g' ;;
+  esac
+  lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e 
"s/^libraries://" -e $lt_sed_strip_eq`
+  case $lt_search_path_spec in
+  *\;*)
+    # if the path contains ";" then we assume it to be the separator
+    # otherwise default to the standard path separator (i.e. ":") - it is
+    # assumed that no part of a normal pathname contains ";" but that should
+    # okay in the real world where ";" in dirpaths is itself problematic.
+    lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED 's/;/ /g'`
+    ;;
+  *)
+    lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED 
"s/$PATH_SEPARATOR/ /g"`
+    ;;
+  esac
+  # Ok, now we have the path, separated by spaces, we can step through it
+  # and add multilib dir if necessary...
+  lt_tmp_lt_search_path_spec=
+  lt_multi_os_dir=/`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 
2>/dev/null`
+  # ...but if some path component already ends with the multilib dir we assume
+  # that all is fine and trust -print-search-dirs as is (GCC 4.2? or newer).
+  case "$lt_multi_os_dir; $lt_search_path_spec " in
+  "/; "* | "/.; "* | "/./; "* | *"$lt_multi_os_dir "* | *"$lt_multi_os_dir/ "*)
+    lt_multi_os_dir=
+    ;;
+  esac
+  for lt_sys_path in $lt_search_path_spec; do
+    if test -d "$lt_sys_path$lt_multi_os_dir"; then
+      lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec 
$lt_sys_path$lt_multi_os_dir"
+    elif test -n "$lt_multi_os_dir"; then
+      test -d "$lt_sys_path" && \
+       lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path"
+    fi
+  done
+  lt_search_path_spec=`$ECHO "$lt_tmp_lt_search_path_spec" | awk '
+BEGIN {RS = " "; FS = "/|\n";} {
+  lt_foo = "";
+  lt_count = 0;
+  for (lt_i = NF; lt_i > 0; lt_i--) {
+    if ($lt_i != "" && $lt_i != ".") {
+      if ($lt_i == "..") {
+        lt_count++;
+      } else {
+        if (lt_count == 0) {
+          lt_foo = "/" $lt_i lt_foo;
+        } else {
+          lt_count--;
+        }
+      }
+    }
+  }
+  if (lt_foo != "") { lt_freq[[lt_foo]]++; }
+  if (lt_freq[[lt_foo]] == 1) { print lt_foo; }
+}'`
+  # AWK program above erroneously prepends '/' to C:/dos/paths
+  # for these hosts.
+  case $host_os in
+    mingw* | cegcc*) lt_search_path_spec=`$ECHO "$lt_search_path_spec" |\
+      $SED 's|/\([[A-Za-z]]:\)|\1|g'` ;;
+  esac
+  sys_lib_search_path_spec=`$ECHO "$lt_search_path_spec" | $lt_NL2SP`
+else
+  sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib"
+fi])
+library_names_spec=
+libname_spec='lib$name'
+soname_spec=
+shrext_cmds=.so
+postinstall_cmds=
+postuninstall_cmds=
+finish_cmds=
+finish_eval=
+shlibpath_var=
+shlibpath_overrides_runpath=unknown
+version_type=none
+dynamic_linker="$host_os ld.so"
+sys_lib_dlsearch_path_spec="/lib /usr/lib"
+need_lib_prefix=unknown
+hardcode_into_libs=no
+
+# when you set need_version to no, make sure it does not cause -set_version
+# flags to be left without arguments
+need_version=unknown
+
+AC_ARG_VAR([LT_SYS_LIBRARY_PATH],
+[User-defined run-time library search path.])
+
+case $host_os in
+aix3*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  library_names_spec='$libname$release$shared_ext$versuffix $libname.a'
+  shlibpath_var=LIBPATH
+
+  # AIX 3 has no versioning support, so we append a major version to the name.
+  soname_spec='$libname$release$shared_ext$major'
+  ;;
+
+aix[[4-9]]*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  hardcode_into_libs=yes
+  if test ia64 = "$host_cpu"; then
+    # AIX 5 supports IA64
+    library_names_spec='$libname$release$shared_ext$major 
$libname$release$shared_ext$versuffix $libname$shared_ext'
+    shlibpath_var=LD_LIBRARY_PATH
+  else
+    # With GCC up to 2.95.x, collect2 would create an import file
+    # for dependence libraries.  The import file would start with
+    # the line '#! .'.  This would cause the generated library to
+    # depend on '.', always an invalid library.  This was fixed in
+    # development snapshots of GCC prior to 3.0.
+    case $host_os in
+      aix4 | aix4.[[01]] | aix4.[[01]].*)
+      if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)'
+          echo ' yes '
+          echo '#endif'; } | $CC -E - | $GREP yes > /dev/null; then
+       :
+      else
+       can_build_shared=no
+      fi
+      ;;
+    esac
+    # Using Import Files as archive members, it is possible to support
+    # filename-based versioning of shared library archives on AIX. While
+    # this would work for both with and without runtime linking, it will
+    # prevent static linking of such archives. So we do filename-based
+    # shared library versioning with .so extension only, which is used
+    # when both runtime linking and shared linking is enabled.
+    # Unfortunately, runtime linking may impact performance, so we do
+    # not want this to be the default eventually. Also, we use the
+    # versioned .so libs for executables only if there is the -brtl
+    # linker flag in LDFLAGS as well, or --with-aix-soname=svr4 only.
+    # To allow for filename-based versioning support, we need to create
+    # libNAME.so.V as an archive file, containing:
+    # *) an Import File, referring to the versioned filename of the
+    #    archive as well as the shared archive member, telling the
+    #    bitwidth (32 or 64) of that shared object, and providing the
+    #    list of exported symbols of that shared object, eventually
+    #    decorated with the 'weak' keyword
+    # *) the shared object with the F_LOADONLY flag set, to really avoid
+    #    it being seen by the linker.
+    # At run time we better use the real file rather than another symlink,
+    # but for link time we create the symlink libNAME.so -> libNAME.so.V
+
+    case $with_aix_soname,$aix_use_runtimelinking in
+    # AIX (on Power*) has no versioning support, so currently we cannot 
hardcode correct
+    # soname into executable. Probably we can add versioning support to
+    # collect2, so additional links can be useful in future.
+    aix,yes) # traditional libtool
+      dynamic_linker='AIX unversionable lib.so'
+      # If using run time linking (on AIX 4.2 or later) use lib<name>.so
+      # instead of lib<name>.a to let people know that these are not
+      # typical AIX shared libraries.
+      library_names_spec='$libname$release$shared_ext$versuffix 
$libname$release$shared_ext$major $libname$shared_ext'
+      ;;
+    aix,no) # traditional AIX only
+      dynamic_linker='AIX lib.a[(]lib.so.V[)]'
+      # We preserve .a as extension for shared libraries through AIX4.2
+      # and later when we are not doing run time linking.
+      library_names_spec='$libname$release.a $libname.a'
+      soname_spec='$libname$release$shared_ext$major'
+      ;;
+    svr4,*) # full svr4 only
+      dynamic_linker="AIX lib.so.V[(]$shared_archive_member_spec.o[)]"
+      library_names_spec='$libname$release$shared_ext$major 
$libname$shared_ext'
+      # We do not specify a path in Import Files, so LIBPATH fires.
+      shlibpath_overrides_runpath=yes
+      ;;
+    *,yes) # both, prefer svr4
+      dynamic_linker="AIX lib.so.V[(]$shared_archive_member_spec.o[)], 
lib.a[(]lib.so.V[)]"
+      library_names_spec='$libname$release$shared_ext$major 
$libname$shared_ext'
+      # unpreferred sharedlib libNAME.a needs extra handling
+      postinstall_cmds='test -n "$linkname" || 
linkname="$realname"~func_stripname "" ".so" "$linkname"~$install_shared_prog 
"$dir/$func_stripname_result.$libext" 
"$destdir/$func_stripname_result.$libext"~test -z "$tstripme" || test -z 
"$striplib" || $striplib "$destdir/$func_stripname_result.$libext"'
+      postuninstall_cmds='for n in $library_names $old_library; do :; 
done~func_stripname "" ".so" "$n"~test "$func_stripname_result" = "$n" || 
func_append rmfiles " $odir/$func_stripname_result.$libext"'
+      # We do not specify a path in Import Files, so LIBPATH fires.
+      shlibpath_overrides_runpath=yes
+      ;;
+    *,no) # both, prefer aix
+      dynamic_linker="AIX lib.a[(]lib.so.V[)], 
lib.so.V[(]$shared_archive_member_spec.o[)]"
+      library_names_spec='$libname$release.a $libname.a'
+      soname_spec='$libname$release$shared_ext$major'
+      # unpreferred sharedlib libNAME.so.V and symlink libNAME.so need extra 
handling
+      postinstall_cmds='test -z "$dlname" || $install_shared_prog $dir/$dlname 
$destdir/$dlname~test -z "$tstripme" || test -z "$striplib" || $striplib 
$destdir/$dlname~test -n "$linkname" || linkname=$realname~func_stripname "" 
".a" "$linkname"~(cd "$destdir" && $LN_S -f $dlname $func_stripname_result.so)'
+      postuninstall_cmds='test -z "$dlname" || func_append rmfiles " 
$odir/$dlname"~for n in $old_library $library_names; do :; done~func_stripname 
"" ".a" "$n"~func_append rmfiles " $odir/$func_stripname_result.so"'
+      ;;
+    esac
+    shlibpath_var=LIBPATH
+  fi
+  ;;
+
+amigaos*)
+  case $host_cpu in
+  powerpc)
+    # Since July 2007 AmigaOS4 officially supports .so libraries.
+    # When compiling the executable, add -use-dynld -Lsobjs: to the 
compileline.
+    library_names_spec='$libname$release$shared_ext$versuffix 
$libname$release$shared_ext$major $libname$shared_ext'
+    ;;
+  m68k)
+    library_names_spec='$libname.ixlibrary $libname.a'
+    # Create ${libname}_ixlibrary.a entries in /sys/libs.
+    finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do 
libname=`func_echo_all "$lib" | $SED 
'\''s%^.*/\([[^/]]*\)\.ixlibrary$%\1%'\''`; $RM 
/sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib 
${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || 
exit 1; done'
+    ;;
+  esac
+  ;;
+
+beos*)
+  library_names_spec='$libname$shared_ext'
+  dynamic_linker="$host_os ld.so"
+  shlibpath_var=LIBRARY_PATH
+  ;;
+
+bsdi[[45]]*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_version=no
+  library_names_spec='$libname$release$shared_ext$versuffix 
$libname$release$shared_ext$major $libname$shared_ext'
+  soname_spec='$libname$release$shared_ext$major'
+  finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib 
/usr/local/lib"
+  sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib"
+  # the default ld.so.conf also contains /usr/contrib/lib and
+  # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow
+  # libtool to hard-code these into programs
+  ;;
+
+cygwin* | mingw* | pw32* | cegcc*)
+  version_type=windows
+  shrext_cmds=.dll
+  need_version=no
+  need_lib_prefix=no
+
+  case $GCC,$cc_basename in
+  yes,*)
+    # gcc
+    library_names_spec='$libname.dll.a'
+    # DLL is installed to $(libdir)/../bin by postinstall_cmds
+    postinstall_cmds='base_file=`basename \$file`~
+      dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo 
\$dlname'\''`~
+      dldir=$destdir/`dirname \$dlpath`~
+      test -d \$dldir || mkdir -p \$dldir~
+      $install_prog $dir/$dlname \$dldir/$dlname~
+      chmod a+x \$dldir/$dlname~
+      if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then
+        eval '\''$striplib \$dldir/$dlname'\'' || exit \$?;
+      fi'
+    postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~
+      dlpath=$dir/\$dldll~
+       $RM \$dlpath'
+    shlibpath_overrides_runpath=yes
+
+    case $host_os in
+    cygwin*)
+      # Cygwin DLLs use 'cyg' prefix rather than 'lib'
+      soname_spec='`echo $libname | sed -e 's/^lib/cyg/'``echo $release | $SED 
-e 's/[[.]]/-/g'`$versuffix$shared_ext'
+m4_if([$1], [],[
+      sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/lib/w32api"])
+      ;;
+    mingw* | cegcc*)
+      # MinGW DLLs use traditional 'lib' prefix
+      soname_spec='$libname`echo $release | $SED -e 
's/[[.]]/-/g'`$versuffix$shared_ext'
+      ;;
+    pw32*)
+      # pw32 DLLs use 'pw' prefix rather than 'lib'
+      library_names_spec='`echo $libname | sed -e 's/^lib/pw/'``echo $release 
| $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext'
+      ;;
+    esac
+    dynamic_linker='Win32 ld.exe'
+    ;;
+
+  *,cl*)
+    # Native MSVC
+    libname_spec='$name'
+    soname_spec='$libname`echo $release | $SED -e 
's/[[.]]/-/g'`$versuffix$shared_ext'
+    library_names_spec='$libname.dll.lib'
+
+    case $build_os in
+    mingw*)
+      sys_lib_search_path_spec=
+      lt_save_ifs=$IFS
+      IFS=';'
+      for lt_path in $LIB
+      do
+        IFS=$lt_save_ifs
+        # Let DOS variable expansion print the short 8.3 style file name.
+        lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do 
@echo %~si"`
+        sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path"
+      done
+      IFS=$lt_save_ifs
+      # Convert to MSYS style.
+      sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 
's|\\\\|/|g' -e 's| \\([[a-zA-Z]]\\):| /\\1|g' -e 's|^ ||'`
+      ;;
+    cygwin*)
+      # Convert to unix form, then to dos form, then back to unix form
+      # but this time dos style (no spaces!) so that the unix form looks
+      # like /cygdrive/c/PROGRA~1:/cygdr...
+      sys_lib_search_path_spec=`cygpath --path --unix "$LIB"`
+      sys_lib_search_path_spec=`cygpath --path --dos 
"$sys_lib_search_path_spec" 2>/dev/null`
+      sys_lib_search_path_spec=`cygpath --path --unix 
"$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"`
+      ;;
+    *)
+      sys_lib_search_path_spec=$LIB
+      if $ECHO "$sys_lib_search_path_spec" | [$GREP ';[c-zC-Z]:/' >/dev/null]; 
then
+        # It is most probably a Windows format PATH.
+        sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 
's/;/ /g'`
+      else
+        sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 
"s/$PATH_SEPARATOR/ /g"`
+      fi
+      # FIXME: find the short name or the path components, as spaces are
+      # common. (e.g. "Program Files" -> "PROGRA~1")
+      ;;
+    esac
+
+    # DLL is installed to $(libdir)/../bin by postinstall_cmds
+    postinstall_cmds='base_file=`basename \$file`~
+      dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo 
\$dlname'\''`~
+      dldir=$destdir/`dirname \$dlpath`~
+      test -d \$dldir || mkdir -p \$dldir~
+      $install_prog $dir/$dlname \$dldir/$dlname'
+    postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~
+      dlpath=$dir/\$dldll~
+       $RM \$dlpath'
+    shlibpath_overrides_runpath=yes
+    dynamic_linker='Win32 link.exe'
+    ;;
+
+  *)
+    # Assume MSVC wrapper
+    library_names_spec='$libname`echo $release | $SED -e 
's/[[.]]/-/g'`$versuffix$shared_ext $libname.lib'
+    dynamic_linker='Win32 ld.exe'
+    ;;
+  esac
+  # FIXME: first we should search . and the directory the executable is in
+  shlibpath_var=PATH
+  ;;
+
+darwin* | rhapsody*)
+  dynamic_linker="$host_os dyld"
+  version_type=darwin
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='$libname$release$major$shared_ext $libname$shared_ext'
+  soname_spec='$libname$release$major$shared_ext'
+  shlibpath_overrides_runpath=yes
+  shlibpath_var=DYLD_LIBRARY_PATH
+  shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`'
+m4_if([$1], [],[
+  sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib"])
+  sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib'
+  ;;
+
+dgux*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='$libname$release$shared_ext$versuffix 
$libname$release$shared_ext$major $libname$shared_ext'
+  soname_spec='$libname$release$shared_ext$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  ;;
+
+freebsd* | dragonfly*)
+  # DragonFly does not have aout.  When/if they implement a new
+  # versioning mechanism, adjust this.
+  if test -x /usr/bin/objformat; then
+    objformat=`/usr/bin/objformat`
+  else
+    case $host_os in
+    freebsd[[23]].*) objformat=aout ;;
+    *) objformat=elf ;;
+    esac
+  fi
+  version_type=freebsd-$objformat
+  case $version_type in
+    freebsd-elf*)
+      library_names_spec='$libname$release$shared_ext$versuffix 
$libname$release$shared_ext$major $libname$shared_ext'
+      soname_spec='$libname$release$shared_ext$major'
+      need_version=no
+      need_lib_prefix=no
+      ;;
+    freebsd-*)
+      library_names_spec='$libname$release$shared_ext$versuffix 
$libname$shared_ext$versuffix'
+      need_version=yes
+      ;;
+  esac
+  shlibpath_var=LD_LIBRARY_PATH
+  case $host_os in
+  freebsd2.*)
+    shlibpath_overrides_runpath=yes
+    ;;
+  freebsd3.[[01]]* | freebsdelf3.[[01]]*)
+    shlibpath_overrides_runpath=yes
+    hardcode_into_libs=yes
+    ;;
+  freebsd3.[[2-9]]* | freebsdelf3.[[2-9]]* | \
+  freebsd4.[[0-5]] | freebsdelf4.[[0-5]] | freebsd4.1.1 | freebsdelf4.1.1)
+    shlibpath_overrides_runpath=no
+    hardcode_into_libs=yes
+    ;;
+  *) # from 4.6 on, and DragonFly
+    shlibpath_overrides_runpath=yes
+    hardcode_into_libs=yes
+    ;;
+  esac
+  ;;
+
+haiku*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  dynamic_linker="$host_os runtime_loader"
+  library_names_spec='$libname$release$shared_ext$versuffix 
$libname$release$shared_ext$major $libname$shared_ext'
+  soname_spec='$libname$release$shared_ext$major'
+  shlibpath_var=LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib 
/boot/system/lib'
+  hardcode_into_libs=yes
+  ;;
+
+hpux9* | hpux10* | hpux11*)
+  # Give a soname corresponding to the major version so that dld.sl refuses to
+  # link against other versions.
+  version_type=sunos
+  need_lib_prefix=no
+  need_version=no
+  case $host_cpu in
+  ia64*)
+    shrext_cmds='.so'
+    hardcode_into_libs=yes
+    dynamic_linker="$host_os dld.so"
+    shlibpath_var=LD_LIBRARY_PATH
+    shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
+    library_names_spec='$libname$release$shared_ext$versuffix 
$libname$release$shared_ext$major $libname$shared_ext'
+    soname_spec='$libname$release$shared_ext$major'
+    if test 32 = "$HPUX_IA64_MODE"; then
+      sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 
/usr/local/lib"
+      sys_lib_dlsearch_path_spec=/usr/lib/hpux32
+    else
+      sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64"
+      sys_lib_dlsearch_path_spec=/usr/lib/hpux64
+    fi
+    ;;
+  hppa*64*)
+    shrext_cmds='.sl'
+    hardcode_into_libs=yes
+    dynamic_linker="$host_os dld.sl"
+    shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH
+    shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
+    library_names_spec='$libname$release$shared_ext$versuffix 
$libname$release$shared_ext$major $libname$shared_ext'
+    soname_spec='$libname$release$shared_ext$major'
+    sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64"
+    sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+    ;;
+  *)
+    shrext_cmds='.sl'
+    dynamic_linker="$host_os dld.sl"
+    shlibpath_var=SHLIB_PATH
+    shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH
+    library_names_spec='$libname$release$shared_ext$versuffix 
$libname$release$shared_ext$major $libname$shared_ext'
+    soname_spec='$libname$release$shared_ext$major'
+    ;;
+  esac
+  # HP-UX runs *really* slowly unless shared libraries are mode 555, ...
+  postinstall_cmds='chmod 555 $lib'
+  # or fails outright, so override atomically:
+  install_override_mode=555
+  ;;
+
+interix[[3-9]]*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='$libname$release$shared_ext$versuffix 
$libname$release$shared_ext$major $libname$shared_ext'
+  soname_spec='$libname$release$shared_ext$major'
+  dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  hardcode_into_libs=yes
+  ;;
+
+irix5* | irix6* | nonstopux*)
+  case $host_os in
+    nonstopux*) version_type=nonstopux ;;
+    *)
+       if test yes = "$lt_cv_prog_gnu_ld"; then
+               version_type=linux # correct to gnu/linux during the next big 
refactor
+       else
+               version_type=irix
+       fi ;;
+  esac
+  need_lib_prefix=no
+  need_version=no
+  soname_spec='$libname$release$shared_ext$major'
+  library_names_spec='$libname$release$shared_ext$versuffix 
$libname$release$shared_ext$major $libname$release$shared_ext 
$libname$shared_ext'
+  case $host_os in
+  irix5* | nonstopux*)
+    libsuff= shlibsuff=
+    ;;
+  *)
+    case $LD in # libtool.m4 will add one of these switches to LD
+    *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ")
+      libsuff= shlibsuff= libmagic=32-bit;;
+    *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ")
+      libsuff=32 shlibsuff=N32 libmagic=N32;;
+    *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ")
+      libsuff=64 shlibsuff=64 libmagic=64-bit;;
+    *) libsuff= shlibsuff= libmagic=never-match;;
+    esac
+    ;;
+  esac
+  shlibpath_var=LD_LIBRARY${shlibsuff}_PATH
+  shlibpath_overrides_runpath=no
+  sys_lib_search_path_spec="/usr/lib$libsuff /lib$libsuff 
/usr/local/lib$libsuff"
+  sys_lib_dlsearch_path_spec="/usr/lib$libsuff /lib$libsuff"
+  hardcode_into_libs=yes
+  ;;
+
+# No shared lib support for Linux oldld, aout, or coff.
+linux*oldld* | linux*aout* | linux*coff*)
+  dynamic_linker=no
+  ;;
+
+linux*android*)
+  version_type=none # Android doesn't support versioned libraries.
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='$libname$release$shared_ext'
+  soname_spec='$libname$release$shared_ext'
+  finish_cmds=
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+
+  # This implies no fast_install, which is unacceptable.
+  # Some rework will be needed to allow for fast_install
+  # before this can be enabled.
+  hardcode_into_libs=yes
+
+  dynamic_linker='Android linker'
+  # Don't embed -rpath directories since the linker doesn't support them.
+  _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+  ;;
+
+# This must be glibc/ELF.
+linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='$libname$release$shared_ext$versuffix 
$libname$release$shared_ext$major $libname$shared_ext'
+  soname_spec='$libname$release$shared_ext$major'
+  finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+
+  # Some binutils ld are patched to set DT_RUNPATH
+  AC_CACHE_VAL([lt_cv_shlibpath_overrides_runpath],
+    [lt_cv_shlibpath_overrides_runpath=no
+    save_LDFLAGS=$LDFLAGS
+    save_libdir=$libdir
+    eval "libdir=/foo; wl=\"$_LT_TAGVAR(lt_prog_compiler_wl, $1)\"; \
+        LDFLAGS=\"\$LDFLAGS $_LT_TAGVAR(hardcode_libdir_flag_spec, $1)\""
+    AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])],
+      [AS_IF([ ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep 
"RUNPATH.*$libdir" >/dev/null],
+        [lt_cv_shlibpath_overrides_runpath=yes])])
+    LDFLAGS=$save_LDFLAGS
+    libdir=$save_libdir
+    ])
+  shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath
+
+  # This implies no fast_install, which is unacceptable.
+  # Some rework will be needed to allow for fast_install
+  # before this can be enabled.
+  hardcode_into_libs=yes
+
+  # Ideally, we could use ldconfig to report *all* directores which are
+  # searched for libraries, however this is still not possible.  Aside from not
+  # being certain /sbin/ldconfig is available, command
+  # 'ldconfig -N -X -v | grep ^/' on 64bit Fedora does not report /usr/lib64,
+  # even though it is searched at run-time.  Try to do the best guess by
+  # appending ld.so.conf contents (and includes) to the search path.
+  if test -f /etc/ld.so.conf; then
+    lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 
2>/dev/null", \[$]2)); skip = 1; } { if (!skip) print \[$]0; skip = 0; }' < 
/etc/ld.so.conf | $SED -e 's/#.*//;/^[  ]*hwcap[        ]/d;s/[:,      ]/ 
/g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '`
+    sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra"
+  fi
+
+  # We used to test for /lib/ld.so.1 and disable shared libraries on
+  # powerpc, because MkLinux only supported shared libraries with the
+  # GNU dynamic linker.  Since this was broken with cross compilers,
+  # most powerpc-linux boxes support dynamic linking these days and
+  # people can always --disable-shared, the test was removed, and we
+  # assume the GNU/Linux dynamic linker is in use.
+  dynamic_linker='GNU/Linux ld.so'
+  ;;
+
+netbsdelf*-gnu)
+  version_type=linux
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='${libname}${release}${shared_ext}$versuffix 
${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+  soname_spec='${libname}${release}${shared_ext}$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  hardcode_into_libs=yes
+  dynamic_linker='NetBSD ld.elf_so'
+  ;;
+
+netbsd*)
+  version_type=sunos
+  need_lib_prefix=no
+  need_version=no
+  if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+    library_names_spec='$libname$release$shared_ext$versuffix 
$libname$shared_ext$versuffix'
+    finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+    dynamic_linker='NetBSD (a.out) ld.so'
+  else
+    library_names_spec='$libname$release$shared_ext$versuffix 
$libname$release$shared_ext$major $libname$shared_ext'
+    soname_spec='$libname$release$shared_ext$major'
+    dynamic_linker='NetBSD ld.elf_so'
+  fi
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  hardcode_into_libs=yes
+  ;;
+
+newsos6)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  library_names_spec='$libname$release$shared_ext$versuffix 
$libname$release$shared_ext$major $libname$shared_ext'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  ;;
+
+*nto* | *qnx*)
+  version_type=qnx
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='$libname$release$shared_ext$versuffix 
$libname$release$shared_ext$major $libname$shared_ext'
+  soname_spec='$libname$release$shared_ext$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  hardcode_into_libs=yes
+  dynamic_linker='ldqnx.so'
+  ;;
+
+openbsd* | bitrig*)
+  version_type=sunos
+  sys_lib_dlsearch_path_spec=/usr/lib
+  need_lib_prefix=no
+  if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then
+    need_version=no
+  else
+    need_version=yes
+  fi
+  library_names_spec='$libname$release$shared_ext$versuffix 
$libname$shared_ext$versuffix'
+  finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  ;;
+
+os2*)
+  libname_spec='$name'
+  version_type=windows
+  shrext_cmds=.dll
+  need_version=no
+  need_lib_prefix=no
+  # OS/2 can only load a DLL with a base name of 8 characters or less.
+  soname_spec='`test -n "$os2dllname" && libname="$os2dllname";
+    v=$($ECHO $release$versuffix | tr -d .-);
+    n=$($ECHO $libname | cut -b -$((8 - ${#v})) | tr . _);
+    $ECHO $n$v`$shared_ext'
+  library_names_spec='${libname}_dll.$libext'
+  dynamic_linker='OS/2 ld.exe'
+  shlibpath_var=BEGINLIBPATH
+  sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib"
+  sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+  postinstall_cmds='base_file=`basename \$file`~
+    dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; $ECHO 
\$dlname'\''`~
+    dldir=$destdir/`dirname \$dlpath`~
+    test -d \$dldir || mkdir -p \$dldir~
+    $install_prog $dir/$dlname \$dldir/$dlname~
+    chmod a+x \$dldir/$dlname~
+    if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then
+      eval '\''$striplib \$dldir/$dlname'\'' || exit \$?;
+    fi'
+  postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; $ECHO \$dlname'\''`~
+    dlpath=$dir/\$dldll~
+    $RM \$dlpath'
+  ;;
+
+osf3* | osf4* | osf5*)
+  version_type=osf
+  need_lib_prefix=no
+  need_version=no
+  soname_spec='$libname$release$shared_ext$major'
+  library_names_spec='$libname$release$shared_ext$versuffix 
$libname$release$shared_ext$major $libname$shared_ext'
+  shlibpath_var=LD_LIBRARY_PATH
+  sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc 
/usr/lib /usr/local/lib /var/shlib"
+  sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+  ;;
+
+rdos*)
+  dynamic_linker=no
+  ;;
+
+solaris*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='$libname$release$shared_ext$versuffix 
$libname$release$shared_ext$major $libname$shared_ext'
+  soname_spec='$libname$release$shared_ext$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  hardcode_into_libs=yes
+  # ldd complains unless libraries are executable
+  postinstall_cmds='chmod +x $lib'
+  ;;
+
+sunos4*)
+  version_type=sunos
+  library_names_spec='$libname$release$shared_ext$versuffix 
$libname$shared_ext$versuffix'
+  finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  if test yes = "$with_gnu_ld"; then
+    need_lib_prefix=no
+  fi
+  need_version=yes
+  ;;
+
+sysv4 | sysv4.3*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  library_names_spec='$libname$release$shared_ext$versuffix 
$libname$release$shared_ext$major $libname$shared_ext'
+  soname_spec='$libname$release$shared_ext$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  case $host_vendor in
+    sni)
+      shlibpath_overrides_runpath=no
+      need_lib_prefix=no
+      runpath_var=LD_RUN_PATH
+      ;;
+    siemens)
+      need_lib_prefix=no
+      ;;
+    motorola)
+      need_lib_prefix=no
+      need_version=no
+      shlibpath_overrides_runpath=no
+      sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib'
+      ;;
+  esac
+  ;;
+
+sysv4*MP*)
+  if test -d /usr/nec; then
+    version_type=linux # correct to gnu/linux during the next big refactor
+    library_names_spec='$libname$shared_ext.$versuffix 
$libname$shared_ext.$major $libname$shared_ext'
+    soname_spec='$libname$shared_ext.$major'
+    shlibpath_var=LD_LIBRARY_PATH
+  fi
+  ;;
+
+sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
+  version_type=sco
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='$libname$release$shared_ext$versuffix 
$libname$release$shared_ext $libname$shared_ext'
+  soname_spec='$libname$release$shared_ext$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  hardcode_into_libs=yes
+  if test yes = "$with_gnu_ld"; then
+    sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib 
/usr/lib /lib'
+  else
+    sys_lib_search_path_spec='/usr/ccs/lib /usr/lib'
+    case $host_os in
+      sco3.2v5*)
+        sys_lib_search_path_spec="$sys_lib_search_path_spec /lib"
+       ;;
+    esac
+  fi
+  sys_lib_dlsearch_path_spec='/usr/lib'
+  ;;
+
+tpf*)
+  # TPF is a cross-target only.  Preferred cross-host = GNU/Linux.
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='$libname$release$shared_ext$versuffix 
$libname$release$shared_ext$major $libname$shared_ext'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  hardcode_into_libs=yes
+  ;;
+
+uts4*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  library_names_spec='$libname$release$shared_ext$versuffix 
$libname$release$shared_ext$major $libname$shared_ext'
+  soname_spec='$libname$release$shared_ext$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  ;;
+
+*)
+  dynamic_linker=no
+  ;;
+esac
+AC_MSG_RESULT([$dynamic_linker])
+test no = "$dynamic_linker" && can_build_shared=no
+
+variables_saved_for_relink="PATH $shlibpath_var $runpath_var"
+if test yes = "$GCC"; then
+  variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX 
COMPILER_PATH LIBRARY_PATH"
+fi
+
+if test set = "${lt_cv_sys_lib_search_path_spec+set}"; then
+  sys_lib_search_path_spec=$lt_cv_sys_lib_search_path_spec
+fi
+
+if test set = "${lt_cv_sys_lib_dlsearch_path_spec+set}"; then
+  sys_lib_dlsearch_path_spec=$lt_cv_sys_lib_dlsearch_path_spec
+fi
+
+# remember unaugmented sys_lib_dlsearch_path content for libtool script 
decls...
+configure_time_dlsearch_path=$sys_lib_dlsearch_path_spec
+
+# ... but it needs LT_SYS_LIBRARY_PATH munging for other configure-time code
+func_munge_path_list sys_lib_dlsearch_path_spec "$LT_SYS_LIBRARY_PATH"
+
+# to be used as default LT_SYS_LIBRARY_PATH value in generated libtool
+configure_time_lt_sys_library_path=$LT_SYS_LIBRARY_PATH
+
+_LT_DECL([], [variables_saved_for_relink], [1],
+    [Variables whose values should be saved in libtool wrapper scripts and
+    restored at link time])
+_LT_DECL([], [need_lib_prefix], [0],
+    [Do we need the "lib" prefix for modules?])
+_LT_DECL([], [need_version], [0], [Do we need a version for libraries?])
+_LT_DECL([], [version_type], [0], [Library versioning type])
+_LT_DECL([], [runpath_var], [0],  [Shared library runtime path variable])
+_LT_DECL([], [shlibpath_var], [0],[Shared library path variable])
+_LT_DECL([], [shlibpath_overrides_runpath], [0],
+    [Is shlibpath searched before the hard-coded library search path?])
+_LT_DECL([], [libname_spec], [1], [Format of library name prefix])
+_LT_DECL([], [library_names_spec], [1],
+    [[List of archive names.  First name is the real one, the rest are links.
+    The last name is the one that the linker finds with -lNAME]])
+_LT_DECL([], [soname_spec], [1],
+    [[The coded name of the library, if different from the real name]])
+_LT_DECL([], [install_override_mode], [1],
+    [Permission mode override for installation of shared libraries])
+_LT_DECL([], [postinstall_cmds], [2],
+    [Command to use after installation of a shared archive])
+_LT_DECL([], [postuninstall_cmds], [2],
+    [Command to use after uninstallation of a shared archive])
+_LT_DECL([], [finish_cmds], [2],
+    [Commands used to finish a libtool library installation in a directory])
+_LT_DECL([], [finish_eval], [1],
+    [[As "finish_cmds", except a single script fragment to be evaled but
+    not shown]])
+_LT_DECL([], [hardcode_into_libs], [0],
+    [Whether we should hardcode library paths into libraries])
+_LT_DECL([], [sys_lib_search_path_spec], [2],
+    [Compile-time system search path for libraries])
+_LT_DECL([sys_lib_dlsearch_path_spec], [configure_time_dlsearch_path], [2],
+    [Detected run-time system search path for libraries])
+_LT_DECL([], [configure_time_lt_sys_library_path], [2],
+    [Explicit LT_SYS_LIBRARY_PATH set during ./configure time])
+])# _LT_SYS_DYNAMIC_LINKER
+
+
+# _LT_PATH_TOOL_PREFIX(TOOL)
+# --------------------------
+# find a file program that can recognize shared library
+AC_DEFUN([_LT_PATH_TOOL_PREFIX],
+[m4_require([_LT_DECL_EGREP])dnl
+AC_MSG_CHECKING([for $1])
+AC_CACHE_VAL(lt_cv_path_MAGIC_CMD,
+[case $MAGIC_CMD in
+[[\\/*] |  ?:[\\/]*])
+  lt_cv_path_MAGIC_CMD=$MAGIC_CMD # Let the user override the test with a path.
+  ;;
+*)
+  lt_save_MAGIC_CMD=$MAGIC_CMD
+  lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR
+dnl $ac_dummy forces splitting on constant user-supplied paths.
+dnl POSIX.2 word splitting is done only on the output of word expansions,
+dnl not every word.  This closes a longstanding sh security hole.
+  ac_dummy="m4_if([$2], , $PATH, [$2])"
+  for ac_dir in $ac_dummy; do
+    IFS=$lt_save_ifs
+    test -z "$ac_dir" && ac_dir=.
+    if test -f "$ac_dir/$1"; then
+      lt_cv_path_MAGIC_CMD=$ac_dir/"$1"
+      if test -n "$file_magic_test_file"; then
+       case $deplibs_check_method in
+       "file_magic "*)
+         file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"`
+         MAGIC_CMD=$lt_cv_path_MAGIC_CMD
+         if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null |
+           $EGREP "$file_magic_regex" > /dev/null; then
+           :
+         else
+           cat <<_LT_EOF 1>&2
+
+*** Warning: the command libtool uses to detect shared libraries,
+*** $file_magic_cmd, produces output that libtool cannot recognize.
+*** The result is that libtool may fail to recognize shared libraries
+*** as such.  This will affect the creation of libtool libraries that
+*** depend on shared libraries, but programs linked with such libtool
+*** libraries will work regardless of this problem.  Nevertheless, you
+*** may want to report the problem to your system manager and/or to
+*** address@hidden
+
+_LT_EOF
+         fi ;;
+       esac
+      fi
+      break
+    fi
+  done
+  IFS=$lt_save_ifs
+  MAGIC_CMD=$lt_save_MAGIC_CMD
+  ;;
+esac])
+MAGIC_CMD=$lt_cv_path_MAGIC_CMD
+if test -n "$MAGIC_CMD"; then
+  AC_MSG_RESULT($MAGIC_CMD)
+else
+  AC_MSG_RESULT(no)
+fi
+_LT_DECL([], [MAGIC_CMD], [0],
+        [Used to examine libraries when file_magic_cmd begins with "file"])dnl
+])# _LT_PATH_TOOL_PREFIX
+
+# Old name:
+AU_ALIAS([AC_PATH_TOOL_PREFIX], [_LT_PATH_TOOL_PREFIX])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_PATH_TOOL_PREFIX], [])
+
+
+# _LT_PATH_MAGIC
+# --------------
+# find a file program that can recognize a shared library
+m4_defun([_LT_PATH_MAGIC],
+[_LT_PATH_TOOL_PREFIX(${ac_tool_prefix}file, /usr/bin$PATH_SEPARATOR$PATH)
+if test -z "$lt_cv_path_MAGIC_CMD"; then
+  if test -n "$ac_tool_prefix"; then
+    _LT_PATH_TOOL_PREFIX(file, /usr/bin$PATH_SEPARATOR$PATH)
+  else
+    MAGIC_CMD=:
+  fi
+fi
+])# _LT_PATH_MAGIC
+
+
+# LT_PATH_LD
+# ----------
+# find the pathname to the GNU or non-GNU linker
+AC_DEFUN([LT_PATH_LD],
+[AC_REQUIRE([AC_PROG_CC])dnl
+AC_REQUIRE([AC_CANONICAL_HOST])dnl
+AC_REQUIRE([AC_CANONICAL_BUILD])dnl
+m4_require([_LT_DECL_SED])dnl
+m4_require([_LT_DECL_EGREP])dnl
+m4_require([_LT_PROG_ECHO_BACKSLASH])dnl
+
+AC_ARG_WITH([gnu-ld],
+    [AS_HELP_STRING([--with-gnu-ld],
+       [assume the C compiler uses GNU ld @<:@default=no@:>@])],
+    [test no = "$withval" || with_gnu_ld=yes],
+    [with_gnu_ld=no])dnl
+
+ac_prog=ld
+if test yes = "$GCC"; then
+  # Check if gcc -print-prog-name=ld gives a path.
+  AC_MSG_CHECKING([for ld used by $CC])
+  case $host in
+  *-*-mingw*)
+    # gcc leaves a trailing carriage return, which upsets mingw
+    ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;;
+  *)
+    ac_prog=`($CC -print-prog-name=ld) 2>&5` ;;
+  esac
+  case $ac_prog in
+    # Accept absolute paths.
+    [[\\/]]* | ?:[[\\/]]*)
+      re_direlt='/[[^/]][[^/]]*/\.\./'
+      # Canonicalize the pathname of ld
+      ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'`
+      while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do
+       ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"`
+      done
+      test -z "$LD" && LD=$ac_prog
+      ;;
+  "")
+    # If it fails, then pretend we aren't using GCC.
+    ac_prog=ld
+    ;;
+  *)
+    # If it is relative, then search for the first ld in PATH.
+    with_gnu_ld=unknown
+    ;;
+  esac
+elif test yes = "$with_gnu_ld"; then
+  AC_MSG_CHECKING([for GNU ld])
+else
+  AC_MSG_CHECKING([for non-GNU ld])
+fi
+AC_CACHE_VAL(lt_cv_path_LD,
+[if test -z "$LD"; then
+  lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR
+  for ac_dir in $PATH; do
+    IFS=$lt_save_ifs
+    test -z "$ac_dir" && ac_dir=.
+    if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then
+      lt_cv_path_LD=$ac_dir/$ac_prog
+      # Check to see if the program is GNU ld.  I'd rather use --version,
+      # but apparently some variants of GNU ld only accept -v.
+      # Break only if it was the GNU/non-GNU ld that we prefer.
+      case `"$lt_cv_path_LD" -v 2>&1 </dev/null` in
+      *GNU* | *'with BFD'*)
+       test no != "$with_gnu_ld" && break
+       ;;
+      *)
+       test yes != "$with_gnu_ld" && break
+       ;;
+      esac
+    fi
+  done
+  IFS=$lt_save_ifs
+else
+  lt_cv_path_LD=$LD # Let the user override the test with a path.
+fi])
+LD=$lt_cv_path_LD
+if test -n "$LD"; then
+  AC_MSG_RESULT($LD)
+else
+  AC_MSG_RESULT(no)
+fi
+test -z "$LD" && AC_MSG_ERROR([no acceptable ld found in \$PATH])
+_LT_PATH_LD_GNU
+AC_SUBST([LD])
+
+_LT_TAGDECL([], [LD], [1], [The linker used to build libraries])
+])# LT_PATH_LD
+
+# Old names:
+AU_ALIAS([AM_PROG_LD], [LT_PATH_LD])
+AU_ALIAS([AC_PROG_LD], [LT_PATH_LD])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AM_PROG_LD], [])
+dnl AC_DEFUN([AC_PROG_LD], [])
+
+
+# _LT_PATH_LD_GNU
+#- --------------
+m4_defun([_LT_PATH_LD_GNU],
+[AC_CACHE_CHECK([if the linker ($LD) is GNU ld], lt_cv_prog_gnu_ld,
+[# I'd rather use --version here, but apparently some GNU lds only accept -v.
+case `$LD -v 2>&1 </dev/null` in
+*GNU* | *'with BFD'*)
+  lt_cv_prog_gnu_ld=yes
+  ;;
+*)
+  lt_cv_prog_gnu_ld=no
+  ;;
+esac])
+with_gnu_ld=$lt_cv_prog_gnu_ld
+])# _LT_PATH_LD_GNU
+
+
+# _LT_CMD_RELOAD
+# --------------
+# find reload flag for linker
+#   -- PORTME Some linkers may need a different reload flag.
+m4_defun([_LT_CMD_RELOAD],
+[AC_CACHE_CHECK([for $LD option to reload object files],
+  lt_cv_ld_reload_flag,
+  [lt_cv_ld_reload_flag='-r'])
+reload_flag=$lt_cv_ld_reload_flag
+case $reload_flag in
+"" | " "*) ;;
+*) reload_flag=" $reload_flag" ;;
+esac
+reload_cmds='$LD$reload_flag -o $output$reload_objs'
+case $host_os in
+  cygwin* | mingw* | pw32* | cegcc*)
+    if test yes != "$GCC"; then
+      reload_cmds=false
+    fi
+    ;;
+  darwin*)
+    if test yes = "$GCC"; then
+      reload_cmds='$LTCC $LTCFLAGS -nostdlib $wl-r -o $output$reload_objs'
+    else
+      reload_cmds='$LD$reload_flag -o $output$reload_objs'
+    fi
+    ;;
+esac
+_LT_TAGDECL([], [reload_flag], [1], [How to create reloadable object files])dnl
+_LT_TAGDECL([], [reload_cmds], [2])dnl
+])# _LT_CMD_RELOAD
+
+
+# _LT_PATH_DD
+# -----------
+# find a working dd
+m4_defun([_LT_PATH_DD],
+[AC_CACHE_CHECK([for a working dd], [ac_cv_path_lt_DD],
+[printf 0123456789abcdef0123456789abcdef >conftest.i
+cat conftest.i conftest.i >conftest2.i
+: ${lt_DD:=$DD}
+AC_PATH_PROGS_FEATURE_CHECK([lt_DD], [dd],
+[if "$ac_path_lt_DD" bs=32 count=1 <conftest2.i >conftest.out 2>/dev/null; then
+  cmp -s conftest.i conftest.out \
+  && ac_cv_path_lt_DD="$ac_path_lt_DD" ac_path_lt_DD_found=:
+fi])
+rm -f conftest.i conftest2.i conftest.out])
+])# _LT_PATH_DD
+
+
+# _LT_CMD_TRUNCATE
+# ----------------
+# find command to truncate a binary pipe
+m4_defun([_LT_CMD_TRUNCATE],
+[m4_require([_LT_PATH_DD])
+AC_CACHE_CHECK([how to truncate binary pipes], [lt_cv_truncate_bin],
+[printf 0123456789abcdef0123456789abcdef >conftest.i
+cat conftest.i conftest.i >conftest2.i
+lt_cv_truncate_bin=
+if "$ac_cv_path_lt_DD" bs=32 count=1 <conftest2.i >conftest.out 2>/dev/null; 
then
+  cmp -s conftest.i conftest.out \
+  && lt_cv_truncate_bin="$ac_cv_path_lt_DD bs=4096 count=1"
+fi
+rm -f conftest.i conftest2.i conftest.out
+test -z "$lt_cv_truncate_bin" && lt_cv_truncate_bin="$SED -e 4q"])
+_LT_DECL([lt_truncate_bin], [lt_cv_truncate_bin], [1],
+  [Command to truncate a binary pipe])
+])# _LT_CMD_TRUNCATE
+
+
+# _LT_CHECK_MAGIC_METHOD
+# ----------------------
+# how to check for library dependencies
+#  -- PORTME fill in with the dynamic library characteristics
+m4_defun([_LT_CHECK_MAGIC_METHOD],
+[m4_require([_LT_DECL_EGREP])
+m4_require([_LT_DECL_OBJDUMP])
+AC_CACHE_CHECK([how to recognize dependent libraries],
+lt_cv_deplibs_check_method,
+[lt_cv_file_magic_cmd='$MAGIC_CMD'
+lt_cv_file_magic_test_file=
+lt_cv_deplibs_check_method='unknown'
+# Need to set the preceding variable on all platforms that support
+# interlibrary dependencies.
+# 'none' -- dependencies not supported.
+# 'unknown' -- same as none, but documents that we really don't know.
+# 'pass_all' -- all dependencies passed with no checks.
+# 'test_compile' -- check by making test program.
+# 'file_magic [[regex]]' -- check by looking for files in library path
+# that responds to the $file_magic_cmd with a given extended regex.
+# If you have 'file' or equivalent on your system and you're not sure
+# whether 'pass_all' will *always* work, you probably want this one.
+
+case $host_os in
+aix[[4-9]]*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+beos*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+bsdi[[45]]*)
+  lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB 
(shared object|dynamic lib)'
+  lt_cv_file_magic_cmd='/usr/bin/file -L'
+  lt_cv_file_magic_test_file=/shlib/libc.so
+  ;;
+
+cygwin*)
+  # func_win32_libid is a shell function defined in ltmain.sh
+  lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL'
+  lt_cv_file_magic_cmd='func_win32_libid'
+  ;;
+
+mingw* | pw32*)
+  # Base MSYS/MinGW do not provide the 'file' command needed by
+  # func_win32_libid shell function, so use a weaker test based on 'objdump',
+  # unless we find 'file', for example because we are cross-compiling.
+  if ( file / ) >/dev/null 2>&1; then
+    lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL'
+    lt_cv_file_magic_cmd='func_win32_libid'
+  else
+    # Keep this pattern in sync with the one in func_win32_libid.
+    lt_cv_deplibs_check_method='file_magic file format 
(pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)'
+    lt_cv_file_magic_cmd='$OBJDUMP -f'
+  fi
+  ;;
+
+cegcc*)
+  # use the weaker test based on 'objdump'. See mingw*.
+  lt_cv_deplibs_check_method='file_magic file format 
pe-arm-.*little(.*architecture: arm)?'
+  lt_cv_file_magic_cmd='$OBJDUMP -f'
+  ;;
+
+darwin* | rhapsody*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+freebsd* | dragonfly*)
+  if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then
+    case $host_cpu in
+    i*86 )
+      # Not sure whether the presence of OpenBSD here was a mistake.
+      # Let's accept both of them until this is cleared up.
+      lt_cv_deplibs_check_method='file_magic 
(FreeBSD|OpenBSD|DragonFly)/i[[3-9]]86 (compact )?demand paged shared library'
+      lt_cv_file_magic_cmd=/usr/bin/file
+      lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*`
+      ;;
+    esac
+  else
+    lt_cv_deplibs_check_method=pass_all
+  fi
+  ;;
+
+haiku*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+hpux10.20* | hpux11*)
+  lt_cv_file_magic_cmd=/usr/bin/file
+  case $host_cpu in
+  ia64*)
+    lt_cv_deplibs_check_method='file_magic 
(s[[0-9]][[0-9]][[0-9]]|ELF-[[0-9]][[0-9]]) shared object file - IA64'
+    lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so
+    ;;
+  hppa*64*)
+    [lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF[ 
-][0-9][0-9])(-bit)?( [LM]SB)? shared object( file)?[, -]* PA-RISC 
[0-9]\.[0-9]']
+    lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl
+    ;;
+  *)
+    lt_cv_deplibs_check_method='file_magic 
(s[[0-9]][[0-9]][[0-9]]|PA-RISC[[0-9]]\.[[0-9]]) shared library'
+    lt_cv_file_magic_test_file=/usr/lib/libc.sl
+    ;;
+  esac
+  ;;
+
+interix[[3-9]]*)
+  # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here
+  lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|\.a)$'
+  ;;
+
+irix5* | irix6* | nonstopux*)
+  case $LD in
+  *-32|*"-32 ") libmagic=32-bit;;
+  *-n32|*"-n32 ") libmagic=N32;;
+  *-64|*"-64 ") libmagic=64-bit;;
+  *) libmagic=never-match;;
+  esac
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+# This must be glibc/ELF.
+linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+netbsd* | netbsdelf*-gnu)
+  if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then
+    lt_cv_deplibs_check_method='match_pattern 
/lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$'
+  else
+    lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|_pic\.a)$'
+  fi
+  ;;
+
+newos6*)
+  lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB 
(executable|dynamic lib)'
+  lt_cv_file_magic_cmd=/usr/bin/file
+  lt_cv_file_magic_test_file=/usr/lib/libnls.so
+  ;;
+
+*nto* | *qnx*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+openbsd* | bitrig*)
+  if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then
+    lt_cv_deplibs_check_method='match_pattern 
/lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|\.so|_pic\.a)$'
+  else
+    lt_cv_deplibs_check_method='match_pattern 
/lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$'
+  fi
+  ;;
+
+osf3* | osf4* | osf5*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+rdos*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+solaris*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+sysv4 | sysv4.3*)
+  case $host_vendor in
+  motorola)
+    lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB 
(shared object|dynamic lib) M[[0-9]][[0-9]]* Version [[0-9]]'
+    lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*`
+    ;;
+  ncr)
+    lt_cv_deplibs_check_method=pass_all
+    ;;
+  sequent)
+    lt_cv_file_magic_cmd='/bin/file'
+    lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB 
(shared object|dynamic lib )'
+    ;;
+  sni)
+    lt_cv_file_magic_cmd='/bin/file'
+    lt_cv_deplibs_check_method="file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB 
dynamic lib"
+    lt_cv_file_magic_test_file=/lib/libc.so
+    ;;
+  siemens)
+    lt_cv_deplibs_check_method=pass_all
+    ;;
+  pc)
+    lt_cv_deplibs_check_method=pass_all
+    ;;
+  esac
+  ;;
+
+tpf*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+os2*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+esac
+])
+
+file_magic_glob=
+want_nocaseglob=no
+if test "$build" = "$host"; then
+  case $host_os in
+  mingw* | pw32*)
+    if ( shopt | grep nocaseglob ) >/dev/null 2>&1; then
+      want_nocaseglob=yes
+    else
+      file_magic_glob=`echo 
aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ | $SED -e 
"s/\(..\)/s\/[[\1]]\/[[\1]]\/g;/g"`
+    fi
+    ;;
+  esac
+fi
+
+file_magic_cmd=$lt_cv_file_magic_cmd
+deplibs_check_method=$lt_cv_deplibs_check_method
+test -z "$deplibs_check_method" && deplibs_check_method=unknown
+
+_LT_DECL([], [deplibs_check_method], [1],
+    [Method to check whether dependent libraries are shared objects])
+_LT_DECL([], [file_magic_cmd], [1],
+    [Command to use when deplibs_check_method = "file_magic"])
+_LT_DECL([], [file_magic_glob], [1],
+    [How to find potential files when deplibs_check_method = "file_magic"])
+_LT_DECL([], [want_nocaseglob], [1],
+    [Find potential files using nocaseglob when deplibs_check_method = 
"file_magic"])
+])# _LT_CHECK_MAGIC_METHOD
+
+
+# LT_PATH_NM
+# ----------
+# find the pathname to a BSD- or MS-compatible name lister
+AC_DEFUN([LT_PATH_NM],
+[AC_REQUIRE([AC_PROG_CC])dnl
+AC_CACHE_CHECK([for BSD- or MS-compatible name lister (nm)], lt_cv_path_NM,
+[if test -n "$NM"; then
+  # Let the user override the test.
+  lt_cv_path_NM=$NM
+else
+  lt_nm_to_check=${ac_tool_prefix}nm
+  if test -n "$ac_tool_prefix" && test "$build" = "$host"; then
+    lt_nm_to_check="$lt_nm_to_check nm"
+  fi
+  for lt_tmp_nm in $lt_nm_to_check; do
+    lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR
+    for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do
+      IFS=$lt_save_ifs
+      test -z "$ac_dir" && ac_dir=.
+      tmp_nm=$ac_dir/$lt_tmp_nm
+      if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext"; then
+       # Check to see if the nm accepts a BSD-compat flag.
+       # Adding the 'sed 1q' prevents false positives on HP-UX, which says:
+       #   nm: unknown option "B" ignored
+       # Tru64's nm complains that /dev/null is an invalid object file
+       # MSYS converts /dev/null to NUL, MinGW nm treats NUL as empty
+       case $build_os in
+       mingw*) lt_bad_file=conftest.nm/nofile ;;
+       *) lt_bad_file=/dev/null ;;
+       esac
+       case `"$tmp_nm" -B $lt_bad_file 2>&1 | sed '1q'` in
+       *$lt_bad_file* | *'Invalid file or object type'*)
+         lt_cv_path_NM="$tmp_nm -B"
+         break 2
+         ;;
+       *)
+         case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in
+         */dev/null*)
+           lt_cv_path_NM="$tmp_nm -p"
+           break 2
+           ;;
+         *)
+           lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but
+           continue # so that we can try to find one that supports BSD flags
+           ;;
+         esac
+         ;;
+       esac
+      fi
+    done
+    IFS=$lt_save_ifs
+  done
+  : ${lt_cv_path_NM=no}
+fi])
+if test no != "$lt_cv_path_NM"; then
+  NM=$lt_cv_path_NM
+else
+  # Didn't find any BSD compatible name lister, look for dumpbin.
+  if test -n "$DUMPBIN"; then :
+    # Let the user override the test.
+  else
+    AC_CHECK_TOOLS(DUMPBIN, [dumpbin "link -dump"], :)
+    case `$DUMPBIN -symbols -headers /dev/null 2>&1 | sed '1q'` in
+    *COFF*)
+      DUMPBIN="$DUMPBIN -symbols -headers"
+      ;;
+    *)
+      DUMPBIN=:
+      ;;
+    esac
+  fi
+  AC_SUBST([DUMPBIN])
+  if test : != "$DUMPBIN"; then
+    NM=$DUMPBIN
+  fi
+fi
+test -z "$NM" && NM=nm
+AC_SUBST([NM])
+_LT_DECL([], [NM], [1], [A BSD- or MS-compatible name lister])dnl
+
+AC_CACHE_CHECK([the name lister ($NM) interface], [lt_cv_nm_interface],
+  [lt_cv_nm_interface="BSD nm"
+  echo "int some_variable = 0;" > conftest.$ac_ext
+  (eval echo "\"\$as_me:$LINENO: $ac_compile\"" >&AS_MESSAGE_LOG_FD)
+  (eval "$ac_compile" 2>conftest.err)
+  cat conftest.err >&AS_MESSAGE_LOG_FD
+  (eval echo "\"\$as_me:$LINENO: $NM \\\"conftest.$ac_objext\\\"\"" 
>&AS_MESSAGE_LOG_FD)
+  (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out)
+  cat conftest.err >&AS_MESSAGE_LOG_FD
+  (eval echo "\"\$as_me:$LINENO: output\"" >&AS_MESSAGE_LOG_FD)
+  cat conftest.out >&AS_MESSAGE_LOG_FD
+  if $GREP 'External.*some_variable' conftest.out > /dev/null; then
+    lt_cv_nm_interface="MS dumpbin"
+  fi
+  rm -f conftest*])
+])# LT_PATH_NM
+
+# Old names:
+AU_ALIAS([AM_PROG_NM], [LT_PATH_NM])
+AU_ALIAS([AC_PROG_NM], [LT_PATH_NM])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AM_PROG_NM], [])
+dnl AC_DEFUN([AC_PROG_NM], [])
+
+# _LT_CHECK_SHAREDLIB_FROM_LINKLIB
+# --------------------------------
+# how to determine the name of the shared library
+# associated with a specific link library.
+#  -- PORTME fill in with the dynamic library characteristics
+m4_defun([_LT_CHECK_SHAREDLIB_FROM_LINKLIB],
+[m4_require([_LT_DECL_EGREP])
+m4_require([_LT_DECL_OBJDUMP])
+m4_require([_LT_DECL_DLLTOOL])
+AC_CACHE_CHECK([how to associate runtime and link libraries],
+lt_cv_sharedlib_from_linklib_cmd,
+[lt_cv_sharedlib_from_linklib_cmd='unknown'
+
+case $host_os in
+cygwin* | mingw* | pw32* | cegcc*)
+  # two different shell functions defined in ltmain.sh;
+  # decide which one to use based on capabilities of $DLLTOOL
+  case `$DLLTOOL --help 2>&1` in
+  *--identify-strict*)
+    lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib
+    ;;
+  *)
+    lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib_fallback
+    ;;
+  esac
+  ;;
+*)
+  # fallback: assume linklib IS sharedlib
+  lt_cv_sharedlib_from_linklib_cmd=$ECHO
+  ;;
+esac
+])
+sharedlib_from_linklib_cmd=$lt_cv_sharedlib_from_linklib_cmd
+test -z "$sharedlib_from_linklib_cmd" && sharedlib_from_linklib_cmd=$ECHO
+
+_LT_DECL([], [sharedlib_from_linklib_cmd], [1],
+    [Command to associate shared and link libraries])
+])# _LT_CHECK_SHAREDLIB_FROM_LINKLIB
+
+
+# _LT_PATH_MANIFEST_TOOL
+# ----------------------
+# locate the manifest tool
+m4_defun([_LT_PATH_MANIFEST_TOOL],
+[AC_CHECK_TOOL(MANIFEST_TOOL, mt, :)
+test -z "$MANIFEST_TOOL" && MANIFEST_TOOL=mt
+AC_CACHE_CHECK([if $MANIFEST_TOOL is a manifest tool], 
[lt_cv_path_mainfest_tool],
+  [lt_cv_path_mainfest_tool=no
+  echo "$as_me:$LINENO: $MANIFEST_TOOL '-?'" >&AS_MESSAGE_LOG_FD
+  $MANIFEST_TOOL '-?' 2>conftest.err > conftest.out
+  cat conftest.err >&AS_MESSAGE_LOG_FD
+  if $GREP 'Manifest Tool' conftest.out > /dev/null; then
+    lt_cv_path_mainfest_tool=yes
+  fi
+  rm -f conftest*])
+if test yes != "$lt_cv_path_mainfest_tool"; then
+  MANIFEST_TOOL=:
+fi
+_LT_DECL([], [MANIFEST_TOOL], [1], [Manifest tool])dnl
+])# _LT_PATH_MANIFEST_TOOL
+
+
+# _LT_DLL_DEF_P([FILE])
+# ---------------------
+# True iff FILE is a Windows DLL '.def' file.
+# Keep in sync with func_dll_def_p in the libtool script
+AC_DEFUN([_LT_DLL_DEF_P],
+[dnl
+  test DEF = "`$SED -n dnl
+    -e '\''s/^[[        ]]*//'\'' dnl Strip leading whitespace
+    -e '\''/^\(;.*\)*$/d'\'' dnl      Delete empty lines and comments
+    -e '\''s/^\(EXPORTS\|LIBRARY\)\([[  ]].*\)*$/DEF/p'\'' dnl
+    -e q dnl                          Only consider the first "real" line
+    $1`" dnl
+])# _LT_DLL_DEF_P
+
+
+# LT_LIB_M
+# --------
+# check for math library
+AC_DEFUN([LT_LIB_M],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+LIBM=
+case $host in
+*-*-beos* | *-*-cegcc* | *-*-cygwin* | *-*-haiku* | *-*-pw32* | *-*-darwin*)
+  # These system don't have libm, or don't need it
+  ;;
+*-ncr-sysv4.3*)
+  AC_CHECK_LIB(mw, _mwvalidcheckl, LIBM=-lmw)
+  AC_CHECK_LIB(m, cos, LIBM="$LIBM -lm")
+  ;;
+*)
+  AC_CHECK_LIB(m, cos, LIBM=-lm)
+  ;;
+esac
+AC_SUBST([LIBM])
+])# LT_LIB_M
+
+# Old name:
+AU_ALIAS([AC_CHECK_LIBM], [LT_LIB_M])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_CHECK_LIBM], [])
+
+
+# _LT_COMPILER_NO_RTTI([TAGNAME])
+# -------------------------------
+m4_defun([_LT_COMPILER_NO_RTTI],
+[m4_require([_LT_TAG_COMPILER])dnl
+
+_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=
+
+if test yes = "$GCC"; then
+  case $cc_basename in
+  nvcc*)
+    _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -Xcompiler 
-fno-builtin' ;;
+  *)
+    _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' ;;
+  esac
+
+  _LT_COMPILER_OPTION([if $compiler supports -fno-rtti -fno-exceptions],
+    lt_cv_prog_compiler_rtti_exceptions,
+    [-fno-rtti -fno-exceptions], [],
+    [_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, 
$1)="$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1) -fno-rtti 
-fno-exceptions"])
+fi
+_LT_TAGDECL([no_builtin_flag], [lt_prog_compiler_no_builtin_flag], [1],
+       [Compiler flag to turn off builtin functions])
+])# _LT_COMPILER_NO_RTTI
+
+
+# _LT_CMD_GLOBAL_SYMBOLS
+# ----------------------
+m4_defun([_LT_CMD_GLOBAL_SYMBOLS],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+AC_REQUIRE([AC_PROG_CC])dnl
+AC_REQUIRE([AC_PROG_AWK])dnl
+AC_REQUIRE([LT_PATH_NM])dnl
+AC_REQUIRE([LT_PATH_LD])dnl
+m4_require([_LT_DECL_SED])dnl
+m4_require([_LT_DECL_EGREP])dnl
+m4_require([_LT_TAG_COMPILER])dnl
+
+# Check for command to grab the raw symbol name followed by C symbol from nm.
+AC_MSG_CHECKING([command to parse $NM output from $compiler object])
+AC_CACHE_VAL([lt_cv_sys_global_symbol_pipe],
+[
+# These are sane defaults that work on at least a few old systems.
+# [They come from Ultrix.  What could be older than Ultrix?!! ;)]
+
+# Character class describing NM global symbol codes.
+symcode='[[BCDEGRST]]'
+
+# Regexp to match symbols that can be accessed directly from C.
+sympat='\([[_A-Za-z]][[_A-Za-z0-9]]*\)'
+
+# Define system-specific variables.
+case $host_os in
+aix*)
+  symcode='[[BCDT]]'
+  ;;
+cygwin* | mingw* | pw32* | cegcc*)
+  symcode='[[ABCDGISTW]]'
+  ;;
+hpux*)
+  if test ia64 = "$host_cpu"; then
+    symcode='[[ABCDEGRST]]'
+  fi
+  ;;
+irix* | nonstopux*)
+  symcode='[[BCDEGRST]]'
+  ;;
+osf*)
+  symcode='[[BCDEGQRST]]'
+  ;;
+solaris*)
+  symcode='[[BDRT]]'
+  ;;
+sco3.2v5*)
+  symcode='[[DT]]'
+  ;;
+sysv4.2uw2*)
+  symcode='[[DT]]'
+  ;;
+sysv5* | sco5v6* | unixware* | OpenUNIX*)
+  symcode='[[ABDT]]'
+  ;;
+sysv4)
+  symcode='[[DFNSTU]]'
+  ;;
+esac
+
+# If we're using GNU nm, then use its standard symbol codes.
+case `$NM -V 2>&1` in
+*GNU* | *'with BFD'*)
+  symcode='[[ABCDGIRSTW]]' ;;
+esac
+
+if test "$lt_cv_nm_interface" = "MS dumpbin"; then
+  # Gets list of data symbols to import.
+  lt_cv_sys_global_symbol_to_import="sed -n -e 's/^I .* \(.*\)$/\1/p'"
+  # Adjust the below global symbol transforms to fixup imported variables.
+  lt_cdecl_hook=" -e 's/^I .* \(.*\)$/extern __declspec(dllimport) char \1;/p'"
+  lt_c_name_hook=" -e 's/^I .* \(.*\)$/  {\"\1\", (void *) 0},/p'"
+  lt_c_name_lib_hook="\
+  -e 's/^I .* \(lib.*\)$/  {\"\1\", (void *) 0},/p'\
+  -e 's/^I .* \(.*\)$/  {\"lib\1\", (void *) 0},/p'"
+else
+  # Disable hooks by default.
+  lt_cv_sys_global_symbol_to_import=
+  lt_cdecl_hook=
+  lt_c_name_hook=
+  lt_c_name_lib_hook=
+fi
+
+# Transform an extracted symbol line into a proper C declaration.
+# Some systems (esp. on ia64) link data and code symbols differently,
+# so use this general approach.
+lt_cv_sys_global_symbol_to_cdecl="sed -n"\
+$lt_cdecl_hook\
+" -e 's/^T .* \(.*\)$/extern int \1();/p'"\
+" -e 's/^$symcode$symcode* .* \(.*\)$/extern char \1;/p'"
+
+# Transform an extracted symbol line into symbol name and symbol address
+lt_cv_sys_global_symbol_to_c_name_address="sed -n"\
+$lt_c_name_hook\
+" -e 's/^: \(.*\) .*$/  {\"\1\", (void *) 0},/p'"\
+" -e 's/^$symcode$symcode* .* \(.*\)$/  {\"\1\", (void *) \&\1},/p'"
+
+# Transform an extracted symbol line into symbol name with lib prefix and
+# symbol address.
+lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n"\
+$lt_c_name_lib_hook\
+" -e 's/^: \(.*\) .*$/  {\"\1\", (void *) 0},/p'"\
+" -e 's/^$symcode$symcode* .* \(lib.*\)$/  {\"\1\", (void *) \&\1},/p'"\
+" -e 's/^$symcode$symcode* .* \(.*\)$/  {\"lib\1\", (void *) \&\1},/p'"
+
+# Handle CRLF in mingw tool chain
+opt_cr=
+case $build_os in
+mingw*)
+  opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp
+  ;;
+esac
+
+# Try without a prefix underscore, then with it.
+for ac_symprfx in "" "_"; do
+
+  # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol.
+  symxfrm="\\1 $ac_symprfx\\2 \\2"
+
+  # Write the raw and C identifiers.
+  if test "$lt_cv_nm_interface" = "MS dumpbin"; then
+    # Fake it for dumpbin and say T for any non-static function,
+    # D for any global variable and I for any imported variable.
+    # Also find C++ and __fastcall symbols from MSVC++,
+    # which start with @ or ?.
+    lt_cv_sys_global_symbol_pipe="$AWK ['"\
+"     {last_section=section; section=\$ 3};"\
+"     /^COFF SYMBOL TABLE/{for(i in hide) delete hide[i]};"\
+"     /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\
+"     /^ *Symbol name *: /{split(\$ 0,sn,\":\"); si=substr(sn[2],2)};"\
+"     /^ *Type *: code/{print \"T\",si,substr(si,length(prfx))};"\
+"     /^ *Type *: data/{print \"I\",si,substr(si,length(prfx))};"\
+"     \$ 0!~/External *\|/{next};"\
+"     / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\
+"     {if(hide[section]) next};"\
+"     {f=\"D\"}; \$ 0~/\(\).*\|/{f=\"T\"};"\
+"     {split(\$ 0,a,/\||\r/); split(a[2],s)};"\
+"     s[1]~/address@hidden/{print f,s[1],s[1]; next};"\
+"     s[1]~prfx {split(s[1],t,\"@\"); print f,t[1],substr(t[1],length(prfx))}"\
+"     ' prfx=^$ac_symprfx]"
+  else
+    lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[[    
]]\($symcode$symcode*\)[[       ]][[    
]]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'"
+  fi
+  lt_cv_sys_global_symbol_pipe="$lt_cv_sys_global_symbol_pipe | sed '/ 
__gnu_lto/d'"
+
+  # Check to see that the pipe works correctly.
+  pipe_works=no
+
+  rm -f conftest*
+  cat > conftest.$ac_ext <<_LT_EOF
+#ifdef __cplusplus
+extern "C" {
+#endif
+char nm_test_var;
+void nm_test_func(void);
+void nm_test_func(void){}
+#ifdef __cplusplus
+}
+#endif
+int main(){nm_test_var='a';nm_test_func();return(0);}
+_LT_EOF
+
+  if AC_TRY_EVAL(ac_compile); then
+    # Now try to grab the symbols.
+    nlist=conftest.nm
+    if AC_TRY_EVAL(NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" 
\> $nlist) && test -s "$nlist"; then
+      # Try sorting and uniquifying the output.
+      if sort "$nlist" | uniq > "$nlist"T; then
+       mv -f "$nlist"T "$nlist"
+      else
+       rm -f "$nlist"T
+      fi
+
+      # Make sure that we snagged all the symbols we need.
+      if $GREP ' nm_test_var$' "$nlist" >/dev/null; then
+       if $GREP ' nm_test_func$' "$nlist" >/dev/null; then
+         cat <<_LT_EOF > conftest.$ac_ext
+/* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests.  
*/
+#if defined _WIN32 || defined __CYGWIN__ || defined _WIN32_WCE
+/* DATA imports from DLLs on WIN32 can't be const, because runtime
+   relocations are performed -- see ld's documentation on pseudo-relocs.  */
+# define LT@&address@hidden
+#elif defined __osf__
+/* This system does not cope well with relocations in const data.  */
+# define LT@&address@hidden
+#else
+# define LT@&address@hidden const
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+_LT_EOF
+         # Now generate the symbol file.
+         eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main 
>> conftest.$ac_ext'
+
+         cat <<_LT_EOF >> conftest.$ac_ext
+
+/* The mapping between symbol names and symbols.  */
+LT@&address@hidden struct {
+  const char *name;
+  void       *address;
+}
+lt__PROGRAM__LTX_preloaded_symbols[[]] =
+{
+  { "@PROGRAM@", (void *) 0 },
+_LT_EOF
+         $SED "s/^$symcode$symcode* .* \(.*\)$/  {\"\1\", (void *) \&\1},/" < 
"$nlist" | $GREP -v main >> conftest.$ac_ext
+         cat <<\_LT_EOF >> conftest.$ac_ext
+  {0, (void *) 0}
+};
+
+/* This works around a problem in FreeBSD linker */
+#ifdef FREEBSD_WORKAROUND
+static const void *lt_preloaded_setup() {
+  return lt__PROGRAM__LTX_preloaded_symbols;
+}
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+_LT_EOF
+         # Now try linking the two files.
+         mv conftest.$ac_objext conftstm.$ac_objext
+         lt_globsym_save_LIBS=$LIBS
+         lt_globsym_save_CFLAGS=$CFLAGS
+         LIBS=conftstm.$ac_objext
+         CFLAGS="$CFLAGS$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)"
+         if AC_TRY_EVAL(ac_link) && test -s conftest$ac_exeext; then
+           pipe_works=yes
+         fi
+         LIBS=$lt_globsym_save_LIBS
+         CFLAGS=$lt_globsym_save_CFLAGS
+       else
+         echo "cannot find nm_test_func in $nlist" >&AS_MESSAGE_LOG_FD
+       fi
+      else
+       echo "cannot find nm_test_var in $nlist" >&AS_MESSAGE_LOG_FD
+      fi
+    else
+      echo "cannot run $lt_cv_sys_global_symbol_pipe" >&AS_MESSAGE_LOG_FD
+    fi
+  else
+    echo "$progname: failed program was:" >&AS_MESSAGE_LOG_FD
+    cat conftest.$ac_ext >&5
+  fi
+  rm -rf conftest* conftst*
+
+  # Do not use the global_symbol_pipe unless it works.
+  if test yes = "$pipe_works"; then
+    break
+  else
+    lt_cv_sys_global_symbol_pipe=
+  fi
+done
+])
+if test -z "$lt_cv_sys_global_symbol_pipe"; then
+  lt_cv_sys_global_symbol_to_cdecl=
+fi
+if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; 
then
+  AC_MSG_RESULT(failed)
+else
+  AC_MSG_RESULT(ok)
+fi
+
+# Response file support.
+if test "$lt_cv_nm_interface" = "MS dumpbin"; then
+  nm_file_list_spec='@'
+elif $NM --help 2>/dev/null | grep 'address@hidden' >/dev/null; then
+  nm_file_list_spec='@'
+fi
+
+_LT_DECL([global_symbol_pipe], [lt_cv_sys_global_symbol_pipe], [1],
+    [Take the output of nm and produce a listing of raw symbols and C names])
+_LT_DECL([global_symbol_to_cdecl], [lt_cv_sys_global_symbol_to_cdecl], [1],
+    [Transform the output of nm in a proper C declaration])
+_LT_DECL([global_symbol_to_import], [lt_cv_sys_global_symbol_to_import], [1],
+    [Transform the output of nm into a list of symbols to manually relocate])
+_LT_DECL([global_symbol_to_c_name_address],
+    [lt_cv_sys_global_symbol_to_c_name_address], [1],
+    [Transform the output of nm in a C name address pair])
+_LT_DECL([global_symbol_to_c_name_address_lib_prefix],
+    [lt_cv_sys_global_symbol_to_c_name_address_lib_prefix], [1],
+    [Transform the output of nm in a C name address pair when lib prefix is 
needed])
+_LT_DECL([nm_interface], [lt_cv_nm_interface], [1],
+    [The name lister interface])
+_LT_DECL([], [nm_file_list_spec], [1],
+    [Specify filename containing input files for $NM])
+]) # _LT_CMD_GLOBAL_SYMBOLS
+
+
+# _LT_COMPILER_PIC([TAGNAME])
+# ---------------------------
+m4_defun([_LT_COMPILER_PIC],
+[m4_require([_LT_TAG_COMPILER])dnl
+_LT_TAGVAR(lt_prog_compiler_wl, $1)=
+_LT_TAGVAR(lt_prog_compiler_pic, $1)=
+_LT_TAGVAR(lt_prog_compiler_static, $1)=
+
+m4_if([$1], [CXX], [
+  # C++ specific cases for pic, static, wl, etc.
+  if test yes = "$GXX"; then
+    _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+    _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+
+    case $host_os in
+    aix*)
+      # All AIX code is PIC.
+      if test ia64 = "$host_cpu"; then
+       # AIX 5 now supports IA64 processor
+       _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+      fi
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+      ;;
+
+    amigaos*)
+      case $host_cpu in
+      powerpc)
+            # see comment about AmigaOS4 .so support
+            _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+        ;;
+      m68k)
+            # FIXME: we need at least 68020 code to build shared libraries, but
+            # adding the '-m68020' flag to GCC prevents building anything 
better,
+            # like '-m68040'.
+            _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 
-malways-restore-a4'
+        ;;
+      esac
+      ;;
+
+    beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*)
+      # PIC is the default for these OSes.
+      ;;
+    mingw* | cygwin* | os2* | pw32* | cegcc*)
+      # This hack is so that the source file can tell whether it is being
+      # built for inclusion in a dll (and should export symbols for example).
+      # Although the cygwin gcc ignores -fPIC, still need this for old-style
+      # (--disable-auto-import) libraries
+      m4_if([$1], [GCJ], [],
+       [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT'])
+      case $host_os in
+      os2*)
+       _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-static'
+       ;;
+      esac
+      ;;
+    darwin* | rhapsody*)
+      # PIC is the default on this platform
+      # Common symbols not allowed in MH_DYLIB files
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common'
+      ;;
+    *djgpp*)
+      # DJGPP does not support shared libraries at all
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)=
+      ;;
+    haiku*)
+      # PIC is the default for Haiku.
+      # The "-static" flag exists, but is broken.
+      _LT_TAGVAR(lt_prog_compiler_static, $1)=
+      ;;
+    interix[[3-9]]*)
+      # Interix 3.x gcc -fpic/-fPIC options generate broken code.
+      # Instead, we relocate shared libraries at runtime.
+      ;;
+    sysv4*MP*)
+      if test -d /usr/nec; then
+       _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic
+      fi
+      ;;
+    hpux*)
+      # PIC is the default for 64-bit PA HP-UX, but not for 32-bit
+      # PA HP-UX.  On IA64 HP-UX, PIC is the default but the pic flag
+      # sets the default TLS model and affects inlining.
+      case $host_cpu in
+      hppa*64*)
+       ;;
+      *)
+       _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+       ;;
+      esac
+      ;;
+    *qnx* | *nto*)
+      # QNX uses GNU C++, but need to define -shared option too, otherwise
+      # it will coredump.
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared'
+      ;;
+    *)
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+      ;;
+    esac
+  else
+    case $host_os in
+      aix[[4-9]]*)
+       # All AIX code is PIC.
+       if test ia64 = "$host_cpu"; then
+         # AIX 5 now supports IA64 processor
+         _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+       else
+         _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp'
+       fi
+       ;;
+      chorus*)
+       case $cc_basename in
+       cxch68*)
+         # Green Hills C++ Compiler
+         # _LT_TAGVAR(lt_prog_compiler_static, $1)="--no_auto_instantiation -u 
__main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a 
$MVME_DIR/lib/classix/libcx.s.a"
+         ;;
+       esac
+       ;;
+      mingw* | cygwin* | os2* | pw32* | cegcc*)
+       # This hack is so that the source file can tell whether it is being
+       # built for inclusion in a dll (and should export symbols for example).
+       m4_if([$1], [GCJ], [],
+         [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT'])
+       ;;
+      dgux*)
+       case $cc_basename in
+         ec++*)
+           _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+           ;;
+         ghcx*)
+           # Green Hills C++ Compiler
+           _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
+           ;;
+         *)
+           ;;
+       esac
+       ;;
+      freebsd* | dragonfly*)
+       # FreeBSD uses GNU C++
+       ;;
+      hpux9* | hpux10* | hpux11*)
+       case $cc_basename in
+         CC*)
+           _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+           _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-a ${wl}archive'
+           if test ia64 != "$host_cpu"; then
+             _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z'
+           fi
+           ;;
+         aCC*)
+           _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+           _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-a ${wl}archive'
+           case $host_cpu in
+           hppa*64*|ia64*)
+             # +Z the default
+             ;;
+           *)
+             _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z'
+             ;;
+           esac
+           ;;
+         *)
+           ;;
+       esac
+       ;;
+      interix*)
+       # This is c89, which is MS Visual C++ (no shared libs)
+       # Anyone wants to do a port?
+       ;;
+      irix5* | irix6* | nonstopux*)
+       case $cc_basename in
+         CC*)
+           _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+           _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+           # CC pic flag -KPIC is the default.
+           ;;
+         *)
+           ;;
+       esac
+       ;;
+      linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
+       case $cc_basename in
+         KCC*)
+           # KAI C++ Compiler
+           _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,'
+           _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+           ;;
+         ecpc* )
+           # old Intel C++ for x86_64, which still supported -KPIC.
+           _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+           _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+           _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+           ;;
+         icpc* )
+           # Intel C++, used to be incompatible with GCC.
+           # ICC 10 doesn't accept -KPIC any more.
+           _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+           _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+           _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+           ;;
+         pgCC* | pgcpp*)
+           # Portland Group C++ compiler
+           _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+           _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic'
+           _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+           ;;
+         cxx*)
+           # Compaq C++
+           # Make sure the PIC flag is empty.  It appears that all Alpha
+           # Linux and Compaq Tru64 Unix objects are PIC.
+           _LT_TAGVAR(lt_prog_compiler_pic, $1)=
+           _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+           ;;
+         xlc* | xlC* | bgxl[[cC]]* | mpixl[[cC]]*)
+           # IBM XL 8.0, 9.0 on PPC and BlueGene
+           _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+           _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic'
+           _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink'
+           ;;
+         *)
+           case `$CC -V 2>&1 | sed 5q` in
+           *Sun\ C*)
+             # Sun C++ 5.9
+             _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+             _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+             _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld '
+             ;;
+           esac
+           ;;
+       esac
+       ;;
+      lynxos*)
+       ;;
+      m88k*)
+       ;;
+      mvs*)
+       case $cc_basename in
+         cxx*)
+           _LT_TAGVAR(lt_prog_compiler_pic, $1)='-W c,exportall'
+           ;;
+         *)
+           ;;
+       esac
+       ;;
+      netbsd* | netbsdelf*-gnu)
+       ;;
+      *qnx* | *nto*)
+        # QNX uses GNU C++, but need to define -shared option too, otherwise
+        # it will coredump.
+        _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared'
+        ;;
+      osf3* | osf4* | osf5*)
+       case $cc_basename in
+         KCC*)
+           _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,'
+           ;;
+         RCC*)
+           # Rational C++ 2.4.1
+           _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
+           ;;
+         cxx*)
+           # Digital/Compaq C++
+           _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+           # Make sure the PIC flag is empty.  It appears that all Alpha
+           # Linux and Compaq Tru64 Unix objects are PIC.
+           _LT_TAGVAR(lt_prog_compiler_pic, $1)=
+           _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+           ;;
+         *)
+           ;;
+       esac
+       ;;
+      psos*)
+       ;;
+      solaris*)
+       case $cc_basename in
+         CC* | sunCC*)
+           # Sun C++ 4.2, 5.x and Centerline C++
+           _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+           _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+           _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld '
+           ;;
+         gcx*)
+           # Green Hills C++ Compiler
+           _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC'
+           ;;
+         *)
+           ;;
+       esac
+       ;;
+      sunos4*)
+       case $cc_basename in
+         CC*)
+           # Sun C++ 4.x
+           _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
+           _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+           ;;
+         lcc*)
+           # Lucid
+           _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
+           ;;
+         *)
+           ;;
+       esac
+       ;;
+      sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*)
+       case $cc_basename in
+         CC*)
+           _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+           _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+           _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+           ;;
+       esac
+       ;;
+      tandem*)
+       case $cc_basename in
+         NCC*)
+           # NonStop-UX NCC 3.20
+           _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+           ;;
+         *)
+           ;;
+       esac
+       ;;
+      vxworks*)
+       ;;
+      *)
+       _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no
+       ;;
+    esac
+  fi
+],
+[
+  if test yes = "$GCC"; then
+    _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+    _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+
+    case $host_os in
+      aix*)
+      # All AIX code is PIC.
+      if test ia64 = "$host_cpu"; then
+       # AIX 5 now supports IA64 processor
+       _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+      fi
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+      ;;
+
+    amigaos*)
+      case $host_cpu in
+      powerpc)
+            # see comment about AmigaOS4 .so support
+            _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+        ;;
+      m68k)
+            # FIXME: we need at least 68020 code to build shared libraries, but
+            # adding the '-m68020' flag to GCC prevents building anything 
better,
+            # like '-m68040'.
+            _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 
-malways-restore-a4'
+        ;;
+      esac
+      ;;
+
+    beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*)
+      # PIC is the default for these OSes.
+      ;;
+
+    mingw* | cygwin* | pw32* | os2* | cegcc*)
+      # This hack is so that the source file can tell whether it is being
+      # built for inclusion in a dll (and should export symbols for example).
+      # Although the cygwin gcc ignores -fPIC, still need this for old-style
+      # (--disable-auto-import) libraries
+      m4_if([$1], [GCJ], [],
+       [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT'])
+      case $host_os in
+      os2*)
+       _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-static'
+       ;;
+      esac
+      ;;
+
+    darwin* | rhapsody*)
+      # PIC is the default on this platform
+      # Common symbols not allowed in MH_DYLIB files
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common'
+      ;;
+
+    haiku*)
+      # PIC is the default for Haiku.
+      # The "-static" flag exists, but is broken.
+      _LT_TAGVAR(lt_prog_compiler_static, $1)=
+      ;;
+
+    hpux*)
+      # PIC is the default for 64-bit PA HP-UX, but not for 32-bit
+      # PA HP-UX.  On IA64 HP-UX, PIC is the default but the pic flag
+      # sets the default TLS model and affects inlining.
+      case $host_cpu in
+      hppa*64*)
+       # +Z the default
+       ;;
+      *)
+       _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+       ;;
+      esac
+      ;;
+
+    interix[[3-9]]*)
+      # Interix 3.x gcc -fpic/-fPIC options generate broken code.
+      # Instead, we relocate shared libraries at runtime.
+      ;;
+
+    msdosdjgpp*)
+      # Just because we use GCC doesn't mean we suddenly get shared libraries
+      # on systems that don't support them.
+      _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no
+      enable_shared=no
+      ;;
+
+    *nto* | *qnx*)
+      # QNX uses GNU C++, but need to define -shared option too, otherwise
+      # it will coredump.
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared'
+      ;;
+
+    sysv4*MP*)
+      if test -d /usr/nec; then
+       _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic
+      fi
+      ;;
+
+    *)
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+      ;;
+    esac
+
+    case $cc_basename in
+    nvcc*) # Cuda Compiler Driver 2.2
+      _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Xlinker '
+      if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then
+        _LT_TAGVAR(lt_prog_compiler_pic, $1)="-Xcompiler 
$_LT_TAGVAR(lt_prog_compiler_pic, $1)"
+      fi
+      ;;
+    esac
+  else
+    # PORTME Check for flag to pass linker flags through the system compiler.
+    case $host_os in
+    aix*)
+      _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+      if test ia64 = "$host_cpu"; then
+       # AIX 5 now supports IA64 processor
+       _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+      else
+       _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp'
+      fi
+      ;;
+
+    darwin* | rhapsody*)
+      # PIC is the default on this platform
+      # Common symbols not allowed in MH_DYLIB files
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common'
+      case $cc_basename in
+      nagfor*)
+        # NAG Fortran compiler
+        _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,-Wl,,'
+        _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC'
+        _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+        ;;
+      esac
+      ;;
+
+    mingw* | cygwin* | pw32* | os2* | cegcc*)
+      # This hack is so that the source file can tell whether it is being
+      # built for inclusion in a dll (and should export symbols for example).
+      m4_if([$1], [GCJ], [],
+       [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT'])
+      case $host_os in
+      os2*)
+       _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-static'
+       ;;
+      esac
+      ;;
+
+    hpux9* | hpux10* | hpux11*)
+      _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+      # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but
+      # not for PA HP-UX.
+      case $host_cpu in
+      hppa*64*|ia64*)
+       # +Z the default
+       ;;
+      *)
+       _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z'
+       ;;
+      esac
+      # Is there a better lt_prog_compiler_static that works with the bundled 
CC?
+      _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-a ${wl}archive'
+      ;;
+
+    irix5* | irix6* | nonstopux*)
+      _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+      # PIC (with -KPIC) is the default.
+      _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+      ;;
+
+    linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
+      case $cc_basename in
+      # old Intel for x86_64, which still supported -KPIC.
+      ecc*)
+       _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+       _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+       _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+        ;;
+      # icc used to be incompatible with GCC.
+      # ICC 10 doesn't accept -KPIC any more.
+      icc* | ifort*)
+       _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+       _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+       _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+        ;;
+      # Lahey Fortran 8.1.
+      lf95*)
+       _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+       _LT_TAGVAR(lt_prog_compiler_pic, $1)='--shared'
+       _LT_TAGVAR(lt_prog_compiler_static, $1)='--static'
+       ;;
+      nagfor*)
+       # NAG Fortran compiler
+       _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,-Wl,,'
+       _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC'
+       _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+       ;;
+      tcc*)
+       # Fabrice Bellard et al's Tiny C Compiler
+       _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+       _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+       _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+       ;;
+      pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*)
+        # Portland Group compilers (*not* the Pentium gcc compiler,
+       # which looks to be a dead project)
+       _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+       _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic'
+       _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+        ;;
+      ccc*)
+        _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+        # All Alpha code is PIC.
+        _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+        ;;
+      xl* | bgxl* | bgf* | mpixl*)
+       # IBM XL C 8.0/Fortran 10.1, 11.1 on PPC and BlueGene
+       _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+       _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic'
+       _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink'
+       ;;
+      *)
+       case `$CC -V 2>&1 | sed 5q` in
+       *Sun\ Ceres\ Fortran* | *Sun*Fortran*\ [[1-7]].* | *Sun*Fortran*\ 
8.[[0-3]]*)
+         # Sun Fortran 8.3 passes all unrecognized flags to the linker
+         _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+         _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+         _LT_TAGVAR(lt_prog_compiler_wl, $1)=''
+         ;;
+       *Sun\ F* | *Sun*Fortran*)
+         _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+         _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+         _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld '
+         ;;
+       *Sun\ C*)
+         # Sun C 5.9
+         _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+         _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+         _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+         ;;
+        *Intel*\ [[CF]]*Compiler*)
+         _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+         _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+         _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+         ;;
+       *Portland\ Group*)
+         _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+         _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic'
+         _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+         ;;
+       esac
+       ;;
+      esac
+      ;;
+
+    newsos6)
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+      _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+      ;;
+
+    *nto* | *qnx*)
+      # QNX uses GNU C++, but need to define -shared option too, otherwise
+      # it will coredump.
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared'
+      ;;
+
+    osf3* | osf4* | osf5*)
+      _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+      # All OSF/1 code is PIC.
+      _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+      ;;
+
+    rdos*)
+      _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+      ;;
+
+    solaris*)
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+      _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+      case $cc_basename in
+      f77* | f90* | f95* | sunf77* | sunf90* | sunf95*)
+       _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ';;
+      *)
+       _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,';;
+      esac
+      ;;
+
+    sunos4*)
+      _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld '
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC'
+      _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+      ;;
+
+    sysv4 | sysv4.2uw2* | sysv4.3*)
+      _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+      _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+      ;;
+
+    sysv4*MP*)
+      if test -d /usr/nec; then
+       _LT_TAGVAR(lt_prog_compiler_pic, $1)='-Kconform_pic'
+       _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+      fi
+      ;;
+
+    sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*)
+      _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+      _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+      ;;
+
+    unicos*)
+      _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+      _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no
+      ;;
+
+    uts4*)
+      _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
+      _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+      ;;
+
+    *)
+      _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no
+      ;;
+    esac
+  fi
+])
+case $host_os in
+  # For platforms that do not support PIC, -DPIC is meaningless:
+  *djgpp*)
+    _LT_TAGVAR(lt_prog_compiler_pic, $1)=
+    ;;
+  *)
+    _LT_TAGVAR(lt_prog_compiler_pic, $1)="$_LT_TAGVAR(lt_prog_compiler_pic, 
$1)@&address@hidden([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])"
+    ;;
+esac
+
+AC_CACHE_CHECK([for $compiler option to produce PIC],
+  [_LT_TAGVAR(lt_cv_prog_compiler_pic, $1)],
+  [_LT_TAGVAR(lt_cv_prog_compiler_pic, $1)=$_LT_TAGVAR(lt_prog_compiler_pic, 
$1)])
+_LT_TAGVAR(lt_prog_compiler_pic, $1)=$_LT_TAGVAR(lt_cv_prog_compiler_pic, $1)
+
+#
+# Check to make sure the PIC flag actually works.
+#
+if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then
+  _LT_COMPILER_OPTION([if $compiler PIC flag $_LT_TAGVAR(lt_prog_compiler_pic, 
$1) works],
+    [_LT_TAGVAR(lt_cv_prog_compiler_pic_works, $1)],
+    [$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&address@hidden([$1],[],[ 
-DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])], [],
+    [case $_LT_TAGVAR(lt_prog_compiler_pic, $1) in
+     "" | " "*) ;;
+     *) _LT_TAGVAR(lt_prog_compiler_pic, $1)=" 
$_LT_TAGVAR(lt_prog_compiler_pic, $1)" ;;
+     esac],
+    [_LT_TAGVAR(lt_prog_compiler_pic, $1)=
+     _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no])
+fi
+_LT_TAGDECL([pic_flag], [lt_prog_compiler_pic], [1],
+       [Additional compiler flags for building library objects])
+
+_LT_TAGDECL([wl], [lt_prog_compiler_wl], [1],
+       [How to pass a linker flag through the compiler])
+#
+# Check to make sure the static flag actually works.
+#
+wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) eval 
lt_tmp_static_flag=\"$_LT_TAGVAR(lt_prog_compiler_static, $1)\"
+_LT_LINKER_OPTION([if $compiler static flag $lt_tmp_static_flag works],
+  _LT_TAGVAR(lt_cv_prog_compiler_static_works, $1),
+  $lt_tmp_static_flag,
+  [],
+  [_LT_TAGVAR(lt_prog_compiler_static, $1)=])
+_LT_TAGDECL([link_static_flag], [lt_prog_compiler_static], [1],
+       [Compiler flag to prevent dynamic linking])
+])# _LT_COMPILER_PIC
+
+
+# _LT_LINKER_SHLIBS([TAGNAME])
+# ----------------------------
+# See if the linker supports building shared libraries.
+m4_defun([_LT_LINKER_SHLIBS],
+[AC_REQUIRE([LT_PATH_LD])dnl
+AC_REQUIRE([LT_PATH_NM])dnl
+m4_require([_LT_PATH_MANIFEST_TOOL])dnl
+m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_DECL_EGREP])dnl
+m4_require([_LT_DECL_SED])dnl
+m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl
+m4_require([_LT_TAG_COMPILER])dnl
+AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries])
+m4_if([$1], [CXX], [
+  _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | 
$global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
+  _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*']
+  case $host_os in
+  aix[[4-9]]*)
+    # If we're using GNU nm, then we don't want the "-C" option.
+    # -C means demangle to GNU nm, but means don't demangle to AIX nm.
+    # Without the "-l" option, or with the "-B" option, AIX nm treats
+    # weak defined symbols like other global defined symbols, whereas
+    # GNU nm marks them as "W".
+    # While the 'weak' keyword is ignored in the Export File, we need
+    # it in the Import File for the 'aix-soname' feature, so we have
+    # to replace the "-B" option with "-P" for AIX nm.
+    if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then
+      _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | 
awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == 
"W")) && ([substr](\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } 
else { print \$ 3 } } }'\'' | sort -u > $export_symbols'
+    else
+      _LT_TAGVAR(export_symbols_cmds, $1)='`func_echo_all $NM | $SED -e 
'\''s/B\([[^B]]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 
2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W") || (\$ 2 == "V") 
|| (\$ 2 == "Z")) && ([substr](\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 
== "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | 
sort -u > $export_symbols'
+    fi
+    ;;
+  pw32*)
+    _LT_TAGVAR(export_symbols_cmds, $1)=$ltdll_cmds
+    ;;
+  cygwin* | mingw* | cegcc*)
+    case $cc_basename in
+    cl*)
+      _LT_TAGVAR(exclude_expsyms, 
$1)='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*'
+      ;;
+    *)
+      _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | 
$global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 
DATA/;s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ 
]]/s/.* //'\'' | sort | uniq > $export_symbols'
+      _LT_TAGVAR(exclude_expsyms, 
$1)=['[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname']
+      ;;
+    esac
+    ;;
+  linux* | k*bsd*-gnu | gnu*)
+    _LT_TAGVAR(link_all_deplibs, $1)=no
+    ;;
+  *)
+    _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | 
$global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
+    ;;
+  esac
+], [
+  runpath_var=
+  _LT_TAGVAR(allow_undefined_flag, $1)=
+  _LT_TAGVAR(always_export_symbols, $1)=no
+  _LT_TAGVAR(archive_cmds, $1)=
+  _LT_TAGVAR(archive_expsym_cmds, $1)=
+  _LT_TAGVAR(compiler_needs_object, $1)=no
+  _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no
+  _LT_TAGVAR(export_dynamic_flag_spec, $1)=
+  _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | 
$global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
+  _LT_TAGVAR(hardcode_automatic, $1)=no
+  _LT_TAGVAR(hardcode_direct, $1)=no
+  _LT_TAGVAR(hardcode_direct_absolute, $1)=no
+  _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
+  _LT_TAGVAR(hardcode_libdir_separator, $1)=
+  _LT_TAGVAR(hardcode_minus_L, $1)=no
+  _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported
+  _LT_TAGVAR(inherit_rpath, $1)=no
+  _LT_TAGVAR(link_all_deplibs, $1)=unknown
+  _LT_TAGVAR(module_cmds, $1)=
+  _LT_TAGVAR(module_expsym_cmds, $1)=
+  _LT_TAGVAR(old_archive_from_new_cmds, $1)=
+  _LT_TAGVAR(old_archive_from_expsyms_cmds, $1)=
+  _LT_TAGVAR(thread_safe_flag_spec, $1)=
+  _LT_TAGVAR(whole_archive_flag_spec, $1)=
+  # include_expsyms should be a list of space-separated symbols to be *always*
+  # included in the symbol list
+  _LT_TAGVAR(include_expsyms, $1)=
+  # exclude_expsyms can be an extended regexp of symbols to exclude
+  # it will be wrapped by ' (' and ')$', so one must not match beginning or
+  # end of line.  Example: 'a|bc|.*d.*' will exclude the symbols 'a' and 'bc',
+  # as well as any symbol that contains 'd'.
+  _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*']
+  # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out
+  # platforms (ab)use it in PIC code, but their linkers get confused if
+  # the symbol is explicitly referenced.  Since portable code cannot
+  # rely on this symbol name, it's probably fine to never include it in
+  # preloaded symbol tables.
+  # Exclude shared library initialization/finalization symbols.
+dnl Note also adjust exclude_expsyms for C++ above.
+  extract_expsyms_cmds=
+
+  case $host_os in
+  cygwin* | mingw* | pw32* | cegcc*)
+    # FIXME: the MSVC++ port hasn't been tested in a loooong time
+    # When not using gcc, we currently assume that we are using
+    # Microsoft Visual C++.
+    if test yes != "$GCC"; then
+      with_gnu_ld=no
+    fi
+    ;;
+  interix*)
+    # we just hope/assume this is gcc and not c89 (= MSVC++)
+    with_gnu_ld=yes
+    ;;
+  openbsd* | bitrig*)
+    with_gnu_ld=no
+    ;;
+  linux* | k*bsd*-gnu | gnu*)
+    _LT_TAGVAR(link_all_deplibs, $1)=no
+    ;;
+  esac
+
+  _LT_TAGVAR(ld_shlibs, $1)=yes
+
+  # On some targets, GNU ld is compatible enough with the native linker
+  # that we're better off using the native interface for both.
+  lt_use_gnu_ld_interface=no
+  if test yes = "$with_gnu_ld"; then
+    case $host_os in
+      aix*)
+       # The AIX port of GNU ld has always aspired to compatibility
+       # with the native linker.  However, as the warning in the GNU ld
+       # block says, versions before 2.19.5* couldn't really create working
+       # shared libraries, regardless of the interface used.
+       case `$LD -v 2>&1` in
+         *\ \(GNU\ Binutils\)\ 2.19.5*) ;;
+         *\ \(GNU\ Binutils\)\ 2.[[2-9]]*) ;;
+         *\ \(GNU\ Binutils\)\ [[3-9]]*) ;;
+         *)
+           lt_use_gnu_ld_interface=yes
+           ;;
+       esac
+       ;;
+      *)
+       lt_use_gnu_ld_interface=yes
+       ;;
+    esac
+  fi
+
+  if test yes = "$lt_use_gnu_ld_interface"; then
+    # If archive_cmds runs LD, not CC, wlarc should be empty
+    wlarc='$wl'
+
+    # Set some defaults for GNU ld with shared library support. These
+    # are reset later if shared libraries are not supported. Putting them
+    # here allows them to be overridden if necessary.
+    runpath_var=LD_RUN_PATH
+    _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir'
+    _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic'
+    # ancient GNU ld didn't support --whole-archive et. al.
+    if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then
+      _LT_TAGVAR(whole_archive_flag_spec, 
$1)=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive'
+    else
+      _LT_TAGVAR(whole_archive_flag_spec, $1)=
+    fi
+    supports_anon_versioning=no
+    case `$LD -v | $SED -e 's/([^)]\+)\s\+//' 2>&1` in
+      *GNU\ gold*) supports_anon_versioning=yes ;;
+      *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.10.*) ;; # catch versions < 2.11
+      *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ...
+      *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ...
+      *\ 2.11.*) ;; # other 2.11 versions
+      *) supports_anon_versioning=yes ;;
+    esac
+
+    # See if GNU ld supports shared libraries.
+    case $host_os in
+    aix[[3-9]]*)
+      # On AIX/PPC, the GNU linker is very broken
+      if test ia64 != "$host_cpu"; then
+       _LT_TAGVAR(ld_shlibs, $1)=no
+       cat <<_LT_EOF 1>&2
+
+*** Warning: the GNU linker, at least up to release 2.19, is reported
+*** to be unable to reliably create shared libraries on AIX.
+*** Therefore, libtool is disabling shared libraries support.  If you
+*** really care for shared libraries, you may want to install binutils
+*** 2.20 or above, or modify your PATH so that a non-GNU linker is found.
+*** You will then need to restart the configuration process.
+
+_LT_EOF
+      fi
+      ;;
+
+    amigaos*)
+      case $host_cpu in
+      powerpc)
+            # see comment about AmigaOS4 .so support
+            _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs 
$compiler_flags $wl-soname $wl$soname -o $lib'
+            _LT_TAGVAR(archive_expsym_cmds, $1)=''
+        ;;
+      m68k)
+            _LT_TAGVAR(archive_cmds, $1)='$RM 
$output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > 
$output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> 
$output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> 
$output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> 
$output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd 
$output_objdir && a2ixlibrary -32)'
+            _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+            _LT_TAGVAR(hardcode_minus_L, $1)=yes
+        ;;
+      esac
+      ;;
+
+    beos*)
+      if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+       _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+       # Joseph Beckenbach <address@hidden> says some releases of gcc
+       # support --undefined.  This deserves some investigation.  FIXME
+       _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs 
$compiler_flags $wl-soname $wl$soname -o $lib'
+      else
+       _LT_TAGVAR(ld_shlibs, $1)=no
+      fi
+      ;;
+
+    cygwin* | mingw* | pw32* | cegcc*)
+      # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless,
+      # as there is no search path for DLLs.
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+      _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-all-symbols'
+      _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+      _LT_TAGVAR(always_export_symbols, $1)=no
+      _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
+      _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | 
$global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 
DATA/;s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ 
]]/s/.* //'\'' | sort | uniq > $export_symbols'
+      _LT_TAGVAR(exclude_expsyms, 
$1)=['[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname']
+
+      if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then
+        _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs 
$compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker 
--out-implib -Xlinker $lib'
+       # If the export-symbols file already is a .def file, use it as
+       # is; otherwise, prepend EXPORTS...
+       _LT_TAGVAR(archive_expsym_cmds, $1)='if 
_LT_DLL_DEF_P([$export_symbols]); then
+          cp $export_symbols $output_objdir/$soname.def;
+        else
+          echo EXPORTS > $output_objdir/$soname.def;
+          cat $export_symbols >> $output_objdir/$soname.def;
+        fi~
+        $CC -shared $output_objdir/$soname.def $libobjs $deplibs 
$compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker 
--out-implib -Xlinker $lib'
+      else
+       _LT_TAGVAR(ld_shlibs, $1)=no
+      fi
+      ;;
+
+    haiku*)
+      _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs 
$compiler_flags $wl-soname $wl$soname -o $lib'
+      _LT_TAGVAR(link_all_deplibs, $1)=yes
+      ;;
+
+    os2*)
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+      _LT_TAGVAR(hardcode_minus_L, $1)=yes
+      _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+      shrext_cmds=.dll
+      _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} 
INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~
+       $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~
+       $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~
+       $ECHO EXPORTS >> $output_objdir/$libname.def~
+       emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> 
$output_objdir/$libname.def~
+       $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs 
$compiler_flags $output_objdir/$libname.def~
+       emximp -o $lib $output_objdir/$libname.def'
+      _LT_TAGVAR(archive_expsym_cmds, $1)='$ECHO "LIBRARY 
${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~
+       $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~
+       $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~
+       $ECHO EXPORTS >> $output_objdir/$libname.def~
+       prefix_cmds="$SED"~
+       if test EXPORTS = "`$SED 1q $export_symbols`"; then
+         prefix_cmds="$prefix_cmds -e 1d";
+       fi~
+       prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~
+       cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~
+       $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs 
$compiler_flags $output_objdir/$libname.def~
+       emximp -o $lib $output_objdir/$libname.def'
+      _LT_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o 
$output_objdir/${libname}_dll.a $output_objdir/$libname.def'
+      _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
+      ;;
+
+    interix[[3-9]]*)
+      _LT_TAGVAR(hardcode_direct, $1)=no
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir'
+      _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E'
+      # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc.
+      # Instead, shared libraries are loaded at an image base (0x10000000 by
+      # default) and relocated if they conflict, which is a slow very memory
+      # consuming and fragmenting process.  To avoid this, we pick a random,
+      # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link
+      # time.  Moving up from 0x10000000 also allows more sbrk(2) space.
+      _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs 
$compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 
262144 + 1342177280` -o $lib'
+      _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s|^|_|" $export_symbols 
>$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs 
$compiler_flags $wl-h,$soname 
$wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr 
${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+      ;;
+
+    gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu)
+      tmp_diet=no
+      if test linux-dietlibc = "$host_os"; then
+       case $cc_basename in
+         diet\ *) tmp_diet=yes;;       # linux-dietlibc with static linking 
(!diet-dyn)
+       esac
+      fi
+      if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \
+        && test no = "$tmp_diet"
+      then
+       tmp_addflag=' $pic_flag'
+       tmp_sharedflag='-shared'
+       case $cc_basename,$host_cpu in
+        pgcc*)                         # Portland Group C compiler
+         _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv 
in $convenience\"\"; do test  -n \"$conv\" && 
new_convenience=\"$new_convenience,$conv\"; done; func_echo_all 
\"$new_convenience\"` $wl--no-whole-archive'
+         tmp_addflag=' $pic_flag'
+         ;;
+       pgf77* | pgf90* | pgf95* | pgfortran*)
+                                       # Portland Group f77 and f90 compilers
+         _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv 
in $convenience\"\"; do test  -n \"$conv\" && 
new_convenience=\"$new_convenience,$conv\"; done; func_echo_all 
\"$new_convenience\"` $wl--no-whole-archive'
+         tmp_addflag=' $pic_flag -Mnomain' ;;
+       ecc*,ia64* | icc*,ia64*)        # Intel C compiler on ia64
+         tmp_addflag=' -i_dynamic' ;;
+       efc*,ia64* | ifort*,ia64*)      # Intel Fortran compiler on ia64
+         tmp_addflag=' -i_dynamic -nofor_main' ;;
+       ifc* | ifort*)                  # Intel Fortran compiler
+         tmp_addflag=' -nofor_main' ;;
+       lf95*)                          # Lahey Fortran 8.1
+         _LT_TAGVAR(whole_archive_flag_spec, $1)=
+         tmp_sharedflag='--shared' ;;
+        nagfor*)                        # NAGFOR 5.3
+          tmp_sharedflag='-Wl,-shared' ;;
+       xl[[cC]]* | bgxl[[cC]]* | mpixl[[cC]]*) # IBM XL C 8.0 on PPC (deal 
with xlf below)
+         tmp_sharedflag='-qmkshrobj'
+         tmp_addflag= ;;
+       nvcc*)  # Cuda Compiler Driver 2.2
+         _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv 
in $convenience\"\"; do test  -n \"$conv\" && 
new_convenience=\"$new_convenience,$conv\"; done; func_echo_all 
\"$new_convenience\"` $wl--no-whole-archive'
+         _LT_TAGVAR(compiler_needs_object, $1)=yes
+         ;;
+       esac
+       case `$CC -V 2>&1 | sed 5q` in
+       *Sun\ C*)                       # Sun C 5.9
+         _LT_TAGVAR(whole_archive_flag_spec, 
$1)='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test 
-z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all 
\"$new_convenience\"` $wl--no-whole-archive'
+         _LT_TAGVAR(compiler_needs_object, $1)=yes
+         tmp_sharedflag='-G' ;;
+       *Sun\ F*)                       # Sun Fortran 8.3
+         tmp_sharedflag='-G' ;;
+       esac
+       _LT_TAGVAR(archive_cmds, $1)='$CC '"$tmp_sharedflag""$tmp_addflag"' 
$libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+
+        if test yes = "$supports_anon_versioning"; then
+          _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > 
$output_objdir/$libname.ver~
+            cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> 
$output_objdir/$libname.ver~
+            echo "local: *; };" >> $output_objdir/$libname.ver~
+            $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs 
$compiler_flags $wl-soname $wl$soname $wl-version-script 
$wl$output_objdir/$libname.ver -o $lib'
+        fi
+
+       case $cc_basename in
+       tcc*)
+         _LT_TAGVAR(export_dynamic_flag_spec, $1)='-rdynamic'
+         ;;
+       xlf* | bgf* | bgxlf* | mpixlf*)
+         # IBM XL Fortran 10.1 on PPC cannot create shared libs itself
+         _LT_TAGVAR(whole_archive_flag_spec, $1)='--whole-archive$convenience 
--no-whole-archive'
+         _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir'
+         _LT_TAGVAR(archive_cmds, $1)='$LD -shared $libobjs $deplibs 
$linker_flags -soname $soname -o $lib'
+         if test yes = "$supports_anon_versioning"; then
+           _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > 
$output_objdir/$libname.ver~
+              cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> 
$output_objdir/$libname.ver~
+              echo "local: *; };" >> $output_objdir/$libname.ver~
+              $LD -shared $libobjs $deplibs $linker_flags -soname $soname 
-version-script $output_objdir/$libname.ver -o $lib'
+         fi
+         ;;
+       esac
+      else
+        _LT_TAGVAR(ld_shlibs, $1)=no
+      fi
+      ;;
+
+    netbsd* | netbsdelf*-gnu)
+      if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+       _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable $libobjs $deplibs 
$linker_flags -o $lib'
+       wlarc=
+      else
+       _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs 
$compiler_flags $wl-soname $wl$soname -o $lib'
+       _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs 
$deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file 
$wl$export_symbols -o $lib'
+      fi
+      ;;
+
+    solaris*)
+      if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then
+       _LT_TAGVAR(ld_shlibs, $1)=no
+       cat <<_LT_EOF 1>&2
+
+*** Warning: The releases 2.8.* of the GNU linker cannot reliably
+*** create shared libraries on Solaris systems.  Therefore, libtool
+*** is disabling shared libraries support.  We urge you to upgrade GNU
+*** binutils to release 2.9.1 or newer.  Another option is to modify
+*** your PATH or compiler configuration so that the native linker is
+*** used, and then restart.
+
+_LT_EOF
+      elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; 
then
+       _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs 
$compiler_flags $wl-soname $wl$soname -o $lib'
+       _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs 
$deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file 
$wl$export_symbols -o $lib'
+      else
+       _LT_TAGVAR(ld_shlibs, $1)=no
+      fi
+      ;;
+
+    sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*)
+      case `$LD -v 2>&1` in
+        *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.1[[0-5]].*)
+       _LT_TAGVAR(ld_shlibs, $1)=no
+       cat <<_LT_EOF 1>&2
+
+*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 cannot
+*** reliably create shared libraries on SCO systems.  Therefore, libtool
+*** is disabling shared libraries support.  We urge you to upgrade GNU
+*** binutils to release 2.16.91.0.3 or newer.  Another option is to modify
+*** your PATH or compiler configuration so that the native linker is
+*** used, and then restart.
+
+_LT_EOF
+       ;;
+       *)
+         # For security reasons, it is highly recommended that you always
+         # use absolute paths for naming shared libraries, and exclude the
+         # DT_RUNPATH tag from executables and libraries.  But doing so
+         # requires that you compile everything twice, which is a pain.
+         if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; 
then
+           _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir'
+           _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs 
$compiler_flags $wl-soname $wl$soname -o $lib'
+           _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs 
$compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file 
$wl$export_symbols -o $lib'
+         else
+           _LT_TAGVAR(ld_shlibs, $1)=no
+         fi
+       ;;
+      esac
+      ;;
+
+    sunos4*)
+      _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bshareable -o $lib 
$libobjs $deplibs $linker_flags'
+      wlarc=
+      _LT_TAGVAR(hardcode_direct, $1)=yes
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      ;;
+
+    *)
+      if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+       _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs 
$compiler_flags $wl-soname $wl$soname -o $lib'
+       _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs 
$deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file 
$wl$export_symbols -o $lib'
+      else
+       _LT_TAGVAR(ld_shlibs, $1)=no
+      fi
+      ;;
+    esac
+
+    if test no = "$_LT_TAGVAR(ld_shlibs, $1)"; then
+      runpath_var=
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
+      _LT_TAGVAR(export_dynamic_flag_spec, $1)=
+      _LT_TAGVAR(whole_archive_flag_spec, $1)=
+    fi
+  else
+    # PORTME fill in a description of your system's linker (not GNU ld)
+    case $host_os in
+    aix3*)
+      _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+      _LT_TAGVAR(always_export_symbols, $1)=yes
+      _LT_TAGVAR(archive_expsym_cmds, $1)='$LD -o $output_objdir/$soname 
$libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR 
$AR_FLAGS $lib $output_objdir/$soname'
+      # Note: this linker hardcodes the directories in LIBPATH if there
+      # are no directories specified by -L.
+      _LT_TAGVAR(hardcode_minus_L, $1)=yes
+      if test yes = "$GCC" && test -z "$lt_prog_compiler_static"; then
+       # Neither direct hardcoding nor static linking is supported with a
+       # broken collect2.
+       _LT_TAGVAR(hardcode_direct, $1)=unsupported
+      fi
+      ;;
+
+    aix[[4-9]]*)
+      if test ia64 = "$host_cpu"; then
+       # On IA64, the linker does run time linking by default, so we don't
+       # have to do anything special.
+       aix_use_runtimelinking=no
+       exp_sym_flag='-Bexport'
+       no_entry_flag=
+      else
+       # If we're using GNU nm, then we don't want the "-C" option.
+       # -C means demangle to GNU nm, but means don't demangle to AIX nm.
+       # Without the "-l" option, or with the "-B" option, AIX nm treats
+       # weak defined symbols like other global defined symbols, whereas
+       # GNU nm marks them as "W".
+       # While the 'weak' keyword is ignored in the Export File, we need
+       # it in the Import File for the 'aix-soname' feature, so we have
+       # to replace the "-B" option with "-P" for AIX nm.
+       if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then
+         _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | 
awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == 
"W")) && ([substr](\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } 
else { print \$ 3 } } }'\'' | sort -u > $export_symbols'
+       else
+         _LT_TAGVAR(export_symbols_cmds, $1)='`func_echo_all $NM | $SED -e 
'\''s/B\([[^B]]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 
2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W") || (\$ 2 == "V") 
|| (\$ 2 == "Z")) && ([substr](\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 
== "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | 
sort -u > $export_symbols'
+       fi
+       aix_use_runtimelinking=no
+
+       # Test if we are trying to use run time linking or normal
+       # AIX style linking. If -brtl is somewhere in LDFLAGS, we
+       # have runtime linking enabled, and use it for executables.
+       # For shared libraries, we enable/disable runtime linking
+       # depending on the kind of the shared library created -
+       # when "with_aix_soname,aix_use_runtimelinking" is:
+       # "aix,no"   lib.a(lib.so.V) shared, rtl:no,  for executables
+       # "aix,yes"  lib.so          shared, rtl:yes, for executables
+       #            lib.a           static archive
+       # "both,no"  lib.so.V(shr.o) shared, rtl:yes
+       #            lib.a(lib.so.V) shared, rtl:no,  for executables
+       # "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables
+       #            lib.a(lib.so.V) shared, rtl:no
+       # "svr4,*"   lib.so.V(shr.o) shared, rtl:yes, for executables
+       #            lib.a           static archive
+       case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*)
+         for ld_flag in $LDFLAGS; do
+         if (test x-brtl = "x$ld_flag" || test x-Wl,-brtl = "x$ld_flag"); then
+           aix_use_runtimelinking=yes
+           break
+         fi
+         done
+         if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then
+           # With aix-soname=svr4, we create the lib.so.V shared archives only,
+           # so we don't have lib.a shared libs to link our executables.
+           # We have to force runtime linking in this case.
+           aix_use_runtimelinking=yes
+           LDFLAGS="$LDFLAGS -Wl,-brtl"
+         fi
+         ;;
+       esac
+
+       exp_sym_flag='-bexport'
+       no_entry_flag='-bnoentry'
+      fi
+
+      # When large executables or shared objects are built, AIX ld can
+      # have problems creating the table of contents.  If linking a library
+      # or program results in "error TOC overflow" add -mminimal-toc to
+      # CXXFLAGS/CFLAGS for g++/gcc.  In the cases where that is not
+      # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS.
+
+      _LT_TAGVAR(archive_cmds, $1)=''
+      _LT_TAGVAR(hardcode_direct, $1)=yes
+      _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+      _LT_TAGVAR(hardcode_libdir_separator, $1)=':'
+      _LT_TAGVAR(link_all_deplibs, $1)=yes
+      _LT_TAGVAR(file_list_spec, $1)='$wl-f,'
+      case $with_aix_soname,$aix_use_runtimelinking in
+      aix,*) ;; # traditional, no import file
+      svr4,* | *,yes) # use import file
+       # The Import File defines what to hardcode.
+       _LT_TAGVAR(hardcode_direct, $1)=no
+       _LT_TAGVAR(hardcode_direct_absolute, $1)=no
+       ;;
+      esac
+
+      if test yes = "$GCC"; then
+       case $host_os in aix4.[[012]]|aix4.[[012]].*)
+       # We only want to do this on AIX 4.2 and lower, the check
+       # below for broken collect2 doesn't work under 4.3+
+         collect2name=`$CC -print-prog-name=collect2`
+         if test -f "$collect2name" &&
+          strings "$collect2name" | $GREP resolve_lib_name >/dev/null
+         then
+         # We have reworked collect2
+         :
+         else
+         # We have old collect2
+         _LT_TAGVAR(hardcode_direct, $1)=unsupported
+         # It fails to find uninstalled libraries when the uninstalled
+         # path is not listed in the libpath.  Setting hardcode_minus_L
+         # to unsupported forces relinking
+         _LT_TAGVAR(hardcode_minus_L, $1)=yes
+         _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+         _LT_TAGVAR(hardcode_libdir_separator, $1)=
+         fi
+         ;;
+       esac
+       shared_flag='-shared'
+       if test yes = "$aix_use_runtimelinking"; then
+         shared_flag="$shared_flag "'$wl-G'
+       fi
+       # Need to ensure runtime linking is disabled for the traditional
+       # shared library, or the linker may eventually find shared libraries
+       # /with/ Import File - we do not want to mix them.
+       shared_flag_aix='-shared'
+       shared_flag_svr4='-shared $wl-G'
+      else
+       # not using gcc
+       if test ia64 = "$host_cpu"; then
+       # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release
+       # chokes on -Wl,-G. The following line is correct:
+         shared_flag='-G'
+       else
+         if test yes = "$aix_use_runtimelinking"; then
+           shared_flag='$wl-G'
+         else
+           shared_flag='$wl-bM:SRE'
+         fi
+         shared_flag_aix='$wl-bM:SRE'
+         shared_flag_svr4='$wl-G'
+       fi
+      fi
+
+      _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-bexpall'
+      # It seems that -bexpall does not export symbols beginning with
+      # underscore (_), so it is better to generate a list of symbols to 
export.
+      _LT_TAGVAR(always_export_symbols, $1)=yes
+      if test aix,yes = "$with_aix_soname,$aix_use_runtimelinking"; then
+       # Warning - without using the other runtime loading flags (-brtl),
+       # -berok will link without error, but may produce a broken library.
+       _LT_TAGVAR(allow_undefined_flag, $1)='-berok'
+        # Determine the default libpath from the value encoded in an
+        # empty executable.
+        _LT_SYS_MODULE_PATH_AIX([$1])
+        _LT_TAGVAR(hardcode_libdir_flag_spec, 
$1)='$wl-blibpath:$libdir:'"$aix_libpath"
+        _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname 
$libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n 
"$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; 
fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag
+      else
+       if test ia64 = "$host_cpu"; then
+         _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R 
$libdir:/usr/lib:/lib'
+         _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs"
+         _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o 
$output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags 
$wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols"
+       else
+        # Determine the default libpath from the value encoded in an
+        # empty executable.
+        _LT_SYS_MODULE_PATH_AIX([$1])
+        _LT_TAGVAR(hardcode_libdir_flag_spec, 
$1)='$wl-blibpath:$libdir:'"$aix_libpath"
+         # Warning - without using the other run time loading flags,
+         # -berok will link without error, but may produce a broken library.
+         _LT_TAGVAR(no_undefined_flag, $1)=' $wl-bernotok'
+         _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-berok'
+         if test yes = "$with_gnu_ld"; then
+           # We only use this code for GNU lds that support --whole-archive.
+           _LT_TAGVAR(whole_archive_flag_spec, 
$1)='$wl--whole-archive$convenience $wl--no-whole-archive'
+         else
+           # Exported symbols can be pulled into shared objects from archives
+           _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience'
+         fi
+         _LT_TAGVAR(archive_cmds_need_lc, $1)=yes
+         _LT_TAGVAR(archive_expsym_cmds, $1)='$RM -r 
$output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d'
+         # -brtl affects multiple linker settings, -berok does not and is 
overridden later
+         compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED -e 
"s%-brtl\\([[, ]]\\)%-berok\\1%g"`'
+         if test svr4 != "$with_aix_soname"; then
+           # This is similar to how AIX traditionally builds its shared 
libraries.
+           _LT_TAGVAR(archive_expsym_cmds, 
$1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_aix' -o 
$output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry 
'$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR 
$AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname'
+         fi
+         if test aix != "$with_aix_soname"; then
+           _LT_TAGVAR(archive_expsym_cmds, 
$1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_svr4' -o 
$output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs 
$wl-bnoentry 
'$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e 
$output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! 
$soname($shared_archive_member_spec.o)"; if test shr_64 = 
"$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 
3 [...]
+         else
+           # used by -dlpreopen to get the symbols
+           _LT_TAGVAR(archive_expsym_cmds, 
$1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$MV  
$output_objdir/$realname.d/$soname $output_objdir'
+         fi
+         _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, 
$1)"'~$RM -r $output_objdir/$realname.d'
+       fi
+      fi
+      ;;
+
+    amigaos*)
+      case $host_cpu in
+      powerpc)
+            # see comment about AmigaOS4 .so support
+            _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs 
$compiler_flags $wl-soname $wl$soname -o $lib'
+            _LT_TAGVAR(archive_expsym_cmds, $1)=''
+        ;;
+      m68k)
+            _LT_TAGVAR(archive_cmds, $1)='$RM 
$output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > 
$output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> 
$output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> 
$output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> 
$output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd 
$output_objdir && a2ixlibrary -32)'
+            _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+            _LT_TAGVAR(hardcode_minus_L, $1)=yes
+        ;;
+      esac
+      ;;
+
+    bsdi[[45]]*)
+      _LT_TAGVAR(export_dynamic_flag_spec, $1)=-rdynamic
+      ;;
+
+    cygwin* | mingw* | pw32* | cegcc*)
+      # When not using gcc, we currently assume that we are using
+      # Microsoft Visual C++.
+      # hardcode_libdir_flag_spec is actually meaningless, as there is
+      # no search path for DLLs.
+      case $cc_basename in
+      cl*)
+       # Native MSVC
+       _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' '
+       _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+       _LT_TAGVAR(always_export_symbols, $1)=yes
+       _LT_TAGVAR(file_list_spec, $1)='@'
+       # Tell ltmain to make .lib files, not .a files.
+       libext=lib
+       # Tell ltmain to make .dll files, not .so files.
+       shrext_cmds=.dll
+       # FIXME: Setting linknames here is a bad hack.
+       _LT_TAGVAR(archive_cmds, $1)='$CC -o $output_objdir/$soname $libobjs 
$compiler_flags $deplibs 
-Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames='
+       _LT_TAGVAR(archive_expsym_cmds, $1)='if 
_LT_DLL_DEF_P([$export_symbols]); then
+            cp "$export_symbols" "$output_objdir/$soname.def";
+            echo "$tool_output_objdir$soname.def" > 
"$output_objdir/$soname.exp";
+          else
+            $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > 
$output_objdir/$soname.exp;
+          fi~
+          $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs 
"@$tool_output_objdir$soname.exp" 
-Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~
+          linknames='
+       # The linker will not automatically build a static lib if we build a 
DLL.
+       # _LT_TAGVAR(old_archive_from_new_cmds, $1)='true'
+       _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
+       _LT_TAGVAR(exclude_expsyms, 
$1)='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*'
+       _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | 
$global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ 
]]*\)/\1,DATA/'\'' | $SED -e '\''/^[[AITW]][[ ]]/s/.*[[ ]]//'\'' | sort | uniq 
> $export_symbols'
+       # Don't use ranlib
+       _LT_TAGVAR(old_postinstall_cmds, $1)='chmod 644 $oldlib'
+       _LT_TAGVAR(postlink_cmds, $1)='lt_outputfile="@OUTPUT@"~
+          lt_tool_outputfile="@TOOL_OUTPUT@"~
+          case $lt_outputfile in
+            *.exe|*.EXE) ;;
+            *)
+              lt_outputfile=$lt_outputfile.exe
+              lt_tool_outputfile=$lt_tool_outputfile.exe
+              ;;
+          esac~
+          if test : != "$MANIFEST_TOOL" && test -f "$lt_outputfile.manifest"; 
then
+            $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" 
-outputresource:"$lt_tool_outputfile" || exit 1;
+            $RM "$lt_outputfile.manifest";
+          fi'
+       ;;
+      *)
+       # Assume MSVC wrapper
+       _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' '
+       _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+       # Tell ltmain to make .lib files, not .a files.
+       libext=lib
+       # Tell ltmain to make .dll files, not .so files.
+       shrext_cmds=.dll
+       # FIXME: Setting linknames here is a bad hack.
+       _LT_TAGVAR(archive_cmds, $1)='$CC -o $lib $libobjs $compiler_flags 
`func_echo_all "$deplibs" | $SED '\''s/ -lc$//'\''` -link -dll~linknames='
+       # The linker will automatically build a .lib file if we build a DLL.
+       _LT_TAGVAR(old_archive_from_new_cmds, $1)='true'
+       # FIXME: Should let the user specify the lib program.
+       _LT_TAGVAR(old_archive_cmds, $1)='lib -OUT:$oldlib$oldobjs$old_deplibs'
+       _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
+       ;;
+      esac
+      ;;
+
+    darwin* | rhapsody*)
+      _LT_DARWIN_LINKER_FEATURES($1)
+      ;;
+
+    dgux*)
+      _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs 
$deplibs $linker_flags'
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      ;;
+
+    # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor
+    # support.  Future versions do this automatically, but an explicit c++rt0.o
+    # does not break anything, and helps significantly (at the cost of a little
+    # extra space).
+    freebsd2.2*)
+      _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs 
$linker_flags /usr/lib/c++rt0.o'
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+      _LT_TAGVAR(hardcode_direct, $1)=yes
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      ;;
+
+    # Unfortunately, older versions of FreeBSD 2 do not have this feature.
+    freebsd2.*)
+      _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs 
$linker_flags'
+      _LT_TAGVAR(hardcode_direct, $1)=yes
+      _LT_TAGVAR(hardcode_minus_L, $1)=yes
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      ;;
+
+    # FreeBSD 3 and greater uses gcc -shared to do shared libraries.
+    freebsd* | dragonfly*)
+      _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs 
$deplibs $compiler_flags'
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+      _LT_TAGVAR(hardcode_direct, $1)=yes
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      ;;
+
+    hpux9*)
+      if test yes = "$GCC"; then
+       _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared 
$pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $libobjs $deplibs 
$compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv 
$output_objdir/$soname $lib'
+      else
+       _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$LD -b +b 
$install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test 
"x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib'
+      fi
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir'
+      _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+      _LT_TAGVAR(hardcode_direct, $1)=yes
+
+      # hardcode_minus_L: Not really in the search PATH,
+      # but as the default location of the library.
+      _LT_TAGVAR(hardcode_minus_L, $1)=yes
+      _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E'
+      ;;
+
+    hpux10*)
+      if test yes,no = "$GCC,$with_gnu_ld"; then
+       _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl+h $wl$soname 
$wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+      else
+       _LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o 
$lib $libobjs $deplibs $linker_flags'
+      fi
+      if test no = "$with_gnu_ld"; then
+       _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir'
+       _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+       _LT_TAGVAR(hardcode_direct, $1)=yes
+       _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+       _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E'
+       # hardcode_minus_L: Not really in the search PATH,
+       # but as the default location of the library.
+       _LT_TAGVAR(hardcode_minus_L, $1)=yes
+      fi
+      ;;
+
+    hpux11*)
+      if test yes,no = "$GCC,$with_gnu_ld"; then
+       case $host_cpu in
+       hppa*64*)
+         _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl+h $wl$soname -o $lib 
$libobjs $deplibs $compiler_flags'
+         ;;
+       ia64*)
+         _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl+h $wl$soname 
$wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
+         ;;
+       *)
+         _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl+h $wl$soname 
$wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+         ;;
+       esac
+      else
+       case $host_cpu in
+       hppa*64*)
+         _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname -o $lib 
$libobjs $deplibs $compiler_flags'
+         ;;
+       ia64*)
+         _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname 
$wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
+         ;;
+       *)
+       m4_if($1, [], [
+         # Older versions of the 11.00 compiler do not understand -b yet
+         # (HP92453-01 A.11.01.20 doesn't, HP92453-01 B.11.X.35175-35176.GP 
does)
+         _LT_LINKER_OPTION([if $CC understands -b],
+           _LT_TAGVAR(lt_cv_prog_compiler__b, $1), [-b],
+           [_LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+b 
$wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags'],
+           [_LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir 
-o $lib $libobjs $deplibs $linker_flags'])],
+         [_LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+b 
$wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags'])
+         ;;
+       esac
+      fi
+      if test no = "$with_gnu_ld"; then
+       _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir'
+       _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+
+       case $host_cpu in
+       hppa*64*|ia64*)
+         _LT_TAGVAR(hardcode_direct, $1)=no
+         _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+         ;;
+       *)
+         _LT_TAGVAR(hardcode_direct, $1)=yes
+         _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+         _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E'
+
+         # hardcode_minus_L: Not really in the search PATH,
+         # but as the default location of the library.
+         _LT_TAGVAR(hardcode_minus_L, $1)=yes
+         ;;
+       esac
+      fi
+      ;;
+
+    irix5* | irix6* | nonstopux*)
+      if test yes = "$GCC"; then
+       _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs 
$compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all 
"$wl-set_version $wl$verstring"` $wl-update_registry 
$wl$output_objdir/so_locations -o $lib'
+       # Try to use the -exported_symbol ld option, if it does not
+       # work, assume that -exports_file does not work either and
+       # implicitly export all symbols.
+       # This should be the same for all languages, so no per-tag cache 
variable.
+       AC_CACHE_CHECK([whether the $host_os linker accepts -exported_symbol],
+         [lt_cv_irix_exported_symbol],
+         [save_LDFLAGS=$LDFLAGS
+          LDFLAGS="$LDFLAGS -shared $wl-exported_symbol ${wl}foo 
$wl-update_registry $wl/dev/null"
+          AC_LINK_IFELSE(
+            [AC_LANG_SOURCE(
+               [AC_LANG_CASE([C], [[int foo (void) { return 0; }]],
+                             [C++], [[int foo (void) { return 0; }]],
+                             [Fortran 77], [[
+      subroutine foo
+      end]],
+                             [Fortran], [[
+      subroutine foo
+      end]])])],
+             [lt_cv_irix_exported_symbol=yes],
+             [lt_cv_irix_exported_symbol=no])
+           LDFLAGS=$save_LDFLAGS])
+       if test yes = "$lt_cv_irix_exported_symbol"; then
+          _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs 
$deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && 
func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry 
$wl$output_objdir/so_locations $wl-exports_file $wl$export_symbols -o $lib'
+       fi
+       _LT_TAGVAR(link_all_deplibs, $1)=no
+      else
+       _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs 
$compiler_flags -soname $soname `test -n "$verstring" && func_echo_all 
"-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib'
+       _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs 
$compiler_flags -soname $soname `test -n "$verstring" && func_echo_all 
"-set_version $verstring"` -update_registry $output_objdir/so_locations 
-exports_file $export_symbols -o $lib'
+      fi
+      _LT_TAGVAR(archive_cmds_need_lc, $1)='no'
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir'
+      _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+      _LT_TAGVAR(inherit_rpath, $1)=yes
+      _LT_TAGVAR(link_all_deplibs, $1)=yes
+      ;;
+
+    linux*)
+      case $cc_basename in
+      tcc*)
+       # Fabrice Bellard et al's Tiny C Compiler
+       _LT_TAGVAR(ld_shlibs, $1)=yes
+       _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs 
$deplibs $compiler_flags'
+       ;;
+      esac
+      ;;
+
+    netbsd* | netbsdelf*-gnu)
+      if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+       _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs 
$linker_flags'  # a.out
+      else
+       _LT_TAGVAR(archive_cmds, $1)='$LD -shared -o $lib $libobjs $deplibs 
$linker_flags'      # ELF
+      fi
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+      _LT_TAGVAR(hardcode_direct, $1)=yes
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      ;;
+
+    newsos6)
+      _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs 
$deplibs $linker_flags'
+      _LT_TAGVAR(hardcode_direct, $1)=yes
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir'
+      _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      ;;
+
+    *nto* | *qnx*)
+      ;;
+
+    openbsd* | bitrig*)
+      if test -f /usr/libexec/ld.so; then
+       _LT_TAGVAR(hardcode_direct, $1)=yes
+       _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+       _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+       if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then
+         _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs 
$deplibs $compiler_flags'
+         _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag -o $lib 
$libobjs $deplibs $compiler_flags $wl-retain-symbols-file,$export_symbols'
+         _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir'
+         _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E'
+       else
+         _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs 
$deplibs $compiler_flags'
+         _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir'
+       fi
+      else
+       _LT_TAGVAR(ld_shlibs, $1)=no
+      fi
+      ;;
+
+    os2*)
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+      _LT_TAGVAR(hardcode_minus_L, $1)=yes
+      _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+      shrext_cmds=.dll
+      _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} 
INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~
+       $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~
+       $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~
+       $ECHO EXPORTS >> $output_objdir/$libname.def~
+       emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> 
$output_objdir/$libname.def~
+       $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs 
$compiler_flags $output_objdir/$libname.def~
+       emximp -o $lib $output_objdir/$libname.def'
+      _LT_TAGVAR(archive_expsym_cmds, $1)='$ECHO "LIBRARY 
${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~
+       $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~
+       $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~
+       $ECHO EXPORTS >> $output_objdir/$libname.def~
+       prefix_cmds="$SED"~
+       if test EXPORTS = "`$SED 1q $export_symbols`"; then
+         prefix_cmds="$prefix_cmds -e 1d";
+       fi~
+       prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~
+       cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~
+       $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs 
$compiler_flags $output_objdir/$libname.def~
+       emximp -o $lib $output_objdir/$libname.def'
+      _LT_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o 
$output_objdir/${libname}_dll.a $output_objdir/$libname.def'
+      _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
+      ;;
+
+    osf3*)
+      if test yes = "$GCC"; then
+       _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*'
+       _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $libobjs 
$deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && 
func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry 
$wl$output_objdir/so_locations -o $lib'
+      else
+       _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*'
+       _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $libobjs 
$deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all 
"-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib'
+      fi
+      _LT_TAGVAR(archive_cmds_need_lc, $1)='no'
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir'
+      _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+      ;;
+
+    osf4* | osf5*)     # as osf3* with the addition of -msym flag
+      if test yes = "$GCC"; then
+       _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*'
+       _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag 
$pic_flag $libobjs $deplibs $compiler_flags $wl-msym $wl-soname $wl$soname 
`test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` 
$wl-update_registry $wl$output_objdir/so_locations -o $lib'
+       _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir'
+      else
+       _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*'
+       _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $libobjs 
$deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && 
func_echo_all "-set_version $verstring"` -update_registry 
$output_objdir/so_locations -o $lib'
+       _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do 
printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" 
"-hidden">> $lib.exp~
+          $CC -shared$allow_undefined_flag $wl-input $wl$lib.exp 
$compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && 
$ECHO "-set_version $verstring"` -update_registry $output_objdir/so_locations 
-o $lib~$RM $lib.exp'
+
+       # Both c and cxx compiler support -rpath directly
+       _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir'
+      fi
+      _LT_TAGVAR(archive_cmds_need_lc, $1)='no'
+      _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+      ;;
+
+    solaris*)
+      _LT_TAGVAR(no_undefined_flag, $1)=' -z defs'
+      if test yes = "$GCC"; then
+       wlarc='$wl'
+       _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl-z ${wl}text 
$wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags'
+       _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat 
$export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> 
$lib.exp~
+          $CC -shared $pic_flag $wl-z ${wl}text $wl-M $wl$lib.exp $wl-h 
$wl$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp'
+      else
+       case `$CC -V 2>&1` in
+       *"Compilers 5.0"*)
+         wlarc=''
+         _LT_TAGVAR(archive_cmds, $1)='$LD -G$allow_undefined_flag -h $soname 
-o $lib $libobjs $deplibs $linker_flags'
+         _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat 
$export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> 
$lib.exp~
+            $LD -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib 
$libobjs $deplibs $linker_flags~$RM $lib.exp'
+         ;;
+       *)
+         wlarc='$wl'
+         _LT_TAGVAR(archive_cmds, $1)='$CC -G$allow_undefined_flag -h $soname 
-o $lib $libobjs $deplibs $compiler_flags'
+         _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat 
$export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> 
$lib.exp~
+            $CC -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib 
$libobjs $deplibs $compiler_flags~$RM $lib.exp'
+         ;;
+       esac
+      fi
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      case $host_os in
+      solaris2.[[0-5]] | solaris2.[[0-5]].*) ;;
+      *)
+       # The compiler driver will combine and reorder linker options,
+       # but understands '-z linker_flag'.  GCC discards it without '$wl',
+       # but is careful enough not to reorder.
+       # Supported since Solaris 2.6 (maybe 2.5.1?)
+       if test yes = "$GCC"; then
+         _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl-z 
${wl}allextract$convenience $wl-z ${wl}defaultextract'
+       else
+         _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z 
defaultextract'
+       fi
+       ;;
+      esac
+      _LT_TAGVAR(link_all_deplibs, $1)=yes
+      ;;
+
+    sunos4*)
+      if test sequent = "$host_vendor"; then
+       # Use $CC to link under sequent, because it throws in some extra .o
+       # files that make .init and .fini sections work.
+       _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h $soname -o $lib $libobjs 
$deplibs $compiler_flags'
+      else
+       _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bstatic -o $lib 
$libobjs $deplibs $linker_flags'
+      fi
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+      _LT_TAGVAR(hardcode_direct, $1)=yes
+      _LT_TAGVAR(hardcode_minus_L, $1)=yes
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      ;;
+
+    sysv4)
+      case $host_vendor in
+       sni)
+         _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs 
$deplibs $linker_flags'
+         _LT_TAGVAR(hardcode_direct, $1)=yes # is this really true???
+       ;;
+       siemens)
+         ## LD is ld it makes a PLAMLIB
+         ## CC just makes a GrossModule.
+         _LT_TAGVAR(archive_cmds, $1)='$LD -G -o $lib $libobjs $deplibs 
$linker_flags'
+         _LT_TAGVAR(reload_cmds, $1)='$CC -r -o $output$reload_objs'
+         _LT_TAGVAR(hardcode_direct, $1)=no
+        ;;
+       motorola)
+         _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs 
$deplibs $linker_flags'
+         _LT_TAGVAR(hardcode_direct, $1)=no #Motorola manual says yes, but my 
tests say they lie
+       ;;
+      esac
+      runpath_var='LD_RUN_PATH'
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      ;;
+
+    sysv4.3*)
+      _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs 
$deplibs $linker_flags'
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      _LT_TAGVAR(export_dynamic_flag_spec, $1)='-Bexport'
+      ;;
+
+    sysv4*MP*)
+      if test -d /usr/nec; then
+       _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs 
$deplibs $linker_flags'
+       _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+       runpath_var=LD_RUN_PATH
+       hardcode_runpath_var=yes
+       _LT_TAGVAR(ld_shlibs, $1)=yes
+      fi
+      ;;
+
+    sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | 
sco3.2v5.0.[[024]]*)
+      _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text'
+      _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      runpath_var='LD_RUN_PATH'
+
+      if test yes = "$GCC"; then
+       _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib 
$libobjs $deplibs $compiler_flags'
+       _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared 
$wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs 
$compiler_flags'
+      else
+       _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs 
$deplibs $compiler_flags'
+       _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols 
$wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+      fi
+      ;;
+
+    sysv5* | sco3.2v5* | sco5v6*)
+      # Note: We CANNOT use -z defs as we might desire, because we do not
+      # link with -lc, and that would cause any symbols used from libc to
+      # always be unresolved, which means just about no library would
+      # ever link correctly.  If we're not using GNU ld we use -z text
+      # though, which does catch some bad symbols but isn't as heavy-handed
+      # as -z defs.
+      _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text'
+      _LT_TAGVAR(allow_undefined_flag, $1)='$wl-z,nodefs'
+      _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R,$libdir'
+      _LT_TAGVAR(hardcode_libdir_separator, $1)=':'
+      _LT_TAGVAR(link_all_deplibs, $1)=yes
+      _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-Bexport'
+      runpath_var='LD_RUN_PATH'
+
+      if test yes = "$GCC"; then
+       _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib 
$libobjs $deplibs $compiler_flags'
+       _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared 
$wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs 
$compiler_flags'
+      else
+       _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs 
$deplibs $compiler_flags'
+       _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols 
$wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+      fi
+      ;;
+
+    uts4*)
+      _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs 
$deplibs $linker_flags'
+      _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      ;;
+
+    *)
+      _LT_TAGVAR(ld_shlibs, $1)=no
+      ;;
+    esac
+
+    if test sni = "$host_vendor"; then
+      case $host in
+      sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*)
+       _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-Blargedynsym'
+       ;;
+      esac
+    fi
+  fi
+])
+AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)])
+test no = "$_LT_TAGVAR(ld_shlibs, $1)" && can_build_shared=no
+
+_LT_TAGVAR(with_gnu_ld, $1)=$with_gnu_ld
+
+_LT_DECL([], [libext], [0], [Old archive suffix (normally "a")])dnl
+_LT_DECL([], [shrext_cmds], [1], [Shared library suffix (normally ".so")])dnl
+_LT_DECL([], [extract_expsyms_cmds], [2],
+    [The commands to extract the exported symbol list from a shared archive])
+
+#
+# Do we need to explicitly link libc?
+#
+case "x$_LT_TAGVAR(archive_cmds_need_lc, $1)" in
+x|xyes)
+  # Assume -lc should be added
+  _LT_TAGVAR(archive_cmds_need_lc, $1)=yes
+
+  if test yes,yes = "$GCC,$enable_shared"; then
+    case $_LT_TAGVAR(archive_cmds, $1) in
+    *'~'*)
+      # FIXME: we may have to deal with multi-command sequences.
+      ;;
+    '$CC '*)
+      # Test whether the compiler implicitly links with -lc since on some
+      # systems, -lgcc has to come before -lc. If gcc already passes -lc
+      # to ld, don't add -lc before -lgcc.
+      AC_CACHE_CHECK([whether -lc should be explicitly linked in],
+       [lt_cv_]_LT_TAGVAR(archive_cmds_need_lc, $1),
+       [$RM conftest*
+       echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+       if AC_TRY_EVAL(ac_compile) 2>conftest.err; then
+         soname=conftest
+         lib=conftest
+         libobjs=conftest.$ac_objext
+         deplibs=
+         wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1)
+         pic_flag=$_LT_TAGVAR(lt_prog_compiler_pic, $1)
+         compiler_flags=-v
+         linker_flags=-v
+         verstring=
+         output_objdir=.
+         libname=conftest
+         lt_save_allow_undefined_flag=$_LT_TAGVAR(allow_undefined_flag, $1)
+         _LT_TAGVAR(allow_undefined_flag, $1)=
+         if AC_TRY_EVAL(_LT_TAGVAR(archive_cmds, $1) 2\>\&1 \| $GREP \" -lc \" 
\>/dev/null 2\>\&1)
+         then
+           lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1)=no
+         else
+           lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1)=yes
+         fi
+         _LT_TAGVAR(allow_undefined_flag, $1)=$lt_save_allow_undefined_flag
+       else
+         cat conftest.err 1>&5
+       fi
+       $RM conftest*
+       ])
+      _LT_TAGVAR(archive_cmds_need_lc, 
$1)=$lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1)
+      ;;
+    esac
+  fi
+  ;;
+esac
+
+_LT_TAGDECL([build_libtool_need_lc], [archive_cmds_need_lc], [0],
+    [Whether or not to add -lc for building shared libraries])
+_LT_TAGDECL([allow_libtool_libs_with_static_runtimes],
+    [enable_shared_with_static_runtimes], [0],
+    [Whether or not to disallow shared libs when runtime libs are static])
+_LT_TAGDECL([], [export_dynamic_flag_spec], [1],
+    [Compiler flag to allow reflexive dlopens])
+_LT_TAGDECL([], [whole_archive_flag_spec], [1],
+    [Compiler flag to generate shared objects directly from archives])
+_LT_TAGDECL([], [compiler_needs_object], [1],
+    [Whether the compiler copes with passing no objects directly])
+_LT_TAGDECL([], [old_archive_from_new_cmds], [2],
+    [Create an old-style archive from a shared archive])
+_LT_TAGDECL([], [old_archive_from_expsyms_cmds], [2],
+    [Create a temporary old-style archive to link instead of a shared archive])
+_LT_TAGDECL([], [archive_cmds], [2], [Commands used to build a shared archive])
+_LT_TAGDECL([], [archive_expsym_cmds], [2])
+_LT_TAGDECL([], [module_cmds], [2],
+    [Commands used to build a loadable module if different from building
+    a shared archive.])
+_LT_TAGDECL([], [module_expsym_cmds], [2])
+_LT_TAGDECL([], [with_gnu_ld], [1],
+    [Whether we are building with GNU ld or not])
+_LT_TAGDECL([], [allow_undefined_flag], [1],
+    [Flag that allows shared libraries with undefined symbols to be built])
+_LT_TAGDECL([], [no_undefined_flag], [1],
+    [Flag that enforces no undefined symbols])
+_LT_TAGDECL([], [hardcode_libdir_flag_spec], [1],
+    [Flag to hardcode $libdir into a binary during linking.
+    This must work even if $libdir does not exist])
+_LT_TAGDECL([], [hardcode_libdir_separator], [1],
+    [Whether we need a single "-rpath" flag with a separated argument])
+_LT_TAGDECL([], [hardcode_direct], [0],
+    [Set to "yes" if using DIR/libNAME$shared_ext during linking hardcodes
+    DIR into the resulting binary])
+_LT_TAGDECL([], [hardcode_direct_absolute], [0],
+    [Set to "yes" if using DIR/libNAME$shared_ext during linking hardcodes
+    DIR into the resulting binary and the resulting library dependency is
+    "absolute", i.e impossible to change by setting $shlibpath_var if the
+    library is relocated])
+_LT_TAGDECL([], [hardcode_minus_L], [0],
+    [Set to "yes" if using the -LDIR flag during linking hardcodes DIR
+    into the resulting binary])
+_LT_TAGDECL([], [hardcode_shlibpath_var], [0],
+    [Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR
+    into the resulting binary])
+_LT_TAGDECL([], [hardcode_automatic], [0],
+    [Set to "yes" if building a shared library automatically hardcodes DIR
+    into the library and all subsequent libraries and executables linked
+    against it])
+_LT_TAGDECL([], [inherit_rpath], [0],
+    [Set to yes if linker adds runtime paths of dependent libraries
+    to runtime path list])
+_LT_TAGDECL([], [link_all_deplibs], [0],
+    [Whether libtool must link a program against all its dependency libraries])
+_LT_TAGDECL([], [always_export_symbols], [0],
+    [Set to "yes" if exported symbols are required])
+_LT_TAGDECL([], [export_symbols_cmds], [2],
+    [The commands to list exported symbols])
+_LT_TAGDECL([], [exclude_expsyms], [1],
+    [Symbols that should not be listed in the preloaded symbols])
+_LT_TAGDECL([], [include_expsyms], [1],
+    [Symbols that must always be exported])
+_LT_TAGDECL([], [prelink_cmds], [2],
+    [Commands necessary for linking programs (against libraries) with 
templates])
+_LT_TAGDECL([], [postlink_cmds], [2],
+    [Commands necessary for finishing linking programs])
+_LT_TAGDECL([], [file_list_spec], [1],
+    [Specify filename containing input files])
+dnl FIXME: Not yet implemented
+dnl _LT_TAGDECL([], [thread_safe_flag_spec], [1],
+dnl    [Compiler flag to generate thread safe objects])
+])# _LT_LINKER_SHLIBS
+
+
+# _LT_LANG_C_CONFIG([TAG])
+# ------------------------
+# Ensure that the configuration variables for a C compiler are suitably
+# defined.  These variables are subsequently used by _LT_CONFIG to write
+# the compiler configuration to 'libtool'.
+m4_defun([_LT_LANG_C_CONFIG],
+[m4_require([_LT_DECL_EGREP])dnl
+lt_save_CC=$CC
+AC_LANG_PUSH(C)
+
+# Source file extension for C test sources.
+ac_ext=c
+
+# Object file extension for compiled C test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# Code to be used in simple compile tests
+lt_simple_compile_test_code="int some_variable = 0;"
+
+# Code to be used in simple link tests
+lt_simple_link_test_code='int main(){return(0);}'
+
+_LT_TAG_COMPILER
+# Save the default compiler, since it gets overwritten when the other
+# tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP.
+compiler_DEFAULT=$CC
+
+# save warnings/boilerplate of simple test code
+_LT_COMPILER_BOILERPLATE
+_LT_LINKER_BOILERPLATE
+
+## CAVEAT EMPTOR:
+## There is no encapsulation within the following macros, do not change
+## the running order or otherwise move them around unless you know exactly
+## what you are doing...
+if test -n "$compiler"; then
+  _LT_COMPILER_NO_RTTI($1)
+  _LT_COMPILER_PIC($1)
+  _LT_COMPILER_C_O($1)
+  _LT_COMPILER_FILE_LOCKS($1)
+  _LT_LINKER_SHLIBS($1)
+  _LT_SYS_DYNAMIC_LINKER($1)
+  _LT_LINKER_HARDCODE_LIBPATH($1)
+  LT_SYS_DLOPEN_SELF
+  _LT_CMD_STRIPLIB
+
+  # Report what library types will actually be built
+  AC_MSG_CHECKING([if libtool supports shared libraries])
+  AC_MSG_RESULT([$can_build_shared])
+
+  AC_MSG_CHECKING([whether to build shared libraries])
+  test no = "$can_build_shared" && enable_shared=no
+
+  # On AIX, shared libraries and static libraries use the same namespace, and
+  # are all built from PIC.
+  case $host_os in
+  aix3*)
+    test yes = "$enable_shared" && enable_static=no
+    if test -n "$RANLIB"; then
+      archive_cmds="$archive_cmds~\$RANLIB \$lib"
+      postinstall_cmds='$RANLIB $lib'
+    fi
+    ;;
+
+  aix[[4-9]]*)
+    if test ia64 != "$host_cpu"; then
+      case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in
+      yes,aix,yes) ;;                  # shared object as lib.so file only
+      yes,svr4,*) ;;                   # shared object as lib.so archive 
member only
+      yes,*) enable_static=no ;;       # shared object in lib.a archive as well
+      esac
+    fi
+    ;;
+  esac
+  AC_MSG_RESULT([$enable_shared])
+
+  AC_MSG_CHECKING([whether to build static libraries])
+  # Make sure either enable_shared or enable_static is yes.
+  test yes = "$enable_shared" || enable_static=yes
+  AC_MSG_RESULT([$enable_static])
+
+  _LT_CONFIG($1)
+fi
+AC_LANG_POP
+CC=$lt_save_CC
+])# _LT_LANG_C_CONFIG
+
+
+# _LT_LANG_CXX_CONFIG([TAG])
+# --------------------------
+# Ensure that the configuration variables for a C++ compiler are suitably
+# defined.  These variables are subsequently used by _LT_CONFIG to write
+# the compiler configuration to 'libtool'.
+m4_defun([_LT_LANG_CXX_CONFIG],
+[m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_DECL_EGREP])dnl
+m4_require([_LT_PATH_MANIFEST_TOOL])dnl
+if test -n "$CXX" && ( test no != "$CXX" &&
+    ( (test g++ = "$CXX" && `g++ -v >/dev/null 2>&1` ) ||
+    (test g++ != "$CXX"))); then
+  AC_PROG_CXXCPP
+else
+  _lt_caught_CXX_error=yes
+fi
+
+AC_LANG_PUSH(C++)
+_LT_TAGVAR(archive_cmds_need_lc, $1)=no
+_LT_TAGVAR(allow_undefined_flag, $1)=
+_LT_TAGVAR(always_export_symbols, $1)=no
+_LT_TAGVAR(archive_expsym_cmds, $1)=
+_LT_TAGVAR(compiler_needs_object, $1)=no
+_LT_TAGVAR(export_dynamic_flag_spec, $1)=
+_LT_TAGVAR(hardcode_direct, $1)=no
+_LT_TAGVAR(hardcode_direct_absolute, $1)=no
+_LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
+_LT_TAGVAR(hardcode_libdir_separator, $1)=
+_LT_TAGVAR(hardcode_minus_L, $1)=no
+_LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported
+_LT_TAGVAR(hardcode_automatic, $1)=no
+_LT_TAGVAR(inherit_rpath, $1)=no
+_LT_TAGVAR(module_cmds, $1)=
+_LT_TAGVAR(module_expsym_cmds, $1)=
+_LT_TAGVAR(link_all_deplibs, $1)=unknown
+_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds
+_LT_TAGVAR(reload_flag, $1)=$reload_flag
+_LT_TAGVAR(reload_cmds, $1)=$reload_cmds
+_LT_TAGVAR(no_undefined_flag, $1)=
+_LT_TAGVAR(whole_archive_flag_spec, $1)=
+_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no
+
+# Source file extension for C++ test sources.
+ac_ext=cpp
+
+# Object file extension for compiled C++ test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# No sense in running all these tests if we already determined that
+# the CXX compiler isn't working.  Some variables (like enable_shared)
+# are currently assumed to apply to all compilers on this platform,
+# and will be corrupted by setting them based on a non-working compiler.
+if test yes != "$_lt_caught_CXX_error"; then
+  # Code to be used in simple compile tests
+  lt_simple_compile_test_code="int some_variable = 0;"
+
+  # Code to be used in simple link tests
+  lt_simple_link_test_code='int main(int, char *[[]]) { return(0); }'
+
+  # ltmain only uses $CC for tagged configurations so make sure $CC is set.
+  _LT_TAG_COMPILER
+
+  # save warnings/boilerplate of simple test code
+  _LT_COMPILER_BOILERPLATE
+  _LT_LINKER_BOILERPLATE
+
+  # Allow CC to be a program name with arguments.
+  lt_save_CC=$CC
+  lt_save_CFLAGS=$CFLAGS
+  lt_save_LD=$LD
+  lt_save_GCC=$GCC
+  GCC=$GXX
+  lt_save_with_gnu_ld=$with_gnu_ld
+  lt_save_path_LD=$lt_cv_path_LD
+  if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then
+    lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx
+  else
+    $as_unset lt_cv_prog_gnu_ld
+  fi
+  if test -n "${lt_cv_path_LDCXX+set}"; then
+    lt_cv_path_LD=$lt_cv_path_LDCXX
+  else
+    $as_unset lt_cv_path_LD
+  fi
+  test -z "${LDCXX+set}" || LD=$LDCXX
+  CC=${CXX-"c++"}
+  CFLAGS=$CXXFLAGS
+  compiler=$CC
+  _LT_TAGVAR(compiler, $1)=$CC
+  _LT_CC_BASENAME([$compiler])
+
+  if test -n "$compiler"; then
+    # We don't want -fno-exception when compiling C++ code, so set the
+    # no_builtin_flag separately
+    if test yes = "$GXX"; then
+      _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin'
+    else
+      _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=
+    fi
+
+    if test yes = "$GXX"; then
+      # Set up default GNU C++ configuration
+
+      LT_PATH_LD
+
+      # Check if GNU C++ uses GNU ld as the underlying linker, since the
+      # archiving commands below assume that GNU ld is being used.
+      if test yes = "$with_gnu_ld"; then
+        _LT_TAGVAR(archive_cmds, $1)='$CC $pic_flag -shared -nostdlib 
$predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname 
$wl$soname -o $lib'
+        _LT_TAGVAR(archive_expsym_cmds, $1)='$CC $pic_flag -shared -nostdlib 
$predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname 
$wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
+
+        _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir'
+        _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic'
+
+        # If archive_cmds runs LD, not CC, wlarc should be empty
+        # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to
+        #     investigate it a little bit more. (MM)
+        wlarc='$wl'
+
+        # ancient GNU ld didn't support --whole-archive et. al.
+        if eval "`$CC -print-prog-name=ld` --help 2>&1" |
+         $GREP 'no-whole-archive' > /dev/null; then
+          _LT_TAGVAR(whole_archive_flag_spec, 
$1)=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive'
+        else
+          _LT_TAGVAR(whole_archive_flag_spec, $1)=
+        fi
+      else
+        with_gnu_ld=no
+        wlarc=
+
+        # A generic and very simple default shared library creation
+        # command for GNU C++ for the case where it uses the native
+        # linker, instead of GNU ld.  If possible, this setting should
+        # overridden to take advantage of the native linker features on
+        # the platform it is being used on.
+        _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects 
$libobjs $deplibs $postdep_objects $compiler_flags -o $lib'
+      fi
+
+      # Commands to make compiler produce verbose output that lists
+      # what "hidden" libraries, object files and flags are used when
+      # linking a shared library.
+      output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | 
$GREP -v "^Configured with:" | $GREP "\-L"'
+
+    else
+      GXX=no
+      with_gnu_ld=no
+      wlarc=
+    fi
+
+    # PORTME: fill in a description of your system's C++ link characteristics
+    AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared 
libraries])
+    _LT_TAGVAR(ld_shlibs, $1)=yes
+    case $host_os in
+      aix3*)
+        # FIXME: insert proper C++ library support
+        _LT_TAGVAR(ld_shlibs, $1)=no
+        ;;
+      aix[[4-9]]*)
+        if test ia64 = "$host_cpu"; then
+          # On IA64, the linker does run time linking by default, so we don't
+          # have to do anything special.
+          aix_use_runtimelinking=no
+          exp_sym_flag='-Bexport'
+          no_entry_flag=
+        else
+          aix_use_runtimelinking=no
+
+          # Test if we are trying to use run time linking or normal
+          # AIX style linking. If -brtl is somewhere in LDFLAGS, we
+          # have runtime linking enabled, and use it for executables.
+          # For shared libraries, we enable/disable runtime linking
+          # depending on the kind of the shared library created -
+          # when "with_aix_soname,aix_use_runtimelinking" is:
+          # "aix,no"   lib.a(lib.so.V) shared, rtl:no,  for executables
+          # "aix,yes"  lib.so          shared, rtl:yes, for executables
+          #            lib.a           static archive
+          # "both,no"  lib.so.V(shr.o) shared, rtl:yes
+          #            lib.a(lib.so.V) shared, rtl:no,  for executables
+          # "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables
+          #            lib.a(lib.so.V) shared, rtl:no
+          # "svr4,*"   lib.so.V(shr.o) shared, rtl:yes, for executables
+          #            lib.a           static archive
+          case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*)
+           for ld_flag in $LDFLAGS; do
+             case $ld_flag in
+             *-brtl*)
+               aix_use_runtimelinking=yes
+               break
+               ;;
+             esac
+           done
+           if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then
+             # With aix-soname=svr4, we create the lib.so.V shared archives 
only,
+             # so we don't have lib.a shared libs to link our executables.
+             # We have to force runtime linking in this case.
+             aix_use_runtimelinking=yes
+             LDFLAGS="$LDFLAGS -Wl,-brtl"
+           fi
+           ;;
+          esac
+
+          exp_sym_flag='-bexport'
+          no_entry_flag='-bnoentry'
+        fi
+
+        # When large executables or shared objects are built, AIX ld can
+        # have problems creating the table of contents.  If linking a library
+        # or program results in "error TOC overflow" add -mminimal-toc to
+        # CXXFLAGS/CFLAGS for g++/gcc.  In the cases where that is not
+        # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS.
+
+        _LT_TAGVAR(archive_cmds, $1)=''
+        _LT_TAGVAR(hardcode_direct, $1)=yes
+        _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+        _LT_TAGVAR(hardcode_libdir_separator, $1)=':'
+        _LT_TAGVAR(link_all_deplibs, $1)=yes
+        _LT_TAGVAR(file_list_spec, $1)='$wl-f,'
+        case $with_aix_soname,$aix_use_runtimelinking in
+        aix,*) ;;      # no import file
+        svr4,* | *,yes) # use import file
+          # The Import File defines what to hardcode.
+          _LT_TAGVAR(hardcode_direct, $1)=no
+          _LT_TAGVAR(hardcode_direct_absolute, $1)=no
+          ;;
+        esac
+
+        if test yes = "$GXX"; then
+          case $host_os in aix4.[[012]]|aix4.[[012]].*)
+          # We only want to do this on AIX 4.2 and lower, the check
+          # below for broken collect2 doesn't work under 4.3+
+         collect2name=`$CC -print-prog-name=collect2`
+         if test -f "$collect2name" &&
+            strings "$collect2name" | $GREP resolve_lib_name >/dev/null
+         then
+           # We have reworked collect2
+           :
+         else
+           # We have old collect2
+           _LT_TAGVAR(hardcode_direct, $1)=unsupported
+           # It fails to find uninstalled libraries when the uninstalled
+           # path is not listed in the libpath.  Setting hardcode_minus_L
+           # to unsupported forces relinking
+           _LT_TAGVAR(hardcode_minus_L, $1)=yes
+           _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+           _LT_TAGVAR(hardcode_libdir_separator, $1)=
+         fi
+          esac
+          shared_flag='-shared'
+         if test yes = "$aix_use_runtimelinking"; then
+           shared_flag=$shared_flag' $wl-G'
+         fi
+         # Need to ensure runtime linking is disabled for the traditional
+         # shared library, or the linker may eventually find shared libraries
+         # /with/ Import File - we do not want to mix them.
+         shared_flag_aix='-shared'
+         shared_flag_svr4='-shared $wl-G'
+        else
+          # not using gcc
+          if test ia64 = "$host_cpu"; then
+         # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release
+         # chokes on -Wl,-G. The following line is correct:
+         shared_flag='-G'
+          else
+           if test yes = "$aix_use_runtimelinking"; then
+             shared_flag='$wl-G'
+           else
+             shared_flag='$wl-bM:SRE'
+           fi
+           shared_flag_aix='$wl-bM:SRE'
+           shared_flag_svr4='$wl-G'
+          fi
+        fi
+
+        _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-bexpall'
+        # It seems that -bexpall does not export symbols beginning with
+        # underscore (_), so it is better to generate a list of symbols to
+       # export.
+        _LT_TAGVAR(always_export_symbols, $1)=yes
+       if test aix,yes = "$with_aix_soname,$aix_use_runtimelinking"; then
+          # Warning - without using the other runtime loading flags (-brtl),
+          # -berok will link without error, but may produce a broken library.
+          # The "-G" linker flag allows undefined symbols.
+          _LT_TAGVAR(no_undefined_flag, $1)='-bernotok'
+          # Determine the default libpath from the value encoded in an empty
+          # executable.
+          _LT_SYS_MODULE_PATH_AIX([$1])
+          _LT_TAGVAR(hardcode_libdir_flag_spec, 
$1)='$wl-blibpath:$libdir:'"$aix_libpath"
+
+          _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname 
$libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n 
"$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; 
fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag
+        else
+          if test ia64 = "$host_cpu"; then
+           _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R 
$libdir:/usr/lib:/lib'
+           _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs"
+           _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o 
$output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags 
$wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols"
+          else
+           # Determine the default libpath from the value encoded in an
+           # empty executable.
+           _LT_SYS_MODULE_PATH_AIX([$1])
+           _LT_TAGVAR(hardcode_libdir_flag_spec, 
$1)='$wl-blibpath:$libdir:'"$aix_libpath"
+           # Warning - without using the other run time loading flags,
+           # -berok will link without error, but may produce a broken library.
+           _LT_TAGVAR(no_undefined_flag, $1)=' $wl-bernotok'
+           _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-berok'
+           if test yes = "$with_gnu_ld"; then
+             # We only use this code for GNU lds that support --whole-archive.
+             _LT_TAGVAR(whole_archive_flag_spec, 
$1)='$wl--whole-archive$convenience $wl--no-whole-archive'
+           else
+             # Exported symbols can be pulled into shared objects from archives
+             _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience'
+           fi
+           _LT_TAGVAR(archive_cmds_need_lc, $1)=yes
+           _LT_TAGVAR(archive_expsym_cmds, $1)='$RM -r 
$output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d'
+           # -brtl affects multiple linker settings, -berok does not and is 
overridden later
+           compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED 
-e "s%-brtl\\([[, ]]\\)%-berok\\1%g"`'
+           if test svr4 != "$with_aix_soname"; then
+             # This is similar to how AIX traditionally builds its shared
+             # libraries. Need -bnortl late, we may have -brtl in LDFLAGS.
+             _LT_TAGVAR(archive_expsym_cmds, 
$1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_aix' -o 
$output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry 
'$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR 
$AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname'
+           fi
+           if test aix != "$with_aix_soname"; then
+             _LT_TAGVAR(archive_expsym_cmds, 
$1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_svr4' -o 
$output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs 
$wl-bnoentry 
'$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e 
$output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! 
$soname($shared_archive_member_spec.o)"; if test shr_64 = 
"$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 
[...]
+           else
+             # used by -dlpreopen to get the symbols
+             _LT_TAGVAR(archive_expsym_cmds, 
$1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$MV  
$output_objdir/$realname.d/$soname $output_objdir'
+           fi
+           _LT_TAGVAR(archive_expsym_cmds, 
$1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$RM -r $output_objdir/$realname.d'
+          fi
+        fi
+        ;;
+
+      beos*)
+       if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; 
then
+         _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+         # Joseph Beckenbach <address@hidden> says some releases of gcc
+         # support --undefined.  This deserves some investigation.  FIXME
+         _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs 
$compiler_flags $wl-soname $wl$soname -o $lib'
+       else
+         _LT_TAGVAR(ld_shlibs, $1)=no
+       fi
+       ;;
+
+      chorus*)
+        case $cc_basename in
+          *)
+         # FIXME: insert proper C++ library support
+         _LT_TAGVAR(ld_shlibs, $1)=no
+         ;;
+        esac
+        ;;
+
+      cygwin* | mingw* | pw32* | cegcc*)
+       case $GXX,$cc_basename in
+       ,cl* | no,cl*)
+         # Native MSVC
+         # hardcode_libdir_flag_spec is actually meaningless, as there is
+         # no search path for DLLs.
+         _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' '
+         _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+         _LT_TAGVAR(always_export_symbols, $1)=yes
+         _LT_TAGVAR(file_list_spec, $1)='@'
+         # Tell ltmain to make .lib files, not .a files.
+         libext=lib
+         # Tell ltmain to make .dll files, not .so files.
+         shrext_cmds=.dll
+         # FIXME: Setting linknames here is a bad hack.
+         _LT_TAGVAR(archive_cmds, $1)='$CC -o $output_objdir/$soname $libobjs 
$compiler_flags $deplibs 
-Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames='
+         _LT_TAGVAR(archive_expsym_cmds, $1)='if 
_LT_DLL_DEF_P([$export_symbols]); then
+              cp "$export_symbols" "$output_objdir/$soname.def";
+              echo "$tool_output_objdir$soname.def" > 
"$output_objdir/$soname.exp";
+            else
+              $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > 
$output_objdir/$soname.exp;
+            fi~
+            $CC -o $tool_output_objdir$soname $libobjs $compiler_flags 
$deplibs "@$tool_output_objdir$soname.exp" 
-Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~
+            linknames='
+         # The linker will not automatically build a static lib if we build a 
DLL.
+         # _LT_TAGVAR(old_archive_from_new_cmds, $1)='true'
+         _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
+         # Don't use ranlib
+         _LT_TAGVAR(old_postinstall_cmds, $1)='chmod 644 $oldlib'
+         _LT_TAGVAR(postlink_cmds, $1)='lt_outputfile="@OUTPUT@"~
+            lt_tool_outputfile="@TOOL_OUTPUT@"~
+            case $lt_outputfile in
+              *.exe|*.EXE) ;;
+              *)
+                lt_outputfile=$lt_outputfile.exe
+                lt_tool_outputfile=$lt_tool_outputfile.exe
+                ;;
+            esac~
+            func_to_tool_file "$lt_outputfile"~
+            if test : != "$MANIFEST_TOOL" && test -f 
"$lt_outputfile.manifest"; then
+              $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" 
-outputresource:"$lt_tool_outputfile" || exit 1;
+              $RM "$lt_outputfile.manifest";
+            fi'
+         ;;
+       *)
+         # g++
+         # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless,
+         # as there is no search path for DLLs.
+         _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+         _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-all-symbols'
+         _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+         _LT_TAGVAR(always_export_symbols, $1)=no
+         _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
+
+         if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then
+           _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects 
$libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname 
$wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+           # If the export-symbols file already is a .def file, use it as
+           # is; otherwise, prepend EXPORTS...
+           _LT_TAGVAR(archive_expsym_cmds, $1)='if 
_LT_DLL_DEF_P([$export_symbols]); then
+              cp $export_symbols $output_objdir/$soname.def;
+            else
+              echo EXPORTS > $output_objdir/$soname.def;
+              cat $export_symbols >> $output_objdir/$soname.def;
+            fi~
+            $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects 
$libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname 
$wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+         else
+           _LT_TAGVAR(ld_shlibs, $1)=no
+         fi
+         ;;
+       esac
+       ;;
+      darwin* | rhapsody*)
+        _LT_DARWIN_LINKER_FEATURES($1)
+       ;;
+
+      os2*)
+       _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+       _LT_TAGVAR(hardcode_minus_L, $1)=yes
+       _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+       shrext_cmds=.dll
+       _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} 
INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~
+         $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~
+         $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~
+         $ECHO EXPORTS >> $output_objdir/$libname.def~
+         emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> 
$output_objdir/$libname.def~
+         $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs 
$compiler_flags $output_objdir/$libname.def~
+         emximp -o $lib $output_objdir/$libname.def'
+       _LT_TAGVAR(archive_expsym_cmds, $1)='$ECHO "LIBRARY 
${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~
+         $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~
+         $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~
+         $ECHO EXPORTS >> $output_objdir/$libname.def~
+         prefix_cmds="$SED"~
+         if test EXPORTS = "`$SED 1q $export_symbols`"; then
+           prefix_cmds="$prefix_cmds -e 1d";
+         fi~
+         prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~
+         cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~
+         $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs 
$compiler_flags $output_objdir/$libname.def~
+         emximp -o $lib $output_objdir/$libname.def'
+       _LT_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o 
$output_objdir/${libname}_dll.a $output_objdir/$libname.def'
+       _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
+       ;;
+
+      dgux*)
+        case $cc_basename in
+          ec++*)
+           # FIXME: insert proper C++ library support
+           _LT_TAGVAR(ld_shlibs, $1)=no
+           ;;
+          ghcx*)
+           # Green Hills C++ Compiler
+           # FIXME: insert proper C++ library support
+           _LT_TAGVAR(ld_shlibs, $1)=no
+           ;;
+          *)
+           # FIXME: insert proper C++ library support
+           _LT_TAGVAR(ld_shlibs, $1)=no
+           ;;
+        esac
+        ;;
+
+      freebsd2.*)
+        # C++ shared libraries reported to be fairly broken before
+       # switch to ELF
+        _LT_TAGVAR(ld_shlibs, $1)=no
+        ;;
+
+      freebsd-elf*)
+        _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+        ;;
+
+      freebsd* | dragonfly*)
+        # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF
+        # conventions
+        _LT_TAGVAR(ld_shlibs, $1)=yes
+        ;;
+
+      haiku*)
+        _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs 
$compiler_flags $wl-soname $wl$soname -o $lib'
+        _LT_TAGVAR(link_all_deplibs, $1)=yes
+        ;;
+
+      hpux9*)
+        _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir'
+        _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+        _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E'
+        _LT_TAGVAR(hardcode_direct, $1)=yes
+        _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH,
+                                            # but as the default
+                                            # location of the library.
+
+        case $cc_basename in
+          CC*)
+            # FIXME: insert proper C++ library support
+            _LT_TAGVAR(ld_shlibs, $1)=no
+            ;;
+          aCC*)
+            _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -b 
$wl+b $wl$install_libdir -o $output_objdir/$soname $predep_objects $libobjs 
$deplibs $postdep_objects $compiler_flags~test "x$output_objdir/$soname" = 
"x$lib" || mv $output_objdir/$soname $lib'
+            # Commands to make compiler produce verbose output that lists
+            # what "hidden" libraries, object files and flags are used when
+            # linking a shared library.
+            #
+            # There doesn't appear to be a way to prevent this compiler from
+            # explicitly linking system object files so we need to strip them
+            # from the output so that they don't get included in the library
+            # dependencies.
+            output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v 
conftest.$objext 2>&1) | $EGREP "\-L"`; list= ; for z in $templist; do case $z 
in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; 
done; func_echo_all "$list"'
+            ;;
+          *)
+            if test yes = "$GXX"; then
+              _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC 
-shared -nostdlib $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname 
$predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test 
"x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib'
+            else
+              # FIXME: insert proper C++ library support
+              _LT_TAGVAR(ld_shlibs, $1)=no
+            fi
+            ;;
+        esac
+        ;;
+
+      hpux10*|hpux11*)
+        if test no = "$with_gnu_ld"; then
+         _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir'
+         _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+
+          case $host_cpu in
+            hppa*64*|ia64*)
+              ;;
+            *)
+             _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E'
+              ;;
+          esac
+        fi
+        case $host_cpu in
+          hppa*64*|ia64*)
+            _LT_TAGVAR(hardcode_direct, $1)=no
+            _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+            ;;
+          *)
+            _LT_TAGVAR(hardcode_direct, $1)=yes
+            _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+            _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH,
+                                                # but as the default
+                                                # location of the library.
+            ;;
+        esac
+
+        case $cc_basename in
+          CC*)
+           # FIXME: insert proper C++ library support
+           _LT_TAGVAR(ld_shlibs, $1)=no
+           ;;
+          aCC*)
+           case $host_cpu in
+             hppa*64*)
+               _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname -o $lib 
$predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+               ;;
+             ia64*)
+               _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname 
$wl+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects 
$compiler_flags'
+               ;;
+             *)
+               _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+b 
$wl$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects 
$compiler_flags'
+               ;;
+           esac
+           # Commands to make compiler produce verbose output that lists
+           # what "hidden" libraries, object files and flags are used when
+           # linking a shared library.
+           #
+           # There doesn't appear to be a way to prevent this compiler from
+           # explicitly linking system object files so we need to strip them
+           # from the output so that they don't get included in the library
+           # dependencies.
+           output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v 
conftest.$objext 2>&1) | $GREP "\-L"`; list= ; for z in $templist; do case $z 
in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; 
done; func_echo_all "$list"'
+           ;;
+          *)
+           if test yes = "$GXX"; then
+             if test no = "$with_gnu_ld"; then
+               case $host_cpu in
+                 hppa*64*)
+                   _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC 
$wl+h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects 
$compiler_flags'
+                   ;;
+                 ia64*)
+                   _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib 
$pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $predep_objects $libobjs 
$deplibs $postdep_objects $compiler_flags'
+                   ;;
+                 *)
+                   _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib 
$pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $predep_objects 
$libobjs $deplibs $postdep_objects $compiler_flags'
+                   ;;
+               esac
+             fi
+           else
+             # FIXME: insert proper C++ library support
+             _LT_TAGVAR(ld_shlibs, $1)=no
+           fi
+           ;;
+        esac
+        ;;
+
+      interix[[3-9]]*)
+       _LT_TAGVAR(hardcode_direct, $1)=no
+       _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+       _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir'
+       _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E'
+       # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc.
+       # Instead, shared libraries are loaded at an image base (0x10000000 by
+       # default) and relocated if they conflict, which is a slow very memory
+       # consuming and fragmenting process.  To avoid this, we pick a random,
+       # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link
+       # time.  Moving up from 0x10000000 also allows more sbrk(2) space.
+       _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs 
$compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 
262144 + 1342177280` -o $lib'
+       _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s|^|_|" $export_symbols 
>$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs 
$compiler_flags $wl-h,$soname 
$wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr 
${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+       ;;
+      irix5* | irix6*)
+        case $cc_basename in
+          CC*)
+           # SGI C++
+           _LT_TAGVAR(archive_cmds, $1)='$CC -shared -all -multigot 
$predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname 
$soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` 
-update_registry $output_objdir/so_locations -o $lib'
+
+           # Archives containing C++ object files must be created using
+           # "CC -ar", where "CC" is the IRIX C++ compiler.  This is
+           # necessary to make sure instantiated templates are included
+           # in the archive.
+           _LT_TAGVAR(old_archive_cmds, $1)='$CC -ar -WR,-u -o $oldlib 
$oldobjs'
+           ;;
+          *)
+           if test yes = "$GXX"; then
+             if test no = "$with_gnu_ld"; then
+               _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib 
$predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname 
$wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version 
$wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib'
+             else
+               _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib 
$predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname 
$wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version 
$wl$verstring"` -o $lib'
+             fi
+           fi
+           _LT_TAGVAR(link_all_deplibs, $1)=yes
+           ;;
+        esac
+        _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir'
+        _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+        _LT_TAGVAR(inherit_rpath, $1)=yes
+        ;;
+
+      linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
+        case $cc_basename in
+          KCC*)
+           # Kuck and Associates, Inc. (KAI) C++ Compiler
+
+           # KCC will only create a shared library if the output file
+           # ends with ".so" (or ".sl" for HP-UX), so rename the library
+           # to its proper name (with version) after linking.
+           _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e 
'\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e 
"s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects 
$compiler_flags --soname $soname -o \$templib; mv \$templib $lib'
+           _LT_TAGVAR(archive_expsym_cmds, $1)='tempext=`echo $shared_ext | 
$SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED 
-e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs 
$postdep_objects $compiler_flags --soname $soname -o \$templib 
$wl-retain-symbols-file,$export_symbols; mv \$templib $lib'
+           # Commands to make compiler produce verbose output that lists
+           # what "hidden" libraries, object files and flags are used when
+           # linking a shared library.
+           #
+           # There doesn't appear to be a way to prevent this compiler from
+           # explicitly linking system object files so we need to strip them
+           # from the output so that they don't get included in the library
+           # dependencies.
+           output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext 
-o libconftest$shared_ext 2>&1 | $GREP "ld"`; rm -f libconftest$shared_ext; 
list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; 
*.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"'
+
+           _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir'
+           _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic'
+
+           # Archives containing C++ object files must be created using
+           # "CC -Bstatic", where "CC" is the KAI C++ compiler.
+           _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs'
+           ;;
+         icpc* | ecpc* )
+           # Intel C++
+           with_gnu_ld=yes
+           # version 8.0 and above of icpc choke on multiply defined symbols
+           # if we add $predep_objects and $postdep_objects, however 7.1 and
+           # earlier do not add the objects themselves.
+           case `$CC -V 2>&1` in
+             *"Version 7."*)
+               _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects 
$libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o 
$lib'
+               _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared 
$predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname 
$wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
+               ;;
+             *)  # Version 8.0 or newer
+               tmp_idyn=
+               case $host_cpu in
+                 ia64*) tmp_idyn=' -i_dynamic';;
+               esac
+               _LT_TAGVAR(archive_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs 
$deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+               _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared'"$tmp_idyn"' 
$libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file 
$wl$export_symbols -o $lib'
+               ;;
+           esac
+           _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+           _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir'
+           _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic'
+           _LT_TAGVAR(whole_archive_flag_spec, 
$1)='$wl--whole-archive$convenience $wl--no-whole-archive'
+           ;;
+          pgCC* | pgcpp*)
+            # Portland Group C++ compiler
+           case `$CC -V` in
+           *pgCC\ [[1-5]].* | *pgcpp\ [[1-5]].*)
+             _LT_TAGVAR(prelink_cmds, $1)='tpldir=Template.dir~
+               rm -rf $tpldir~
+               $CC --prelink_objects --instantiation_dir $tpldir $objs 
$libobjs $compile_deplibs~
+               compile_command="$compile_command `find $tpldir -name \*.o | 
sort | $NL2SP`"'
+             _LT_TAGVAR(old_archive_cmds, $1)='tpldir=Template.dir~
+                rm -rf $tpldir~
+                $CC --prelink_objects --instantiation_dir $tpldir 
$oldobjs$old_deplibs~
+                $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name 
\*.o | sort | $NL2SP`~
+                $RANLIB $oldlib'
+             _LT_TAGVAR(archive_cmds, $1)='tpldir=Template.dir~
+                rm -rf $tpldir~
+                $CC --prelink_objects --instantiation_dir $tpldir 
$predep_objects $libobjs $deplibs $convenience $postdep_objects~
+                $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find 
$tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags $wl-soname 
$wl$soname -o $lib'
+             _LT_TAGVAR(archive_expsym_cmds, $1)='tpldir=Template.dir~
+                rm -rf $tpldir~
+                $CC --prelink_objects --instantiation_dir $tpldir 
$predep_objects $libobjs $deplibs $convenience $postdep_objects~
+                $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find 
$tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags $wl-soname 
$wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
+             ;;
+           *) # Version 6 and above use weak symbols
+             _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag 
$predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname 
$wl$soname -o $lib'
+             _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag 
$predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname 
$wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
+             ;;
+           esac
+
+           _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl--rpath $wl$libdir'
+           _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic'
+           _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for 
conv in $convenience\"\"; do test  -n \"$conv\" && 
new_convenience=\"$new_convenience,$conv\"; done; func_echo_all 
\"$new_convenience\"` $wl--no-whole-archive'
+            ;;
+         cxx*)
+           # Compaq C++
+           _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs 
$deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib'
+           _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects 
$libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname  -o 
$lib $wl-retain-symbols-file $wl$export_symbols'
+
+           runpath_var=LD_RUN_PATH
+           _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir'
+           _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+
+           # Commands to make compiler produce verbose output that lists
+           # what "hidden" libraries, object files and flags are used when
+           # linking a shared library.
+           #
+           # There doesn't appear to be a way to prevent this compiler from
+           # explicitly linking system object files so we need to strip them
+           # from the output so that they don't get included in the library
+           # dependencies.
+           output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v 
conftest.$objext 2>&1 | $GREP "ld"`; templist=`func_echo_all "$templist" | $SED 
"s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list= ; for z in $templist; do case $z in 
conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; 
done; func_echo_all "X$list" | $Xsed'
+           ;;
+         xl* | mpixl* | bgxl*)
+           # IBM XL 8.0 on PPC, with GNU ld
+           _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir'
+           _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic'
+           _LT_TAGVAR(archive_cmds, $1)='$CC -qmkshrobj $libobjs $deplibs 
$compiler_flags $wl-soname $wl$soname -o $lib'
+           if test yes = "$supports_anon_versioning"; then
+             _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > 
$output_objdir/$libname.ver~
+                cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> 
$output_objdir/$libname.ver~
+                echo "local: *; };" >> $output_objdir/$libname.ver~
+                $CC -qmkshrobj $libobjs $deplibs $compiler_flags $wl-soname 
$wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib'
+           fi
+           ;;
+         *)
+           case `$CC -V 2>&1 | sed 5q` in
+           *Sun\ C*)
+             # Sun C++ 5.9
+             _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs'
+             _LT_TAGVAR(archive_cmds, $1)='$CC -G$allow_undefined_flag 
-h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects 
$compiler_flags'
+             _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G$allow_undefined_flag 
-h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects 
$compiler_flags $wl-retain-symbols-file $wl$export_symbols'
+             _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+             _LT_TAGVAR(whole_archive_flag_spec, 
$1)='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test 
-z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all 
\"$new_convenience\"` $wl--no-whole-archive'
+             _LT_TAGVAR(compiler_needs_object, $1)=yes
+
+             # Not sure whether something based on
+             # $CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1
+             # would be better.
+             output_verbose_link_cmd='func_echo_all'
+
+             # Archives containing C++ object files must be created using
+             # "CC -xar", where "CC" is the Sun C++ compiler.  This is
+             # necessary to make sure instantiated templates are included
+             # in the archive.
+             _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs'
+             ;;
+           esac
+           ;;
+       esac
+       ;;
+
+      lynxos*)
+        # FIXME: insert proper C++ library support
+       _LT_TAGVAR(ld_shlibs, $1)=no
+       ;;
+
+      m88k*)
+        # FIXME: insert proper C++ library support
+        _LT_TAGVAR(ld_shlibs, $1)=no
+       ;;
+
+      mvs*)
+        case $cc_basename in
+          cxx*)
+           # FIXME: insert proper C++ library support
+           _LT_TAGVAR(ld_shlibs, $1)=no
+           ;;
+         *)
+           # FIXME: insert proper C++ library support
+           _LT_TAGVAR(ld_shlibs, $1)=no
+           ;;
+       esac
+       ;;
+
+      netbsd*)
+        if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+         _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable  -o $lib 
$predep_objects $libobjs $deplibs $postdep_objects $linker_flags'
+         wlarc=
+         _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+         _LT_TAGVAR(hardcode_direct, $1)=yes
+         _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+       fi
+       # Workaround some broken pre-1.5 toolchains
+       output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | 
$GREP conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"'
+       ;;
+
+      *nto* | *qnx*)
+        _LT_TAGVAR(ld_shlibs, $1)=yes
+       ;;
+
+      openbsd* | bitrig*)
+       if test -f /usr/libexec/ld.so; then
+         _LT_TAGVAR(hardcode_direct, $1)=yes
+         _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+         _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+         _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects 
$libobjs $deplibs $postdep_objects $compiler_flags -o $lib'
+         _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir'
+         if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`"; then
+           _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag 
$predep_objects $libobjs $deplibs $postdep_objects $compiler_flags 
$wl-retain-symbols-file,$export_symbols -o $lib'
+           _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E'
+           _LT_TAGVAR(whole_archive_flag_spec, 
$1)=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive'
+         fi
+         output_verbose_link_cmd=func_echo_all
+       else
+         _LT_TAGVAR(ld_shlibs, $1)=no
+       fi
+       ;;
+
+      osf3* | osf4* | osf5*)
+        case $cc_basename in
+          KCC*)
+           # Kuck and Associates, Inc. (KAI) C++ Compiler
+
+           # KCC will only create a shared library if the output file
+           # ends with ".so" (or ".sl" for HP-UX), so rename the library
+           # to its proper name (with version) after linking.
+           _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e 
'\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo "$lib" | $SED -e 
"s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects 
$compiler_flags --soname $soname -o \$templib; mv \$templib $lib'
+
+           _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir'
+           _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+
+           # Archives containing C++ object files must be created using
+           # the KAI C++ compiler.
+           case $host in
+             osf3*) _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib 
$oldobjs' ;;
+             *) _LT_TAGVAR(old_archive_cmds, $1)='$CC -o $oldlib $oldobjs' ;;
+           esac
+           ;;
+          RCC*)
+           # Rational C++ 2.4.1
+           # FIXME: insert proper C++ library support
+           _LT_TAGVAR(ld_shlibs, $1)=no
+           ;;
+          cxx*)
+           case $host in
+             osf3*)
+               _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved 
$wl\*'
+               _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag 
$predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname 
$soname `test -n "$verstring" && func_echo_all "$wl-set_version $verstring"` 
-update_registry $output_objdir/so_locations -o $lib'
+               _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir'
+               ;;
+             *)
+               _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*'
+               _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag 
$predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym 
-soname $soname `test -n "$verstring" && func_echo_all "-set_version 
$verstring"` -update_registry $output_objdir/so_locations -o $lib'
+               _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat 
$export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~
+                  echo "-hidden">> $lib.exp~
+                  $CC -shared$allow_undefined_flag $predep_objects $libobjs 
$deplibs $postdep_objects $compiler_flags -msym -soname $soname $wl-input 
$wl$lib.exp  `test -n "$verstring" && $ECHO "-set_version $verstring"` 
-update_registry $output_objdir/so_locations -o $lib~
+                  $RM $lib.exp'
+               _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir'
+               ;;
+           esac
+
+           _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+
+           # Commands to make compiler produce verbose output that lists
+           # what "hidden" libraries, object files and flags are used when
+           # linking a shared library.
+           #
+           # There doesn't appear to be a way to prevent this compiler from
+           # explicitly linking system object files so we need to strip them
+           # from the output so that they don't get included in the library
+           # dependencies.
+           output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v 
conftest.$objext 2>&1 | $GREP "ld" | $GREP -v "ld:"`; templist=`func_echo_all 
"$templist" | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list= ; for z in 
$templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) 
list="$list $z";;esac; done; func_echo_all "$list"'
+           ;;
+         *)
+           if test yes,no = "$GXX,$with_gnu_ld"; then
+             _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved 
$wl\*'
+             case $host in
+               osf3*)
+                 _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib 
$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects 
$compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all 
"$wl-set_version $wl$verstring"` $wl-update_registry 
$wl$output_objdir/so_locations -o $lib'
+                 ;;
+               *)
+                 _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib 
$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects 
$compiler_flags $wl-msym $wl-soname $wl$soname `test -n "$verstring" && 
func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry 
$wl$output_objdir/so_locations -o $lib'
+                 ;;
+             esac
+
+             _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir'
+             _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+
+             # Commands to make compiler produce verbose output that lists
+             # what "hidden" libraries, object files and flags are used when
+             # linking a shared library.
+             output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 
2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"'
+
+           else
+             # FIXME: insert proper C++ library support
+             _LT_TAGVAR(ld_shlibs, $1)=no
+           fi
+           ;;
+        esac
+        ;;
+
+      psos*)
+        # FIXME: insert proper C++ library support
+        _LT_TAGVAR(ld_shlibs, $1)=no
+        ;;
+
+      sunos4*)
+        case $cc_basename in
+          CC*)
+           # Sun C++ 4.x
+           # FIXME: insert proper C++ library support
+           _LT_TAGVAR(ld_shlibs, $1)=no
+           ;;
+          lcc*)
+           # Lucid
+           # FIXME: insert proper C++ library support
+           _LT_TAGVAR(ld_shlibs, $1)=no
+           ;;
+          *)
+           # FIXME: insert proper C++ library support
+           _LT_TAGVAR(ld_shlibs, $1)=no
+           ;;
+        esac
+        ;;
+
+      solaris*)
+        case $cc_basename in
+          CC* | sunCC*)
+           # Sun C++ 4.2, 5.x and Centerline C++
+            _LT_TAGVAR(archive_cmds_need_lc,$1)=yes
+           _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs'
+           _LT_TAGVAR(archive_cmds, $1)='$CC -G$allow_undefined_flag -h$soname 
-o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+           _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > 
$lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: 
*; };" >> $lib.exp~
+              $CC -G$allow_undefined_flag $wl-M $wl$lib.exp -h$soname -o $lib 
$predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp'
+
+           _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+           _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+           case $host_os in
+             solaris2.[[0-5]] | solaris2.[[0-5]].*) ;;
+             *)
+               # The compiler driver will combine and reorder linker options,
+               # but understands '-z linker_flag'.
+               # Supported since Solaris 2.6 (maybe 2.5.1?)
+               _LT_TAGVAR(whole_archive_flag_spec, $1)='-z 
allextract$convenience -z defaultextract'
+               ;;
+           esac
+           _LT_TAGVAR(link_all_deplibs, $1)=yes
+
+           output_verbose_link_cmd='func_echo_all'
+
+           # Archives containing C++ object files must be created using
+           # "CC -xar", where "CC" is the Sun C++ compiler.  This is
+           # necessary to make sure instantiated templates are included
+           # in the archive.
+           _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs'
+           ;;
+          gcx*)
+           # Green Hills C++ Compiler
+           _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs 
$deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib'
+
+           # The C++ compiler must be used to create the archive.
+           _LT_TAGVAR(old_archive_cmds, $1)='$CC $LDFLAGS -archive -o $oldlib 
$oldobjs'
+           ;;
+          *)
+           # GNU C++ compiler with Solaris linker
+           if test yes,no = "$GXX,$with_gnu_ld"; then
+             _LT_TAGVAR(no_undefined_flag, $1)=' $wl-z ${wl}defs'
+             if $CC --version | $GREP -v '^2\.7' > /dev/null; then
+               _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib 
$predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h 
$wl$soname -o $lib'
+               _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > 
$lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: 
*; };" >> $lib.exp~
+                  $CC -shared $pic_flag -nostdlib $wl-M $wl$lib.exp $wl-h 
$wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects 
$compiler_flags~$RM $lib.exp'
+
+               # Commands to make compiler produce verbose output that lists
+               # what "hidden" libraries, object files and flags are used when
+               # linking a shared library.
+               output_verbose_link_cmd='$CC -shared $CFLAGS -v 
conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"'
+             else
+               # g++ 2.7 appears to require '-G' NOT '-shared' on this
+               # platform.
+               _LT_TAGVAR(archive_cmds, $1)='$CC -G -nostdlib $predep_objects 
$libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib'
+               _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > 
$lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: 
*; };" >> $lib.exp~
+                  $CC -G -nostdlib $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib 
$predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp'
+
+               # Commands to make compiler produce verbose output that lists
+               # what "hidden" libraries, object files and flags are used when
+               # linking a shared library.
+               output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 
2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"'
+             fi
+
+             _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R $wl$libdir'
+             case $host_os in
+               solaris2.[[0-5]] | solaris2.[[0-5]].*) ;;
+               *)
+                 _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl-z 
${wl}allextract$convenience $wl-z ${wl}defaultextract'
+                 ;;
+             esac
+           fi
+           ;;
+        esac
+        ;;
+
+    sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | 
sco3.2v5.0.[[024]]*)
+      _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text'
+      _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+      _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+      runpath_var='LD_RUN_PATH'
+
+      case $cc_basename in
+        CC*)
+         _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs 
$deplibs $compiler_flags'
+         _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G 
$wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs 
$compiler_flags'
+         ;;
+       *)
+         _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib 
$libobjs $deplibs $compiler_flags'
+         _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared 
$wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs 
$compiler_flags'
+         ;;
+      esac
+      ;;
+
+      sysv5* | sco3.2v5* | sco5v6*)
+       # Note: We CANNOT use -z defs as we might desire, because we do not
+       # link with -lc, and that would cause any symbols used from libc to
+       # always be unresolved, which means just about no library would
+       # ever link correctly.  If we're not using GNU ld we use -z text
+       # though, which does catch some bad symbols but isn't as heavy-handed
+       # as -z defs.
+       _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text'
+       _LT_TAGVAR(allow_undefined_flag, $1)='$wl-z,nodefs'
+       _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+       _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+       _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R,$libdir'
+       _LT_TAGVAR(hardcode_libdir_separator, $1)=':'
+       _LT_TAGVAR(link_all_deplibs, $1)=yes
+       _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-Bexport'
+       runpath_var='LD_RUN_PATH'
+
+       case $cc_basename in
+          CC*)
+           _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs 
$deplibs $compiler_flags'
+           _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G 
$wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs 
$compiler_flags'
+           _LT_TAGVAR(old_archive_cmds, $1)='$CC -Tprelink_objects $oldobjs~
+              '"$_LT_TAGVAR(old_archive_cmds, $1)"
+           _LT_TAGVAR(reload_cmds, $1)='$CC -Tprelink_objects $reload_objs~
+              '"$_LT_TAGVAR(reload_cmds, $1)"
+           ;;
+         *)
+           _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib 
$libobjs $deplibs $compiler_flags'
+           _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared 
$wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs 
$compiler_flags'
+           ;;
+       esac
+      ;;
+
+      tandem*)
+        case $cc_basename in
+          NCC*)
+           # NonStop-UX NCC 3.20
+           # FIXME: insert proper C++ library support
+           _LT_TAGVAR(ld_shlibs, $1)=no
+           ;;
+          *)
+           # FIXME: insert proper C++ library support
+           _LT_TAGVAR(ld_shlibs, $1)=no
+           ;;
+        esac
+        ;;
+
+      vxworks*)
+        # FIXME: insert proper C++ library support
+        _LT_TAGVAR(ld_shlibs, $1)=no
+        ;;
+
+      *)
+        # FIXME: insert proper C++ library support
+        _LT_TAGVAR(ld_shlibs, $1)=no
+        ;;
+    esac
+
+    AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)])
+    test no = "$_LT_TAGVAR(ld_shlibs, $1)" && can_build_shared=no
+
+    _LT_TAGVAR(GCC, $1)=$GXX
+    _LT_TAGVAR(LD, $1)=$LD
+
+    ## CAVEAT EMPTOR:
+    ## There is no encapsulation within the following macros, do not change
+    ## the running order or otherwise move them around unless you know exactly
+    ## what you are doing...
+    _LT_SYS_HIDDEN_LIBDEPS($1)
+    _LT_COMPILER_PIC($1)
+    _LT_COMPILER_C_O($1)
+    _LT_COMPILER_FILE_LOCKS($1)
+    _LT_LINKER_SHLIBS($1)
+    _LT_SYS_DYNAMIC_LINKER($1)
+    _LT_LINKER_HARDCODE_LIBPATH($1)
+
+    _LT_CONFIG($1)
+  fi # test -n "$compiler"
+
+  CC=$lt_save_CC
+  CFLAGS=$lt_save_CFLAGS
+  LDCXX=$LD
+  LD=$lt_save_LD
+  GCC=$lt_save_GCC
+  with_gnu_ld=$lt_save_with_gnu_ld
+  lt_cv_path_LDCXX=$lt_cv_path_LD
+  lt_cv_path_LD=$lt_save_path_LD
+  lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld
+  lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld
+fi # test yes != "$_lt_caught_CXX_error"
+
+AC_LANG_POP
+])# _LT_LANG_CXX_CONFIG
+
+
+# _LT_FUNC_STRIPNAME_CNF
+# ----------------------
+# func_stripname_cnf prefix suffix name
+# strip PREFIX and SUFFIX off of NAME.
+# PREFIX and SUFFIX must not contain globbing or regex special
+# characters, hashes, percent signs, but SUFFIX may contain a leading
+# dot (in which case that matches only a dot).
+#
+# This function is identical to the (non-XSI) version of func_stripname,
+# except this one can be used by m4 code that may be executed by configure,
+# rather than the libtool script.
+m4_defun([_LT_FUNC_STRIPNAME_CNF],[dnl
+AC_REQUIRE([_LT_DECL_SED])
+AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH])
+func_stripname_cnf ()
+{
+  case @S|@2 in
+  .*) func_stripname_result=`$ECHO "@S|@3" | $SED "address@hidden|@1%%; 
address@hidden|@2\$%%"`;;
+  *)  func_stripname_result=`$ECHO "@S|@3" | $SED "address@hidden|@1%%; 
address@hidden|@2\$%%"`;;
+  esac
+} # func_stripname_cnf
+])# _LT_FUNC_STRIPNAME_CNF
+
+
+# _LT_SYS_HIDDEN_LIBDEPS([TAGNAME])
+# ---------------------------------
+# Figure out "hidden" library dependencies from verbose
+# compiler output when linking a shared library.
+# Parse the compiler output and extract the necessary
+# objects, libraries and library flags.
+m4_defun([_LT_SYS_HIDDEN_LIBDEPS],
+[m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+AC_REQUIRE([_LT_FUNC_STRIPNAME_CNF])dnl
+# Dependencies to place before and after the object being linked:
+_LT_TAGVAR(predep_objects, $1)=
+_LT_TAGVAR(postdep_objects, $1)=
+_LT_TAGVAR(predeps, $1)=
+_LT_TAGVAR(postdeps, $1)=
+_LT_TAGVAR(compiler_lib_search_path, $1)=
+
+dnl we can't use the lt_simple_compile_test_code here,
+dnl because it contains code intended for an executable,
+dnl not a library.  It's possible we should let each
+dnl tag define a new lt_????_link_test_code variable,
+dnl but it's only used here...
+m4_if([$1], [], [cat > conftest.$ac_ext <<_LT_EOF
+int a;
+void foo (void) { a = 0; }
+_LT_EOF
+], [$1], [CXX], [cat > conftest.$ac_ext <<_LT_EOF
+class Foo
+{
+public:
+  Foo (void) { a = 0; }
+private:
+  int a;
+};
+_LT_EOF
+], [$1], [F77], [cat > conftest.$ac_ext <<_LT_EOF
+      subroutine foo
+      implicit none
+      integer*4 a
+      a=0
+      return
+      end
+_LT_EOF
+], [$1], [FC], [cat > conftest.$ac_ext <<_LT_EOF
+      subroutine foo
+      implicit none
+      integer a
+      a=0
+      return
+      end
+_LT_EOF
+], [$1], [GCJ], [cat > conftest.$ac_ext <<_LT_EOF
+public class foo {
+  private int a;
+  public void bar (void) {
+    a = 0;
+  }
+};
+_LT_EOF
+], [$1], [GO], [cat > conftest.$ac_ext <<_LT_EOF
+package foo
+func foo() {
+}
+_LT_EOF
+])
+
+_lt_libdeps_save_CFLAGS=$CFLAGS
+case "$CC $CFLAGS " in #(
+*\ -flto*\ *) CFLAGS="$CFLAGS -fno-lto" ;;
+*\ -fwhopr*\ *) CFLAGS="$CFLAGS -fno-whopr" ;;
+*\ -fuse-linker-plugin*\ *) CFLAGS="$CFLAGS -fno-use-linker-plugin" ;;
+esac
+
+dnl Parse the compiler output and extract the necessary
+dnl objects, libraries and library flags.
+if AC_TRY_EVAL(ac_compile); then
+  # Parse the compiler output and extract the necessary
+  # objects, libraries and library flags.
+
+  # Sentinel used to keep track of whether or not we are before
+  # the conftest object file.
+  pre_test_object_deps_done=no
+
+  for p in `eval "$output_verbose_link_cmd"`; do
+    case $prev$p in
+
+    -L* | -R* | -l*)
+       # Some compilers place space between "-{L,R}" and the path.
+       # Remove the space.
+       if test x-L = "$p" ||
+          test x-R = "$p"; then
+        prev=$p
+        continue
+       fi
+
+       # Expand the sysroot to ease extracting the directories later.
+       if test -z "$prev"; then
+         case $p in
+         -L*) func_stripname_cnf '-L' '' "$p"; prev=-L; 
p=$func_stripname_result ;;
+         -R*) func_stripname_cnf '-R' '' "$p"; prev=-R; 
p=$func_stripname_result ;;
+         -l*) func_stripname_cnf '-l' '' "$p"; prev=-l; 
p=$func_stripname_result ;;
+         esac
+       fi
+       case $p in
+       =*) func_stripname_cnf '=' '' "$p"; p=$lt_sysroot$func_stripname_result 
;;
+       esac
+       if test no = "$pre_test_object_deps_done"; then
+        case $prev in
+        -L | -R)
+          # Internal compiler library paths should come after those
+          # provided the user.  The postdeps already come after the
+          # user supplied libs so there is no need to process them.
+          if test -z "$_LT_TAGVAR(compiler_lib_search_path, $1)"; then
+            _LT_TAGVAR(compiler_lib_search_path, $1)=$prev$p
+          else
+            _LT_TAGVAR(compiler_lib_search_path, 
$1)="${_LT_TAGVAR(compiler_lib_search_path, $1)} $prev$p"
+          fi
+          ;;
+        # The "-l" case would never come before the object being
+        # linked, so don't bother handling this case.
+        esac
+       else
+        if test -z "$_LT_TAGVAR(postdeps, $1)"; then
+          _LT_TAGVAR(postdeps, $1)=$prev$p
+        else
+          _LT_TAGVAR(postdeps, $1)="${_LT_TAGVAR(postdeps, $1)} $prev$p"
+        fi
+       fi
+       prev=
+       ;;
+
+    *.lto.$objext) ;; # Ignore GCC LTO objects
+    *.$objext)
+       # This assumes that the test object file only shows up
+       # once in the compiler output.
+       if test "$p" = "conftest.$objext"; then
+        pre_test_object_deps_done=yes
+        continue
+       fi
+
+       if test no = "$pre_test_object_deps_done"; then
+        if test -z "$_LT_TAGVAR(predep_objects, $1)"; then
+          _LT_TAGVAR(predep_objects, $1)=$p
+        else
+          _LT_TAGVAR(predep_objects, $1)="$_LT_TAGVAR(predep_objects, $1) $p"
+        fi
+       else
+        if test -z "$_LT_TAGVAR(postdep_objects, $1)"; then
+          _LT_TAGVAR(postdep_objects, $1)=$p
+        else
+          _LT_TAGVAR(postdep_objects, $1)="$_LT_TAGVAR(postdep_objects, $1) $p"
+        fi
+       fi
+       ;;
+
+    *) ;; # Ignore the rest.
+
+    esac
+  done
+
+  # Clean up.
+  rm -f a.out a.exe
+else
+  echo "libtool.m4: error: problem compiling $1 test program"
+fi
+
+$RM -f confest.$objext
+CFLAGS=$_lt_libdeps_save_CFLAGS
+
+# PORTME: override above test on systems where it is broken
+m4_if([$1], [CXX],
+[case $host_os in
+interix[[3-9]]*)
+  # Interix 3.5 installs completely hosed .la files for C++, so rather than
+  # hack all around it, let's just trust "g++" to DTRT.
+  _LT_TAGVAR(predep_objects,$1)=
+  _LT_TAGVAR(postdep_objects,$1)=
+  _LT_TAGVAR(postdeps,$1)=
+  ;;
+esac
+])
+
+case " $_LT_TAGVAR(postdeps, $1) " in
+*" -lc "*) _LT_TAGVAR(archive_cmds_need_lc, $1)=no ;;
+esac
+ _LT_TAGVAR(compiler_lib_search_dirs, $1)=
+if test -n "${_LT_TAGVAR(compiler_lib_search_path, $1)}"; then
+ _LT_TAGVAR(compiler_lib_search_dirs, $1)=`echo " 
${_LT_TAGVAR(compiler_lib_search_path, $1)}" | $SED -e 's! -L! !g' -e 's!^ !!'`
+fi
+_LT_TAGDECL([], [compiler_lib_search_dirs], [1],
+    [The directories searched by this compiler when creating a shared library])
+_LT_TAGDECL([], [predep_objects], [1],
+    [Dependencies to place before and after the objects being linked to
+    create a shared library])
+_LT_TAGDECL([], [postdep_objects], [1])
+_LT_TAGDECL([], [predeps], [1])
+_LT_TAGDECL([], [postdeps], [1])
+_LT_TAGDECL([], [compiler_lib_search_path], [1],
+    [The library search path used internally by the compiler when linking
+    a shared library])
+])# _LT_SYS_HIDDEN_LIBDEPS
+
+
+# _LT_LANG_F77_CONFIG([TAG])
+# --------------------------
+# Ensure that the configuration variables for a Fortran 77 compiler are
+# suitably defined.  These variables are subsequently used by _LT_CONFIG
+# to write the compiler configuration to 'libtool'.
+m4_defun([_LT_LANG_F77_CONFIG],
+[AC_LANG_PUSH(Fortran 77)
+if test -z "$F77" || test no = "$F77"; then
+  _lt_disable_F77=yes
+fi
+
+_LT_TAGVAR(archive_cmds_need_lc, $1)=no
+_LT_TAGVAR(allow_undefined_flag, $1)=
+_LT_TAGVAR(always_export_symbols, $1)=no
+_LT_TAGVAR(archive_expsym_cmds, $1)=
+_LT_TAGVAR(export_dynamic_flag_spec, $1)=
+_LT_TAGVAR(hardcode_direct, $1)=no
+_LT_TAGVAR(hardcode_direct_absolute, $1)=no
+_LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
+_LT_TAGVAR(hardcode_libdir_separator, $1)=
+_LT_TAGVAR(hardcode_minus_L, $1)=no
+_LT_TAGVAR(hardcode_automatic, $1)=no
+_LT_TAGVAR(inherit_rpath, $1)=no
+_LT_TAGVAR(module_cmds, $1)=
+_LT_TAGVAR(module_expsym_cmds, $1)=
+_LT_TAGVAR(link_all_deplibs, $1)=unknown
+_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds
+_LT_TAGVAR(reload_flag, $1)=$reload_flag
+_LT_TAGVAR(reload_cmds, $1)=$reload_cmds
+_LT_TAGVAR(no_undefined_flag, $1)=
+_LT_TAGVAR(whole_archive_flag_spec, $1)=
+_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no
+
+# Source file extension for f77 test sources.
+ac_ext=f
+
+# Object file extension for compiled f77 test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# No sense in running all these tests if we already determined that
+# the F77 compiler isn't working.  Some variables (like enable_shared)
+# are currently assumed to apply to all compilers on this platform,
+# and will be corrupted by setting them based on a non-working compiler.
+if test yes != "$_lt_disable_F77"; then
+  # Code to be used in simple compile tests
+  lt_simple_compile_test_code="\
+      subroutine t
+      return
+      end
+"
+
+  # Code to be used in simple link tests
+  lt_simple_link_test_code="\
+      program t
+      end
+"
+
+  # ltmain only uses $CC for tagged configurations so make sure $CC is set.
+  _LT_TAG_COMPILER
+
+  # save warnings/boilerplate of simple test code
+  _LT_COMPILER_BOILERPLATE
+  _LT_LINKER_BOILERPLATE
+
+  # Allow CC to be a program name with arguments.
+  lt_save_CC=$CC
+  lt_save_GCC=$GCC
+  lt_save_CFLAGS=$CFLAGS
+  CC=${F77-"f77"}
+  CFLAGS=$FFLAGS
+  compiler=$CC
+  _LT_TAGVAR(compiler, $1)=$CC
+  _LT_CC_BASENAME([$compiler])
+  GCC=$G77
+  if test -n "$compiler"; then
+    AC_MSG_CHECKING([if libtool supports shared libraries])
+    AC_MSG_RESULT([$can_build_shared])
+
+    AC_MSG_CHECKING([whether to build shared libraries])
+    test no = "$can_build_shared" && enable_shared=no
+
+    # On AIX, shared libraries and static libraries use the same namespace, and
+    # are all built from PIC.
+    case $host_os in
+      aix3*)
+        test yes = "$enable_shared" && enable_static=no
+        if test -n "$RANLIB"; then
+          archive_cmds="$archive_cmds~\$RANLIB \$lib"
+          postinstall_cmds='$RANLIB $lib'
+        fi
+        ;;
+      aix[[4-9]]*)
+       if test ia64 != "$host_cpu"; then
+         case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in
+         yes,aix,yes) ;;               # shared object as lib.so file only
+         yes,svr4,*) ;;                # shared object as lib.so archive 
member only
+         yes,*) enable_static=no ;;    # shared object in lib.a archive as well
+         esac
+       fi
+        ;;
+    esac
+    AC_MSG_RESULT([$enable_shared])
+
+    AC_MSG_CHECKING([whether to build static libraries])
+    # Make sure either enable_shared or enable_static is yes.
+    test yes = "$enable_shared" || enable_static=yes
+    AC_MSG_RESULT([$enable_static])
+
+    _LT_TAGVAR(GCC, $1)=$G77
+    _LT_TAGVAR(LD, $1)=$LD
+
+    ## CAVEAT EMPTOR:
+    ## There is no encapsulation within the following macros, do not change
+    ## the running order or otherwise move them around unless you know exactly
+    ## what you are doing...
+    _LT_COMPILER_PIC($1)
+    _LT_COMPILER_C_O($1)
+    _LT_COMPILER_FILE_LOCKS($1)
+    _LT_LINKER_SHLIBS($1)
+    _LT_SYS_DYNAMIC_LINKER($1)
+    _LT_LINKER_HARDCODE_LIBPATH($1)
+
+    _LT_CONFIG($1)
+  fi # test -n "$compiler"
+
+  GCC=$lt_save_GCC
+  CC=$lt_save_CC
+  CFLAGS=$lt_save_CFLAGS
+fi # test yes != "$_lt_disable_F77"
+
+AC_LANG_POP
+])# _LT_LANG_F77_CONFIG
+
+
+# _LT_LANG_FC_CONFIG([TAG])
+# -------------------------
+# Ensure that the configuration variables for a Fortran compiler are
+# suitably defined.  These variables are subsequently used by _LT_CONFIG
+# to write the compiler configuration to 'libtool'.
+m4_defun([_LT_LANG_FC_CONFIG],
+[AC_LANG_PUSH(Fortran)
+
+if test -z "$FC" || test no = "$FC"; then
+  _lt_disable_FC=yes
+fi
+
+_LT_TAGVAR(archive_cmds_need_lc, $1)=no
+_LT_TAGVAR(allow_undefined_flag, $1)=
+_LT_TAGVAR(always_export_symbols, $1)=no
+_LT_TAGVAR(archive_expsym_cmds, $1)=
+_LT_TAGVAR(export_dynamic_flag_spec, $1)=
+_LT_TAGVAR(hardcode_direct, $1)=no
+_LT_TAGVAR(hardcode_direct_absolute, $1)=no
+_LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
+_LT_TAGVAR(hardcode_libdir_separator, $1)=
+_LT_TAGVAR(hardcode_minus_L, $1)=no
+_LT_TAGVAR(hardcode_automatic, $1)=no
+_LT_TAGVAR(inherit_rpath, $1)=no
+_LT_TAGVAR(module_cmds, $1)=
+_LT_TAGVAR(module_expsym_cmds, $1)=
+_LT_TAGVAR(link_all_deplibs, $1)=unknown
+_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds
+_LT_TAGVAR(reload_flag, $1)=$reload_flag
+_LT_TAGVAR(reload_cmds, $1)=$reload_cmds
+_LT_TAGVAR(no_undefined_flag, $1)=
+_LT_TAGVAR(whole_archive_flag_spec, $1)=
+_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no
+
+# Source file extension for fc test sources.
+ac_ext=${ac_fc_srcext-f}
+
+# Object file extension for compiled fc test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# No sense in running all these tests if we already determined that
+# the FC compiler isn't working.  Some variables (like enable_shared)
+# are currently assumed to apply to all compilers on this platform,
+# and will be corrupted by setting them based on a non-working compiler.
+if test yes != "$_lt_disable_FC"; then
+  # Code to be used in simple compile tests
+  lt_simple_compile_test_code="\
+      subroutine t
+      return
+      end
+"
+
+  # Code to be used in simple link tests
+  lt_simple_link_test_code="\
+      program t
+      end
+"
+
+  # ltmain only uses $CC for tagged configurations so make sure $CC is set.
+  _LT_TAG_COMPILER
+
+  # save warnings/boilerplate of simple test code
+  _LT_COMPILER_BOILERPLATE
+  _LT_LINKER_BOILERPLATE
+
+  # Allow CC to be a program name with arguments.
+  lt_save_CC=$CC
+  lt_save_GCC=$GCC
+  lt_save_CFLAGS=$CFLAGS
+  CC=${FC-"f95"}
+  CFLAGS=$FCFLAGS
+  compiler=$CC
+  GCC=$ac_cv_fc_compiler_gnu
+
+  _LT_TAGVAR(compiler, $1)=$CC
+  _LT_CC_BASENAME([$compiler])
+
+  if test -n "$compiler"; then
+    AC_MSG_CHECKING([if libtool supports shared libraries])
+    AC_MSG_RESULT([$can_build_shared])
+
+    AC_MSG_CHECKING([whether to build shared libraries])
+    test no = "$can_build_shared" && enable_shared=no
+
+    # On AIX, shared libraries and static libraries use the same namespace, and
+    # are all built from PIC.
+    case $host_os in
+      aix3*)
+        test yes = "$enable_shared" && enable_static=no
+        if test -n "$RANLIB"; then
+          archive_cmds="$archive_cmds~\$RANLIB \$lib"
+          postinstall_cmds='$RANLIB $lib'
+        fi
+        ;;
+      aix[[4-9]]*)
+       if test ia64 != "$host_cpu"; then
+         case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in
+         yes,aix,yes) ;;               # shared object as lib.so file only
+         yes,svr4,*) ;;                # shared object as lib.so archive 
member only
+         yes,*) enable_static=no ;;    # shared object in lib.a archive as well
+         esac
+       fi
+        ;;
+    esac
+    AC_MSG_RESULT([$enable_shared])
+
+    AC_MSG_CHECKING([whether to build static libraries])
+    # Make sure either enable_shared or enable_static is yes.
+    test yes = "$enable_shared" || enable_static=yes
+    AC_MSG_RESULT([$enable_static])
+
+    _LT_TAGVAR(GCC, $1)=$ac_cv_fc_compiler_gnu
+    _LT_TAGVAR(LD, $1)=$LD
+
+    ## CAVEAT EMPTOR:
+    ## There is no encapsulation within the following macros, do not change
+    ## the running order or otherwise move them around unless you know exactly
+    ## what you are doing...
+    _LT_SYS_HIDDEN_LIBDEPS($1)
+    _LT_COMPILER_PIC($1)
+    _LT_COMPILER_C_O($1)
+    _LT_COMPILER_FILE_LOCKS($1)
+    _LT_LINKER_SHLIBS($1)
+    _LT_SYS_DYNAMIC_LINKER($1)
+    _LT_LINKER_HARDCODE_LIBPATH($1)
+
+    _LT_CONFIG($1)
+  fi # test -n "$compiler"
+
+  GCC=$lt_save_GCC
+  CC=$lt_save_CC
+  CFLAGS=$lt_save_CFLAGS
+fi # test yes != "$_lt_disable_FC"
+
+AC_LANG_POP
+])# _LT_LANG_FC_CONFIG
+
+
+# _LT_LANG_GCJ_CONFIG([TAG])
+# --------------------------
+# Ensure that the configuration variables for the GNU Java Compiler compiler
+# are suitably defined.  These variables are subsequently used by _LT_CONFIG
+# to write the compiler configuration to 'libtool'.
+m4_defun([_LT_LANG_GCJ_CONFIG],
+[AC_REQUIRE([LT_PROG_GCJ])dnl
+AC_LANG_SAVE
+
+# Source file extension for Java test sources.
+ac_ext=java
+
+# Object file extension for compiled Java test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# Code to be used in simple compile tests
+lt_simple_compile_test_code="class foo {}"
+
+# Code to be used in simple link tests
+lt_simple_link_test_code='public class conftest { public static void 
main(String[[]] argv) {}; }'
+
+# ltmain only uses $CC for tagged configurations so make sure $CC is set.
+_LT_TAG_COMPILER
+
+# save warnings/boilerplate of simple test code
+_LT_COMPILER_BOILERPLATE
+_LT_LINKER_BOILERPLATE
+
+# Allow CC to be a program name with arguments.
+lt_save_CC=$CC
+lt_save_CFLAGS=$CFLAGS
+lt_save_GCC=$GCC
+GCC=yes
+CC=${GCJ-"gcj"}
+CFLAGS=$GCJFLAGS
+compiler=$CC
+_LT_TAGVAR(compiler, $1)=$CC
+_LT_TAGVAR(LD, $1)=$LD
+_LT_CC_BASENAME([$compiler])
+
+# GCJ did not exist at the time GCC didn't implicitly link libc in.
+_LT_TAGVAR(archive_cmds_need_lc, $1)=no
+
+_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds
+_LT_TAGVAR(reload_flag, $1)=$reload_flag
+_LT_TAGVAR(reload_cmds, $1)=$reload_cmds
+
+## CAVEAT EMPTOR:
+## There is no encapsulation within the following macros, do not change
+## the running order or otherwise move them around unless you know exactly
+## what you are doing...
+if test -n "$compiler"; then
+  _LT_COMPILER_NO_RTTI($1)
+  _LT_COMPILER_PIC($1)
+  _LT_COMPILER_C_O($1)
+  _LT_COMPILER_FILE_LOCKS($1)
+  _LT_LINKER_SHLIBS($1)
+  _LT_LINKER_HARDCODE_LIBPATH($1)
+
+  _LT_CONFIG($1)
+fi
+
+AC_LANG_RESTORE
+
+GCC=$lt_save_GCC
+CC=$lt_save_CC
+CFLAGS=$lt_save_CFLAGS
+])# _LT_LANG_GCJ_CONFIG
+
+
+# _LT_LANG_GO_CONFIG([TAG])
+# --------------------------
+# Ensure that the configuration variables for the GNU Go compiler
+# are suitably defined.  These variables are subsequently used by _LT_CONFIG
+# to write the compiler configuration to 'libtool'.
+m4_defun([_LT_LANG_GO_CONFIG],
+[AC_REQUIRE([LT_PROG_GO])dnl
+AC_LANG_SAVE
+
+# Source file extension for Go test sources.
+ac_ext=go
+
+# Object file extension for compiled Go test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# Code to be used in simple compile tests
+lt_simple_compile_test_code="package main; func main() { }"
+
+# Code to be used in simple link tests
+lt_simple_link_test_code='package main; func main() { }'
+
+# ltmain only uses $CC for tagged configurations so make sure $CC is set.
+_LT_TAG_COMPILER
+
+# save warnings/boilerplate of simple test code
+_LT_COMPILER_BOILERPLATE
+_LT_LINKER_BOILERPLATE
+
+# Allow CC to be a program name with arguments.
+lt_save_CC=$CC
+lt_save_CFLAGS=$CFLAGS
+lt_save_GCC=$GCC
+GCC=yes
+CC=${GOC-"gccgo"}
+CFLAGS=$GOFLAGS
+compiler=$CC
+_LT_TAGVAR(compiler, $1)=$CC
+_LT_TAGVAR(LD, $1)=$LD
+_LT_CC_BASENAME([$compiler])
+
+# Go did not exist at the time GCC didn't implicitly link libc in.
+_LT_TAGVAR(archive_cmds_need_lc, $1)=no
+
+_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds
+_LT_TAGVAR(reload_flag, $1)=$reload_flag
+_LT_TAGVAR(reload_cmds, $1)=$reload_cmds
+
+## CAVEAT EMPTOR:
+## There is no encapsulation within the following macros, do not change
+## the running order or otherwise move them around unless you know exactly
+## what you are doing...
+if test -n "$compiler"; then
+  _LT_COMPILER_NO_RTTI($1)
+  _LT_COMPILER_PIC($1)
+  _LT_COMPILER_C_O($1)
+  _LT_COMPILER_FILE_LOCKS($1)
+  _LT_LINKER_SHLIBS($1)
+  _LT_LINKER_HARDCODE_LIBPATH($1)
+
+  _LT_CONFIG($1)
+fi
+
+AC_LANG_RESTORE
+
+GCC=$lt_save_GCC
+CC=$lt_save_CC
+CFLAGS=$lt_save_CFLAGS
+])# _LT_LANG_GO_CONFIG
+
+
+# _LT_LANG_RC_CONFIG([TAG])
+# -------------------------
+# Ensure that the configuration variables for the Windows resource compiler
+# are suitably defined.  These variables are subsequently used by _LT_CONFIG
+# to write the compiler configuration to 'libtool'.
+m4_defun([_LT_LANG_RC_CONFIG],
+[AC_REQUIRE([LT_PROG_RC])dnl
+AC_LANG_SAVE
+
+# Source file extension for RC test sources.
+ac_ext=rc
+
+# Object file extension for compiled RC test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# Code to be used in simple compile tests
+lt_simple_compile_test_code='sample MENU { MENUITEM "&Soup", 100, CHECKED }'
+
+# Code to be used in simple link tests
+lt_simple_link_test_code=$lt_simple_compile_test_code
+
+# ltmain only uses $CC for tagged configurations so make sure $CC is set.
+_LT_TAG_COMPILER
+
+# save warnings/boilerplate of simple test code
+_LT_COMPILER_BOILERPLATE
+_LT_LINKER_BOILERPLATE
+
+# Allow CC to be a program name with arguments.
+lt_save_CC=$CC
+lt_save_CFLAGS=$CFLAGS
+lt_save_GCC=$GCC
+GCC=
+CC=${RC-"windres"}
+CFLAGS=
+compiler=$CC
+_LT_TAGVAR(compiler, $1)=$CC
+_LT_CC_BASENAME([$compiler])
+_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes
+
+if test -n "$compiler"; then
+  :
+  _LT_CONFIG($1)
+fi
+
+GCC=$lt_save_GCC
+AC_LANG_RESTORE
+CC=$lt_save_CC
+CFLAGS=$lt_save_CFLAGS
+])# _LT_LANG_RC_CONFIG
+
+
+# LT_PROG_GCJ
+# -----------
+AC_DEFUN([LT_PROG_GCJ],
+[m4_ifdef([AC_PROG_GCJ], [AC_PROG_GCJ],
+  [m4_ifdef([A][M_PROG_GCJ], [A][M_PROG_GCJ],
+    [AC_CHECK_TOOL(GCJ, gcj,)
+      test set = "${GCJFLAGS+set}" || GCJFLAGS="-g -O2"
+      AC_SUBST(GCJFLAGS)])])[]dnl
+])
+
+# Old name:
+AU_ALIAS([LT_AC_PROG_GCJ], [LT_PROG_GCJ])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([LT_AC_PROG_GCJ], [])
+
+
+# LT_PROG_GO
+# ----------
+AC_DEFUN([LT_PROG_GO],
+[AC_CHECK_TOOL(GOC, gccgo,)
+])
+
+
+# LT_PROG_RC
+# ----------
+AC_DEFUN([LT_PROG_RC],
+[AC_CHECK_TOOL(RC, windres,)
+])
+
+# Old name:
+AU_ALIAS([LT_AC_PROG_RC], [LT_PROG_RC])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([LT_AC_PROG_RC], [])
+
+
+# _LT_DECL_EGREP
+# --------------
+# If we don't have a new enough Autoconf to choose the best grep
+# available, choose the one first in the user's PATH.
+m4_defun([_LT_DECL_EGREP],
+[AC_REQUIRE([AC_PROG_EGREP])dnl
+AC_REQUIRE([AC_PROG_FGREP])dnl
+test -z "$GREP" && GREP=grep
+_LT_DECL([], [GREP], [1], [A grep program that handles long lines])
+_LT_DECL([], [EGREP], [1], [An ERE matcher])
+_LT_DECL([], [FGREP], [1], [A literal string matcher])
+dnl Non-bleeding-edge autoconf doesn't subst GREP, so do it here too
+AC_SUBST([GREP])
+])
+
+
+# _LT_DECL_OBJDUMP
+# --------------
+# If we don't have a new enough Autoconf to choose the best objdump
+# available, choose the one first in the user's PATH.
+m4_defun([_LT_DECL_OBJDUMP],
+[AC_CHECK_TOOL(OBJDUMP, objdump, false)
+test -z "$OBJDUMP" && OBJDUMP=objdump
+_LT_DECL([], [OBJDUMP], [1], [An object symbol dumper])
+AC_SUBST([OBJDUMP])
+])
+
+# _LT_DECL_DLLTOOL
+# ----------------
+# Ensure DLLTOOL variable is set.
+m4_defun([_LT_DECL_DLLTOOL],
+[AC_CHECK_TOOL(DLLTOOL, dlltool, false)
+test -z "$DLLTOOL" && DLLTOOL=dlltool
+_LT_DECL([], [DLLTOOL], [1], [DLL creation program])
+AC_SUBST([DLLTOOL])
+])
+
+# _LT_DECL_SED
+# ------------
+# Check for a fully-functional sed program, that truncates
+# as few characters as possible.  Prefer GNU sed if found.
+m4_defun([_LT_DECL_SED],
+[AC_PROG_SED
+test -z "$SED" && SED=sed
+Xsed="$SED -e 1s/^X//"
+_LT_DECL([], [SED], [1], [A sed program that does not truncate output])
+_LT_DECL([], [Xsed], ["\$SED -e 1s/^X//"],
+    [Sed that helps us avoid accidentally triggering echo(1) options like -n])
+])# _LT_DECL_SED
+
+m4_ifndef([AC_PROG_SED], [
+############################################################
+# NOTE: This macro has been submitted for inclusion into   #
+#  GNU Autoconf as AC_PROG_SED.  When it is available in   #
+#  a released version of Autoconf we should remove this    #
+#  macro and use it instead.                               #
+############################################################
+
+m4_defun([AC_PROG_SED],
+[AC_MSG_CHECKING([for a sed that does not truncate output])
+AC_CACHE_VAL(lt_cv_path_SED,
+[# Loop through the user's path and test for sed and gsed.
+# Then use that list of sed's as ones to test for truncation.
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for lt_ac_prog in sed gsed; do
+    for ac_exec_ext in '' $ac_executable_extensions; do
+      if $as_executable_p "$as_dir/$lt_ac_prog$ac_exec_ext"; then
+        lt_ac_sed_list="$lt_ac_sed_list $as_dir/$lt_ac_prog$ac_exec_ext"
+      fi
+    done
+  done
+done
+IFS=$as_save_IFS
+lt_ac_max=0
+lt_ac_count=0
+# Add /usr/xpg4/bin/sed as it is typically found on Solaris
+# along with /bin/sed that truncates output.
+for lt_ac_sed in $lt_ac_sed_list /usr/xpg4/bin/sed; do
+  test ! -f "$lt_ac_sed" && continue
+  cat /dev/null > conftest.in
+  lt_ac_count=0
+  echo $ECHO_N "0123456789$ECHO_C" >conftest.in
+  # Check for GNU sed and select it if it is found.
+  if "$lt_ac_sed" --version 2>&1 < /dev/null | grep 'GNU' > /dev/null; then
+    lt_cv_path_SED=$lt_ac_sed
+    break
+  fi
+  while true; do
+    cat conftest.in conftest.in >conftest.tmp
+    mv conftest.tmp conftest.in
+    cp conftest.in conftest.nl
+    echo >>conftest.nl
+    $lt_ac_sed -e 's/a$//' < conftest.nl >conftest.out || break
+    cmp -s conftest.out conftest.nl || break
+    # 10000 chars as input seems more than enough
+    test 10 -lt "$lt_ac_count" && break
+    lt_ac_count=`expr $lt_ac_count + 1`
+    if test "$lt_ac_count" -gt "$lt_ac_max"; then
+      lt_ac_max=$lt_ac_count
+      lt_cv_path_SED=$lt_ac_sed
+    fi
+  done
+done
+])
+SED=$lt_cv_path_SED
+AC_SUBST([SED])
+AC_MSG_RESULT([$SED])
+])#AC_PROG_SED
+])#m4_ifndef
+
+# Old name:
+AU_ALIAS([LT_AC_PROG_SED], [AC_PROG_SED])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([LT_AC_PROG_SED], [])
+
+
+# _LT_CHECK_SHELL_FEATURES
+# ------------------------
+# Find out whether the shell is Bourne or XSI compatible,
+# or has some other useful features.
+m4_defun([_LT_CHECK_SHELL_FEATURES],
+[if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+  lt_unset=unset
+else
+  lt_unset=false
+fi
+_LT_DECL([], [lt_unset], [0], [whether the shell understands "unset"])dnl
+
+# test EBCDIC or ASCII
+case `echo X|tr X '\101'` in
+ A) # ASCII based system
+    # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr
+  lt_SP2NL='tr \040 \012'
+  lt_NL2SP='tr \015\012 \040\040'
+  ;;
+ *) # EBCDIC based system
+  lt_SP2NL='tr \100 \n'
+  lt_NL2SP='tr \r\n \100\100'
+  ;;
+esac
+_LT_DECL([SP2NL], [lt_SP2NL], [1], [turn spaces into newlines])dnl
+_LT_DECL([NL2SP], [lt_NL2SP], [1], [turn newlines into spaces])dnl
+])# _LT_CHECK_SHELL_FEATURES
+
+
+# _LT_PATH_CONVERSION_FUNCTIONS
+# -----------------------------
+# Determine what file name conversion functions should be used by
+# func_to_host_file (and, implicitly, by func_to_host_path).  These are needed
+# for certain cross-compile configurations and native mingw.
+m4_defun([_LT_PATH_CONVERSION_FUNCTIONS],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+AC_REQUIRE([AC_CANONICAL_BUILD])dnl
+AC_MSG_CHECKING([how to convert $build file names to $host format])
+AC_CACHE_VAL(lt_cv_to_host_file_cmd,
+[case $host in
+  *-*-mingw* )
+    case $build in
+      *-*-mingw* ) # actually msys
+        lt_cv_to_host_file_cmd=func_convert_file_msys_to_w32
+        ;;
+      *-*-cygwin* )
+        lt_cv_to_host_file_cmd=func_convert_file_cygwin_to_w32
+        ;;
+      * ) # otherwise, assume *nix
+        lt_cv_to_host_file_cmd=func_convert_file_nix_to_w32
+        ;;
+    esac
+    ;;
+  *-*-cygwin* )
+    case $build in
+      *-*-mingw* ) # actually msys
+        lt_cv_to_host_file_cmd=func_convert_file_msys_to_cygwin
+        ;;
+      *-*-cygwin* )
+        lt_cv_to_host_file_cmd=func_convert_file_noop
+        ;;
+      * ) # otherwise, assume *nix
+        lt_cv_to_host_file_cmd=func_convert_file_nix_to_cygwin
+        ;;
+    esac
+    ;;
+  * ) # unhandled hosts (and "normal" native builds)
+    lt_cv_to_host_file_cmd=func_convert_file_noop
+    ;;
+esac
+])
+to_host_file_cmd=$lt_cv_to_host_file_cmd
+AC_MSG_RESULT([$lt_cv_to_host_file_cmd])
+_LT_DECL([to_host_file_cmd], [lt_cv_to_host_file_cmd],
+         [0], [convert $build file names to $host format])dnl
+
+AC_MSG_CHECKING([how to convert $build file names to toolchain format])
+AC_CACHE_VAL(lt_cv_to_tool_file_cmd,
+[#assume ordinary cross tools, or native build.
+lt_cv_to_tool_file_cmd=func_convert_file_noop
+case $host in
+  *-*-mingw* )
+    case $build in
+      *-*-mingw* ) # actually msys
+        lt_cv_to_tool_file_cmd=func_convert_file_msys_to_w32
+        ;;
+    esac
+    ;;
+esac
+])
+to_tool_file_cmd=$lt_cv_to_tool_file_cmd
+AC_MSG_RESULT([$lt_cv_to_tool_file_cmd])
+_LT_DECL([to_tool_file_cmd], [lt_cv_to_tool_file_cmd],
+         [0], [convert $build files to toolchain format])dnl
+])# _LT_PATH_CONVERSION_FUNCTIONS
diff --git a/m4/ltoptions.m4 b/m4/ltoptions.m4
new file mode 100644
index 0000000..94b0829
--- /dev/null
+++ b/m4/ltoptions.m4
@@ -0,0 +1,437 @@
+# Helper functions for option handling.                    -*- Autoconf -*-
+#
+#   Copyright (C) 2004-2005, 2007-2009, 2011-2015 Free Software
+#   Foundation, Inc.
+#   Written by Gary V. Vaughan, 2004
+#
+# This file is free software; the Free Software Foundation gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+
+# serial 8 ltoptions.m4
+
+# This is to help aclocal find these macros, as it can't see m4_define.
+AC_DEFUN([LTOPTIONS_VERSION], [m4_if([1])])
+
+
+# _LT_MANGLE_OPTION(MACRO-NAME, OPTION-NAME)
+# ------------------------------------------
+m4_define([_LT_MANGLE_OPTION],
+[[_LT_OPTION_]m4_bpatsubst($1__$2, [[^a-zA-Z0-9_]], [_])])
+
+
+# _LT_SET_OPTION(MACRO-NAME, OPTION-NAME)
+# ---------------------------------------
+# Set option OPTION-NAME for macro MACRO-NAME, and if there is a
+# matching handler defined, dispatch to it.  Other OPTION-NAMEs are
+# saved as a flag.
+m4_define([_LT_SET_OPTION],
+[m4_define(_LT_MANGLE_OPTION([$1], [$2]))dnl
+m4_ifdef(_LT_MANGLE_DEFUN([$1], [$2]),
+        _LT_MANGLE_DEFUN([$1], [$2]),
+    [m4_warning([Unknown $1 option '$2'])])[]dnl
+])
+
+
+# _LT_IF_OPTION(MACRO-NAME, OPTION-NAME, IF-SET, [IF-NOT-SET])
+# ------------------------------------------------------------
+# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise.
+m4_define([_LT_IF_OPTION],
+[m4_ifdef(_LT_MANGLE_OPTION([$1], [$2]), [$3], [$4])])
+
+
+# _LT_UNLESS_OPTIONS(MACRO-NAME, OPTION-LIST, IF-NOT-SET)
+# -------------------------------------------------------
+# Execute IF-NOT-SET unless all options in OPTION-LIST for MACRO-NAME
+# are set.
+m4_define([_LT_UNLESS_OPTIONS],
+[m4_foreach([_LT_Option], m4_split(m4_normalize([$2])),
+           [m4_ifdef(_LT_MANGLE_OPTION([$1], _LT_Option),
+                     [m4_define([$0_found])])])[]dnl
+m4_ifdef([$0_found], [m4_undefine([$0_found])], [$3
+])[]dnl
+])
+
+
+# _LT_SET_OPTIONS(MACRO-NAME, OPTION-LIST)
+# ----------------------------------------
+# OPTION-LIST is a space-separated list of Libtool options associated
+# with MACRO-NAME.  If any OPTION has a matching handler declared with
+# LT_OPTION_DEFINE, dispatch to that macro; otherwise complain about
+# the unknown option and exit.
+m4_defun([_LT_SET_OPTIONS],
+[# Set options
+m4_foreach([_LT_Option], m4_split(m4_normalize([$2])),
+    [_LT_SET_OPTION([$1], _LT_Option)])
+
+m4_if([$1],[LT_INIT],[
+  dnl
+  dnl Simply set some default values (i.e off) if boolean options were not
+  dnl specified:
+  _LT_UNLESS_OPTIONS([LT_INIT], [dlopen], [enable_dlopen=no
+  ])
+  _LT_UNLESS_OPTIONS([LT_INIT], [win32-dll], [enable_win32_dll=no
+  ])
+  dnl
+  dnl If no reference was made to various pairs of opposing options, then
+  dnl we run the default mode handler for the pair.  For example, if neither
+  dnl 'shared' nor 'disable-shared' was passed, we enable building of shared
+  dnl archives by default:
+  _LT_UNLESS_OPTIONS([LT_INIT], [shared disable-shared], [_LT_ENABLE_SHARED])
+  _LT_UNLESS_OPTIONS([LT_INIT], [static disable-static], [_LT_ENABLE_STATIC])
+  _LT_UNLESS_OPTIONS([LT_INIT], [pic-only no-pic], [_LT_WITH_PIC])
+  _LT_UNLESS_OPTIONS([LT_INIT], [fast-install disable-fast-install],
+                  [_LT_ENABLE_FAST_INSTALL])
+  _LT_UNLESS_OPTIONS([LT_INIT], [aix-soname=aix aix-soname=both 
aix-soname=svr4],
+                  [_LT_WITH_AIX_SONAME([aix])])
+  ])
+])# _LT_SET_OPTIONS
+
+
+## --------------------------------- ##
+## Macros to handle LT_INIT options. ##
+## --------------------------------- ##
+
+# _LT_MANGLE_DEFUN(MACRO-NAME, OPTION-NAME)
+# -----------------------------------------
+m4_define([_LT_MANGLE_DEFUN],
+[[_LT_OPTION_DEFUN_]m4_bpatsubst(m4_toupper([$1__$2]), [[^A-Z0-9_]], [_])])
+
+
+# LT_OPTION_DEFINE(MACRO-NAME, OPTION-NAME, CODE)
+# -----------------------------------------------
+m4_define([LT_OPTION_DEFINE],
+[m4_define(_LT_MANGLE_DEFUN([$1], [$2]), [$3])[]dnl
+])# LT_OPTION_DEFINE
+
+
+# dlopen
+# ------
+LT_OPTION_DEFINE([LT_INIT], [dlopen], [enable_dlopen=yes
+])
+
+AU_DEFUN([AC_LIBTOOL_DLOPEN],
+[_LT_SET_OPTION([LT_INIT], [dlopen])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you
+put the 'dlopen' option into LT_INIT's first parameter.])
+])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_DLOPEN], [])
+
+
+# win32-dll
+# ---------
+# Declare package support for building win32 dll's.
+LT_OPTION_DEFINE([LT_INIT], [win32-dll],
+[enable_win32_dll=yes
+
+case $host in
+*-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-cegcc*)
+  AC_CHECK_TOOL(AS, as, false)
+  AC_CHECK_TOOL(DLLTOOL, dlltool, false)
+  AC_CHECK_TOOL(OBJDUMP, objdump, false)
+  ;;
+esac
+
+test -z "$AS" && AS=as
+_LT_DECL([], [AS],      [1], [Assembler program])dnl
+
+test -z "$DLLTOOL" && DLLTOOL=dlltool
+_LT_DECL([], [DLLTOOL], [1], [DLL creation program])dnl
+
+test -z "$OBJDUMP" && OBJDUMP=objdump
+_LT_DECL([], [OBJDUMP], [1], [Object dumper program])dnl
+])# win32-dll
+
+AU_DEFUN([AC_LIBTOOL_WIN32_DLL],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+_LT_SET_OPTION([LT_INIT], [win32-dll])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you
+put the 'win32-dll' option into LT_INIT's first parameter.])
+])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_WIN32_DLL], [])
+
+
+# _LT_ENABLE_SHARED([DEFAULT])
+# ----------------------------
+# implement the --enable-shared flag, and supports the 'shared' and
+# 'disable-shared' LT_INIT options.
+# DEFAULT is either 'yes' or 'no'.  If omitted, it defaults to 'yes'.
+m4_define([_LT_ENABLE_SHARED],
+[m4_define([_LT_ENABLE_SHARED_DEFAULT], [m4_if($1, no, no, yes)])dnl
+AC_ARG_ENABLE([shared],
+    [AS_HELP_STRING([--enable-shared@<:@=PKGS@:>@],
+       [build shared libraries @<:@default=]_LT_ENABLE_SHARED_DEFAULT[@:>@])],
+    [p=${PACKAGE-default}
+    case $enableval in
+    yes) enable_shared=yes ;;
+    no) enable_shared=no ;;
+    *)
+      enable_shared=no
+      # Look at the argument we got.  We use all the common list separators.
+      lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
+      for pkg in $enableval; do
+       IFS=$lt_save_ifs
+       if test "X$pkg" = "X$p"; then
+         enable_shared=yes
+       fi
+      done
+      IFS=$lt_save_ifs
+      ;;
+    esac],
+    [enable_shared=]_LT_ENABLE_SHARED_DEFAULT)
+
+    _LT_DECL([build_libtool_libs], [enable_shared], [0],
+       [Whether or not to build shared libraries])
+])# _LT_ENABLE_SHARED
+
+LT_OPTION_DEFINE([LT_INIT], [shared], [_LT_ENABLE_SHARED([yes])])
+LT_OPTION_DEFINE([LT_INIT], [disable-shared], [_LT_ENABLE_SHARED([no])])
+
+# Old names:
+AC_DEFUN([AC_ENABLE_SHARED],
+[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[shared])
+])
+
+AC_DEFUN([AC_DISABLE_SHARED],
+[_LT_SET_OPTION([LT_INIT], [disable-shared])
+])
+
+AU_DEFUN([AM_ENABLE_SHARED], [AC_ENABLE_SHARED($@)])
+AU_DEFUN([AM_DISABLE_SHARED], [AC_DISABLE_SHARED($@)])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AM_ENABLE_SHARED], [])
+dnl AC_DEFUN([AM_DISABLE_SHARED], [])
+
+
+
+# _LT_ENABLE_STATIC([DEFAULT])
+# ----------------------------
+# implement the --enable-static flag, and support the 'static' and
+# 'disable-static' LT_INIT options.
+# DEFAULT is either 'yes' or 'no'.  If omitted, it defaults to 'yes'.
+m4_define([_LT_ENABLE_STATIC],
+[m4_define([_LT_ENABLE_STATIC_DEFAULT], [m4_if($1, no, no, yes)])dnl
+AC_ARG_ENABLE([static],
+    [AS_HELP_STRING([--enable-static@<:@=PKGS@:>@],
+       [build static libraries @<:@default=]_LT_ENABLE_STATIC_DEFAULT[@:>@])],
+    [p=${PACKAGE-default}
+    case $enableval in
+    yes) enable_static=yes ;;
+    no) enable_static=no ;;
+    *)
+     enable_static=no
+      # Look at the argument we got.  We use all the common list separators.
+      lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
+      for pkg in $enableval; do
+       IFS=$lt_save_ifs
+       if test "X$pkg" = "X$p"; then
+         enable_static=yes
+       fi
+      done
+      IFS=$lt_save_ifs
+      ;;
+    esac],
+    [enable_static=]_LT_ENABLE_STATIC_DEFAULT)
+
+    _LT_DECL([build_old_libs], [enable_static], [0],
+       [Whether or not to build static libraries])
+])# _LT_ENABLE_STATIC
+
+LT_OPTION_DEFINE([LT_INIT], [static], [_LT_ENABLE_STATIC([yes])])
+LT_OPTION_DEFINE([LT_INIT], [disable-static], [_LT_ENABLE_STATIC([no])])
+
+# Old names:
+AC_DEFUN([AC_ENABLE_STATIC],
+[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[static])
+])
+
+AC_DEFUN([AC_DISABLE_STATIC],
+[_LT_SET_OPTION([LT_INIT], [disable-static])
+])
+
+AU_DEFUN([AM_ENABLE_STATIC], [AC_ENABLE_STATIC($@)])
+AU_DEFUN([AM_DISABLE_STATIC], [AC_DISABLE_STATIC($@)])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AM_ENABLE_STATIC], [])
+dnl AC_DEFUN([AM_DISABLE_STATIC], [])
+
+
+
+# _LT_ENABLE_FAST_INSTALL([DEFAULT])
+# ----------------------------------
+# implement the --enable-fast-install flag, and support the 'fast-install'
+# and 'disable-fast-install' LT_INIT options.
+# DEFAULT is either 'yes' or 'no'.  If omitted, it defaults to 'yes'.
+m4_define([_LT_ENABLE_FAST_INSTALL],
+[m4_define([_LT_ENABLE_FAST_INSTALL_DEFAULT], [m4_if($1, no, no, yes)])dnl
+AC_ARG_ENABLE([fast-install],
+    [AS_HELP_STRING([--enable-fast-install@<:@=PKGS@:>@],
+    [optimize for fast installation 
@<:@default=]_LT_ENABLE_FAST_INSTALL_DEFAULT[@:>@])],
+    [p=${PACKAGE-default}
+    case $enableval in
+    yes) enable_fast_install=yes ;;
+    no) enable_fast_install=no ;;
+    *)
+      enable_fast_install=no
+      # Look at the argument we got.  We use all the common list separators.
+      lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
+      for pkg in $enableval; do
+       IFS=$lt_save_ifs
+       if test "X$pkg" = "X$p"; then
+         enable_fast_install=yes
+       fi
+      done
+      IFS=$lt_save_ifs
+      ;;
+    esac],
+    [enable_fast_install=]_LT_ENABLE_FAST_INSTALL_DEFAULT)
+
+_LT_DECL([fast_install], [enable_fast_install], [0],
+        [Whether or not to optimize for fast installation])dnl
+])# _LT_ENABLE_FAST_INSTALL
+
+LT_OPTION_DEFINE([LT_INIT], [fast-install], [_LT_ENABLE_FAST_INSTALL([yes])])
+LT_OPTION_DEFINE([LT_INIT], [disable-fast-install], 
[_LT_ENABLE_FAST_INSTALL([no])])
+
+# Old names:
+AU_DEFUN([AC_ENABLE_FAST_INSTALL],
+[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[fast-install])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you put
+the 'fast-install' option into LT_INIT's first parameter.])
+])
+
+AU_DEFUN([AC_DISABLE_FAST_INSTALL],
+[_LT_SET_OPTION([LT_INIT], [disable-fast-install])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you put
+the 'disable-fast-install' option into LT_INIT's first parameter.])
+])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_ENABLE_FAST_INSTALL], [])
+dnl AC_DEFUN([AM_DISABLE_FAST_INSTALL], [])
+
+
+# _LT_WITH_AIX_SONAME([DEFAULT])
+# ----------------------------------
+# implement the --with-aix-soname flag, and support the `aix-soname=aix'
+# and `aix-soname=both' and `aix-soname=svr4' LT_INIT options. DEFAULT
+# is either `aix', `both' or `svr4'.  If omitted, it defaults to `aix'.
+m4_define([_LT_WITH_AIX_SONAME],
+[m4_define([_LT_WITH_AIX_SONAME_DEFAULT], [m4_if($1, svr4, svr4, m4_if($1, 
both, both, aix))])dnl
+shared_archive_member_spec=
+case $host,$enable_shared in
+power*-*-aix[[5-9]]*,yes)
+  AC_MSG_CHECKING([which variant of shared library versioning to provide])
+  AC_ARG_WITH([aix-soname],
+    [AS_HELP_STRING([--with-aix-soname=aix|svr4|both],
+      [shared library versioning (aka "SONAME") variant to provide on AIX, 
@<:@default=]_LT_WITH_AIX_SONAME_DEFAULT[@:>@.])],
+    [case $withval in
+    aix|svr4|both)
+      ;;
+    *)
+      AC_MSG_ERROR([Unknown argument to --with-aix-soname])
+      ;;
+    esac
+    lt_cv_with_aix_soname=$with_aix_soname],
+    [AC_CACHE_VAL([lt_cv_with_aix_soname],
+      [lt_cv_with_aix_soname=]_LT_WITH_AIX_SONAME_DEFAULT)
+    with_aix_soname=$lt_cv_with_aix_soname])
+  AC_MSG_RESULT([$with_aix_soname])
+  if test aix != "$with_aix_soname"; then
+    # For the AIX way of multilib, we name the shared archive member
+    # based on the bitwidth used, traditionally 'shr.o' or 'shr_64.o',
+    # and 'shr.imp' or 'shr_64.imp', respectively, for the Import File.
+    # Even when GNU compilers ignore OBJECT_MODE but need '-maix64' flag,
+    # the AIX toolchain works better with OBJECT_MODE set (default 32).
+    if test 64 = "${OBJECT_MODE-32}"; then
+      shared_archive_member_spec=shr_64
+    else
+      shared_archive_member_spec=shr
+    fi
+  fi
+  ;;
+*)
+  with_aix_soname=aix
+  ;;
+esac
+
+_LT_DECL([], [shared_archive_member_spec], [0],
+    [Shared archive member basename, for filename based shared library 
versioning on AIX])dnl
+])# _LT_WITH_AIX_SONAME
+
+LT_OPTION_DEFINE([LT_INIT], [aix-soname=aix], [_LT_WITH_AIX_SONAME([aix])])
+LT_OPTION_DEFINE([LT_INIT], [aix-soname=both], [_LT_WITH_AIX_SONAME([both])])
+LT_OPTION_DEFINE([LT_INIT], [aix-soname=svr4], [_LT_WITH_AIX_SONAME([svr4])])
+
+
+# _LT_WITH_PIC([MODE])
+# --------------------
+# implement the --with-pic flag, and support the 'pic-only' and 'no-pic'
+# LT_INIT options.
+# MODE is either 'yes' or 'no'.  If omitted, it defaults to 'both'.
+m4_define([_LT_WITH_PIC],
+[AC_ARG_WITH([pic],
+    [AS_HELP_STRING([--with-pic@<:@=PKGS@:>@],
+       [try to use only PIC/non-PIC objects @<:@default=use both@:>@])],
+    [lt_p=${PACKAGE-default}
+    case $withval in
+    yes|no) pic_mode=$withval ;;
+    *)
+      pic_mode=default
+      # Look at the argument we got.  We use all the common list separators.
+      lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
+      for lt_pkg in $withval; do
+       IFS=$lt_save_ifs
+       if test "X$lt_pkg" = "X$lt_p"; then
+         pic_mode=yes
+       fi
+      done
+      IFS=$lt_save_ifs
+      ;;
+    esac],
+    [pic_mode=m4_default([$1], [default])])
+
+_LT_DECL([], [pic_mode], [0], [What type of objects to build])dnl
+])# _LT_WITH_PIC
+
+LT_OPTION_DEFINE([LT_INIT], [pic-only], [_LT_WITH_PIC([yes])])
+LT_OPTION_DEFINE([LT_INIT], [no-pic], [_LT_WITH_PIC([no])])
+
+# Old name:
+AU_DEFUN([AC_LIBTOOL_PICMODE],
+[_LT_SET_OPTION([LT_INIT], [pic-only])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you
+put the 'pic-only' option into LT_INIT's first parameter.])
+])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_PICMODE], [])
+
+## ----------------- ##
+## LTDL_INIT Options ##
+## ----------------- ##
+
+m4_define([_LTDL_MODE], [])
+LT_OPTION_DEFINE([LTDL_INIT], [nonrecursive],
+                [m4_define([_LTDL_MODE], [nonrecursive])])
+LT_OPTION_DEFINE([LTDL_INIT], [recursive],
+                [m4_define([_LTDL_MODE], [recursive])])
+LT_OPTION_DEFINE([LTDL_INIT], [subproject],
+                [m4_define([_LTDL_MODE], [subproject])])
+
+m4_define([_LTDL_TYPE], [])
+LT_OPTION_DEFINE([LTDL_INIT], [installable],
+                [m4_define([_LTDL_TYPE], [installable])])
+LT_OPTION_DEFINE([LTDL_INIT], [convenience],
+                [m4_define([_LTDL_TYPE], [convenience])])
diff --git a/m4/ltsugar.m4 b/m4/ltsugar.m4
new file mode 100644
index 0000000..48bc934
--- /dev/null
+++ b/m4/ltsugar.m4
@@ -0,0 +1,124 @@
+# ltsugar.m4 -- libtool m4 base layer.                         -*-Autoconf-*-
+#
+# Copyright (C) 2004-2005, 2007-2008, 2011-2015 Free Software
+# Foundation, Inc.
+# Written by Gary V. Vaughan, 2004
+#
+# This file is free software; the Free Software Foundation gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+
+# serial 6 ltsugar.m4
+
+# This is to help aclocal find these macros, as it can't see m4_define.
+AC_DEFUN([LTSUGAR_VERSION], [m4_if([0.1])])
+
+
+# lt_join(SEP, ARG1, [ARG2...])
+# -----------------------------
+# Produce ARG1SEPARG2...SEPARGn, omitting [] arguments and their
+# associated separator.
+# Needed until we can rely on m4_join from Autoconf 2.62, since all earlier
+# versions in m4sugar had bugs.
+m4_define([lt_join],
+[m4_if([$#], [1], [],
+       [$#], [2], [[$2]],
+       [m4_if([$2], [], [], [[$2]_])$0([$1], m4_shift(m4_shift($@)))])])
+m4_define([_lt_join],
+[m4_if([$#$2], [2], [],
+       [m4_if([$2], [], [], [[$1$2]])$0([$1], m4_shift(m4_shift($@)))])])
+
+
+# lt_car(LIST)
+# lt_cdr(LIST)
+# ------------
+# Manipulate m4 lists.
+# These macros are necessary as long as will still need to support
+# Autoconf-2.59, which quotes differently.
+m4_define([lt_car], [[$1]])
+m4_define([lt_cdr],
+[m4_if([$#], 0, [m4_fatal([$0: cannot be called without arguments])],
+       [$#], 1, [],
+       [m4_dquote(m4_shift($@))])])
+m4_define([lt_unquote], $1)
+
+
+# lt_append(MACRO-NAME, STRING, [SEPARATOR])
+# ------------------------------------------
+# Redefine MACRO-NAME to hold its former content plus 'SEPARATOR''STRING'.
+# Note that neither SEPARATOR nor STRING are expanded; they are appended
+# to MACRO-NAME as is (leaving the expansion for when MACRO-NAME is invoked).
+# No SEPARATOR is output if MACRO-NAME was previously undefined (different
+# than defined and empty).
+#
+# This macro is needed until we can rely on Autoconf 2.62, since earlier
+# versions of m4sugar mistakenly expanded SEPARATOR but not STRING.
+m4_define([lt_append],
+[m4_define([$1],
+          m4_ifdef([$1], [m4_defn([$1])[$3]])[$2])])
+
+
+
+# lt_combine(SEP, PREFIX-LIST, INFIX, SUFFIX1, [SUFFIX2...])
+# ----------------------------------------------------------
+# Produce a SEP delimited list of all paired combinations of elements of
+# PREFIX-LIST with SUFFIX1 through SUFFIXn.  Each element of the list
+# has the form PREFIXmINFIXSUFFIXn.
+# Needed until we can rely on m4_combine added in Autoconf 2.62.
+m4_define([lt_combine],
+[m4_if(m4_eval([$# > 3]), [1],
+       [m4_pushdef([_Lt_sep], [m4_define([_Lt_sep], m4_defn([lt_car]))])]]dnl
+[[m4_foreach([_Lt_prefix], [$2],
+            [m4_foreach([_Lt_suffix],
+               ]m4_dquote(m4_dquote(m4_shift(m4_shift(m4_shift($@)))))[,
+       [_Lt_sep([$1])[]m4_defn([_Lt_prefix])[$3]m4_defn([_Lt_suffix])])])])])
+
+
+# lt_if_append_uniq(MACRO-NAME, VARNAME, [SEPARATOR], [UNIQ], [NOT-UNIQ])
+# -----------------------------------------------------------------------
+# Iff MACRO-NAME does not yet contain VARNAME, then append it (delimited
+# by SEPARATOR if supplied) and expand UNIQ, else NOT-UNIQ.
+m4_define([lt_if_append_uniq],
+[m4_ifdef([$1],
+         [m4_if(m4_index([$3]m4_defn([$1])[$3], [$3$2$3]), [-1],
+                [lt_append([$1], [$2], [$3])$4],
+                [$5])],
+         [lt_append([$1], [$2], [$3])$4])])
+
+
+# lt_dict_add(DICT, KEY, VALUE)
+# -----------------------------
+m4_define([lt_dict_add],
+[m4_define([$1($2)], [$3])])
+
+
+# lt_dict_add_subkey(DICT, KEY, SUBKEY, VALUE)
+# --------------------------------------------
+m4_define([lt_dict_add_subkey],
+[m4_define([$1($2:$3)], [$4])])
+
+
+# lt_dict_fetch(DICT, KEY, [SUBKEY])
+# ----------------------------------
+m4_define([lt_dict_fetch],
+[m4_ifval([$3],
+       m4_ifdef([$1($2:$3)], [m4_defn([$1($2:$3)])]),
+    m4_ifdef([$1($2)], [m4_defn([$1($2)])]))])
+
+
+# lt_if_dict_fetch(DICT, KEY, [SUBKEY], VALUE, IF-TRUE, [IF-FALSE])
+# -----------------------------------------------------------------
+m4_define([lt_if_dict_fetch],
+[m4_if(lt_dict_fetch([$1], [$2], [$3]), [$4],
+       [$5],
+    [$6])])
+
+
+# lt_dict_filter(DICT, [SUBKEY], VALUE, [SEPARATOR], KEY, [...])
+# --------------------------------------------------------------
+m4_define([lt_dict_filter],
+[m4_if([$5], [], [],
+  [lt_join(m4_quote(m4_default([$4], [[, ]])),
+           lt_unquote(m4_split(m4_normalize(m4_foreach(_Lt_key, 
lt_car([m4_shiftn(4, $@)]),
+                     [lt_if_dict_fetch([$1], _Lt_key, [$2], [$3], [_Lt_key 
])])))))])[]dnl
+])
diff --git a/m4/ltversion.m4 b/m4/ltversion.m4
new file mode 100644
index 0000000..fa04b52
--- /dev/null
+++ b/m4/ltversion.m4
@@ -0,0 +1,23 @@
+# ltversion.m4 -- version numbers                      -*- Autoconf -*-
+#
+#   Copyright (C) 2004, 2011-2015 Free Software Foundation, Inc.
+#   Written by Scott James Remnant, 2004
+#
+# This file is free software; the Free Software Foundation gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+
+# @configure_input@
+
+# serial 4179 ltversion.m4
+# This file is part of GNU Libtool
+
+m4_define([LT_PACKAGE_VERSION], [2.4.6])
+m4_define([LT_PACKAGE_REVISION], [2.4.6])
+
+AC_DEFUN([LTVERSION_VERSION],
+[macro_version='2.4.6'
+macro_revision='2.4.6'
+_LT_DECL(, macro_version, 0, [Which release of libtool.m4 was used?])
+_LT_DECL(, macro_revision, 0)
+])
diff --git a/m4/lt~obsolete.m4 b/m4/lt~obsolete.m4
new file mode 100644
index 0000000..c6b26f8
--- /dev/null
+++ b/m4/lt~obsolete.m4
@@ -0,0 +1,99 @@
+# lt~obsolete.m4 -- aclocal satisfying obsolete definitions.    -*-Autoconf-*-
+#
+#   Copyright (C) 2004-2005, 2007, 2009, 2011-2015 Free Software
+#   Foundation, Inc.
+#   Written by Scott James Remnant, 2004.
+#
+# This file is free software; the Free Software Foundation gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+
+# serial 5 lt~obsolete.m4
+
+# These exist entirely to fool aclocal when bootstrapping libtool.
+#
+# In the past libtool.m4 has provided macros via AC_DEFUN (or AU_DEFUN),
+# which have later been changed to m4_define as they aren't part of the
+# exported API, or moved to Autoconf or Automake where they belong.
+#
+# The trouble is, aclocal is a bit thick.  It'll see the old AC_DEFUN
+# in /usr/share/aclocal/libtool.m4 and remember it, then when it sees us
+# using a macro with the same name in our local m4/libtool.m4 it'll
+# pull the old libtool.m4 in (it doesn't see our shiny new m4_define
+# and doesn't know about Autoconf macros at all.)
+#
+# So we provide this file, which has a silly filename so it's always
+# included after everything else.  This provides aclocal with the
+# AC_DEFUNs it wants, but when m4 processes it, it doesn't do anything
+# because those macros already exist, or will be overwritten later.
+# We use AC_DEFUN over AU_DEFUN for compatibility with aclocal-1.6.
+#
+# Anytime we withdraw an AC_DEFUN or AU_DEFUN, remember to add it here.
+# Yes, that means every name once taken will need to remain here until
+# we give up compatibility with versions before 1.7, at which point
+# we need to keep only those names which we still refer to.
+
+# This is to help aclocal find these macros, as it can't see m4_define.
+AC_DEFUN([LTOBSOLETE_VERSION], [m4_if([1])])
+
+m4_ifndef([AC_LIBTOOL_LINKER_OPTION],  [AC_DEFUN([AC_LIBTOOL_LINKER_OPTION])])
+m4_ifndef([AC_PROG_EGREP],             [AC_DEFUN([AC_PROG_EGREP])])
+m4_ifndef([_LT_AC_PROG_ECHO_BACKSLASH],        
[AC_DEFUN([_LT_AC_PROG_ECHO_BACKSLASH])])
+m4_ifndef([_LT_AC_SHELL_INIT],         [AC_DEFUN([_LT_AC_SHELL_INIT])])
+m4_ifndef([_LT_AC_SYS_LIBPATH_AIX],    [AC_DEFUN([_LT_AC_SYS_LIBPATH_AIX])])
+m4_ifndef([_LT_PROG_LTMAIN],           [AC_DEFUN([_LT_PROG_LTMAIN])])
+m4_ifndef([_LT_AC_TAGVAR],             [AC_DEFUN([_LT_AC_TAGVAR])])
+m4_ifndef([AC_LTDL_ENABLE_INSTALL],    [AC_DEFUN([AC_LTDL_ENABLE_INSTALL])])
+m4_ifndef([AC_LTDL_PREOPEN],           [AC_DEFUN([AC_LTDL_PREOPEN])])
+m4_ifndef([_LT_AC_SYS_COMPILER],       [AC_DEFUN([_LT_AC_SYS_COMPILER])])
+m4_ifndef([_LT_AC_LOCK],               [AC_DEFUN([_LT_AC_LOCK])])
+m4_ifndef([AC_LIBTOOL_SYS_OLD_ARCHIVE],        
[AC_DEFUN([AC_LIBTOOL_SYS_OLD_ARCHIVE])])
+m4_ifndef([_LT_AC_TRY_DLOPEN_SELF],    [AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF])])
+m4_ifndef([AC_LIBTOOL_PROG_CC_C_O],    [AC_DEFUN([AC_LIBTOOL_PROG_CC_C_O])])
+m4_ifndef([AC_LIBTOOL_SYS_HARD_LINK_LOCKS], 
[AC_DEFUN([AC_LIBTOOL_SYS_HARD_LINK_LOCKS])])
+m4_ifndef([AC_LIBTOOL_OBJDIR],         [AC_DEFUN([AC_LIBTOOL_OBJDIR])])
+m4_ifndef([AC_LTDL_OBJDIR],            [AC_DEFUN([AC_LTDL_OBJDIR])])
+m4_ifndef([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH], 
[AC_DEFUN([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH])])
+m4_ifndef([AC_LIBTOOL_SYS_LIB_STRIP],  [AC_DEFUN([AC_LIBTOOL_SYS_LIB_STRIP])])
+m4_ifndef([AC_PATH_MAGIC],             [AC_DEFUN([AC_PATH_MAGIC])])
+m4_ifndef([AC_PROG_LD_GNU],            [AC_DEFUN([AC_PROG_LD_GNU])])
+m4_ifndef([AC_PROG_LD_RELOAD_FLAG],    [AC_DEFUN([AC_PROG_LD_RELOAD_FLAG])])
+m4_ifndef([AC_DEPLIBS_CHECK_METHOD],   [AC_DEFUN([AC_DEPLIBS_CHECK_METHOD])])
+m4_ifndef([AC_LIBTOOL_PROG_COMPILER_NO_RTTI], 
[AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_NO_RTTI])])
+m4_ifndef([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE], 
[AC_DEFUN([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE])])
+m4_ifndef([AC_LIBTOOL_PROG_COMPILER_PIC], 
[AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_PIC])])
+m4_ifndef([AC_LIBTOOL_PROG_LD_SHLIBS], [AC_DEFUN([AC_LIBTOOL_PROG_LD_SHLIBS])])
+m4_ifndef([AC_LIBTOOL_POSTDEP_PREDEP], [AC_DEFUN([AC_LIBTOOL_POSTDEP_PREDEP])])
+m4_ifndef([LT_AC_PROG_EGREP],          [AC_DEFUN([LT_AC_PROG_EGREP])])
+m4_ifndef([LT_AC_PROG_SED],            [AC_DEFUN([LT_AC_PROG_SED])])
+m4_ifndef([_LT_CC_BASENAME],           [AC_DEFUN([_LT_CC_BASENAME])])
+m4_ifndef([_LT_COMPILER_BOILERPLATE],  [AC_DEFUN([_LT_COMPILER_BOILERPLATE])])
+m4_ifndef([_LT_LINKER_BOILERPLATE],    [AC_DEFUN([_LT_LINKER_BOILERPLATE])])
+m4_ifndef([_AC_PROG_LIBTOOL],          [AC_DEFUN([_AC_PROG_LIBTOOL])])
+m4_ifndef([AC_LIBTOOL_SETUP],          [AC_DEFUN([AC_LIBTOOL_SETUP])])
+m4_ifndef([_LT_AC_CHECK_DLFCN],                
[AC_DEFUN([_LT_AC_CHECK_DLFCN])])
+m4_ifndef([AC_LIBTOOL_SYS_DYNAMIC_LINKER],     
[AC_DEFUN([AC_LIBTOOL_SYS_DYNAMIC_LINKER])])
+m4_ifndef([_LT_AC_TAGCONFIG],          [AC_DEFUN([_LT_AC_TAGCONFIG])])
+m4_ifndef([AC_DISABLE_FAST_INSTALL],   [AC_DEFUN([AC_DISABLE_FAST_INSTALL])])
+m4_ifndef([_LT_AC_LANG_CXX],           [AC_DEFUN([_LT_AC_LANG_CXX])])
+m4_ifndef([_LT_AC_LANG_F77],           [AC_DEFUN([_LT_AC_LANG_F77])])
+m4_ifndef([_LT_AC_LANG_GCJ],           [AC_DEFUN([_LT_AC_LANG_GCJ])])
+m4_ifndef([AC_LIBTOOL_LANG_C_CONFIG],  [AC_DEFUN([AC_LIBTOOL_LANG_C_CONFIG])])
+m4_ifndef([_LT_AC_LANG_C_CONFIG],      [AC_DEFUN([_LT_AC_LANG_C_CONFIG])])
+m4_ifndef([AC_LIBTOOL_LANG_CXX_CONFIG],        
[AC_DEFUN([AC_LIBTOOL_LANG_CXX_CONFIG])])
+m4_ifndef([_LT_AC_LANG_CXX_CONFIG],    [AC_DEFUN([_LT_AC_LANG_CXX_CONFIG])])
+m4_ifndef([AC_LIBTOOL_LANG_F77_CONFIG],        
[AC_DEFUN([AC_LIBTOOL_LANG_F77_CONFIG])])
+m4_ifndef([_LT_AC_LANG_F77_CONFIG],    [AC_DEFUN([_LT_AC_LANG_F77_CONFIG])])
+m4_ifndef([AC_LIBTOOL_LANG_GCJ_CONFIG],        
[AC_DEFUN([AC_LIBTOOL_LANG_GCJ_CONFIG])])
+m4_ifndef([_LT_AC_LANG_GCJ_CONFIG],    [AC_DEFUN([_LT_AC_LANG_GCJ_CONFIG])])
+m4_ifndef([AC_LIBTOOL_LANG_RC_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_RC_CONFIG])])
+m4_ifndef([_LT_AC_LANG_RC_CONFIG],     [AC_DEFUN([_LT_AC_LANG_RC_CONFIG])])
+m4_ifndef([AC_LIBTOOL_CONFIG],         [AC_DEFUN([AC_LIBTOOL_CONFIG])])
+m4_ifndef([_LT_AC_FILE_LTDLL_C],       [AC_DEFUN([_LT_AC_FILE_LTDLL_C])])
+m4_ifndef([_LT_REQUIRED_DARWIN_CHECKS],        
[AC_DEFUN([_LT_REQUIRED_DARWIN_CHECKS])])
+m4_ifndef([_LT_AC_PROG_CXXCPP],                
[AC_DEFUN([_LT_AC_PROG_CXXCPP])])
+m4_ifndef([_LT_PREPARE_SED_QUOTE_VARS],        
[AC_DEFUN([_LT_PREPARE_SED_QUOTE_VARS])])
+m4_ifndef([_LT_PROG_ECHO_BACKSLASH],   [AC_DEFUN([_LT_PROG_ECHO_BACKSLASH])])
+m4_ifndef([_LT_PROG_F77],              [AC_DEFUN([_LT_PROG_F77])])
+m4_ifndef([_LT_PROG_FC],               [AC_DEFUN([_LT_PROG_FC])])
+m4_ifndef([_LT_PROG_CXX],              [AC_DEFUN([_LT_PROG_CXX])])
diff --git a/m4/nls.m4 b/m4/nls.m4
new file mode 100644
index 0000000..003704c
--- /dev/null
+++ b/m4/nls.m4
@@ -0,0 +1,32 @@
+# nls.m4 serial 5 (gettext-0.18)
+dnl Copyright (C) 1995-2003, 2005-2006, 2008-2010 Free Software Foundation,
+dnl Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+dnl
+dnl This file can can be used in projects which are not available under
+dnl the GNU General Public License or the GNU Library General Public
+dnl License but which still want to provide support for the GNU gettext
+dnl functionality.
+dnl Please note that the actual code of the GNU gettext library is covered
+dnl by the GNU Library General Public License, and the rest of the GNU
+dnl gettext package package is covered by the GNU General Public License.
+dnl They are *not* in the public domain.
+
+dnl Authors:
+dnl   Ulrich Drepper <address@hidden>, 1995-2000.
+dnl   Bruno Haible <address@hidden>, 2000-2003.
+
+AC_PREREQ([2.50])
+
+AC_DEFUN([AM_NLS],
+[
+  AC_MSG_CHECKING([whether NLS is requested])
+  dnl Default is enabled NLS
+  AC_ARG_ENABLE([nls],
+    [  --disable-nls           do not use Native Language Support],
+    USE_NLS=$enableval, USE_NLS=yes)
+  AC_MSG_RESULT([$USE_NLS])
+  AC_SUBST([USE_NLS])
+])
diff --git a/m4/pkg.m4 b/m4/pkg.m4
new file mode 100644
index 0000000..f2bfc2d
--- /dev/null
+++ b/m4/pkg.m4
@@ -0,0 +1,57 @@
+
+dnl PKG_CHECK_MODULES(GSTUFF, gtk+-2.0 >= 1.3 glib = 1.3.4, action-if, 
action-not)
+dnl defines GSTUFF_LIBS, GSTUFF_CFLAGS, see pkg-config man page
+dnl also defines GSTUFF_PKG_ERRORS on error
+AC_DEFUN([PKG_CHECK_MODULES], [
+  succeeded=no
+
+  if test -z "$PKG_CONFIG"; then
+    AC_PATH_PROG(PKG_CONFIG, pkg-config, no)
+  fi
+
+  if test "$PKG_CONFIG" = "no" ; then
+     echo "*** The pkg-config script could not be found. Make sure it is"
+     echo "*** in your path, or set the PKG_CONFIG environment variable"
+     echo "*** to the full path to pkg-config."
+     echo "*** Or see http://www.freedesktop.org/software/pkgconfig to get 
pkg-config."
+  else
+     PKG_CONFIG_MIN_VERSION=0.9.0
+     if $PKG_CONFIG --atleast-pkgconfig-version $PKG_CONFIG_MIN_VERSION; then
+        AC_MSG_CHECKING(for $2)
+
+        if $PKG_CONFIG --exists "$2" ; then
+            AC_MSG_RESULT(yes)
+            succeeded=yes
+
+            AC_MSG_CHECKING($1_CFLAGS)
+            $1_CFLAGS=`$PKG_CONFIG --cflags "$2"`
+            AC_MSG_RESULT($$1_CFLAGS)
+
+            AC_MSG_CHECKING($1_LIBS)
+            $1_LIBS=`$PKG_CONFIG --libs "$2"`
+            AC_MSG_RESULT($$1_LIBS)
+        else
+            $1_CFLAGS=""
+            $1_LIBS=""
+            ## If we have a custom action on failure, don't print errors, but 
+            ## do set a variable so people can do so.
+            $1_PKG_ERRORS=`$PKG_CONFIG --errors-to-stdout --print-errors "$2"`
+            ifelse([$4], ,echo $$1_PKG_ERRORS,)
+        fi
+
+        AC_SUBST($1_CFLAGS)
+        AC_SUBST($1_LIBS)
+     else
+        echo "*** Your version of pkg-config is too old. You need version 
$PKG_CONFIG_MIN_VERSION or newer."
+        echo "*** See http://www.freedesktop.org/software/pkgconfig";
+     fi
+  fi
+
+  if test $succeeded = yes; then
+     ifelse([$3], , :, [$3])
+  else
+     ifelse([$4], , AC_MSG_ERROR([Library requirements ($2) not met; consider 
adjusting the PKG_CONFIG_PATH environment variable if your libraries are in a 
nonstandard prefix so pkg-config can find them.]), [$4])
+  fi
+])
+
+
diff --git a/m4/po.m4 b/m4/po.m4
new file mode 100644
index 0000000..47f36a4
--- /dev/null
+++ b/m4/po.m4
@@ -0,0 +1,449 @@
+# po.m4 serial 17 (gettext-0.18)
+dnl Copyright (C) 1995-2010 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+dnl
+dnl This file can can be used in projects which are not available under
+dnl the GNU General Public License or the GNU Library General Public
+dnl License but which still want to provide support for the GNU gettext
+dnl functionality.
+dnl Please note that the actual code of the GNU gettext library is covered
+dnl by the GNU Library General Public License, and the rest of the GNU
+dnl gettext package package is covered by the GNU General Public License.
+dnl They are *not* in the public domain.
+
+dnl Authors:
+dnl   Ulrich Drepper <address@hidden>, 1995-2000.
+dnl   Bruno Haible <address@hidden>, 2000-2003.
+
+AC_PREREQ([2.50])
+
+dnl Checks for all prerequisites of the po subdirectory.
+AC_DEFUN([AM_PO_SUBDIRS],
+[
+  AC_REQUIRE([AC_PROG_MAKE_SET])dnl
+  AC_REQUIRE([AC_PROG_INSTALL])dnl
+  AC_REQUIRE([AM_PROG_MKDIR_P])dnl defined by automake
+  AC_REQUIRE([AM_NLS])dnl
+
+  dnl Release version of the gettext macros. This is used to ensure that
+  dnl the gettext macros and po/Makefile.in.in are in sync.
+  AC_SUBST([GETTEXT_MACRO_VERSION], [0.18])
+
+  dnl Perform the following tests also if --disable-nls has been given,
+  dnl because they are needed for "make dist" to work.
+
+  dnl Search for GNU msgfmt in the PATH.
+  dnl The first test excludes Solaris msgfmt and early GNU msgfmt versions.
+  dnl The second test excludes FreeBSD msgfmt.
+  AM_PATH_PROG_WITH_TEST(MSGFMT, msgfmt,
+    [$ac_dir/$ac_word --statistics /dev/null >&]AS_MESSAGE_LOG_FD[ 2>&1 &&
+     (if $ac_dir/$ac_word --statistics /dev/null 2>&1 >/dev/null | grep usage 
>/dev/null; then exit 1; else exit 0; fi)],
+    :)
+  AC_PATH_PROG([GMSGFMT], [gmsgfmt], [$MSGFMT])
+
+  dnl Test whether it is GNU msgfmt >= 0.15.
+changequote(,)dnl
+  case `$MSGFMT --version | sed 1q | sed -e 's,^[^0-9]*,,'` in
+    '' | 0.[0-9] | 0.[0-9].* | 0.1[0-4] | 0.1[0-4].*) MSGFMT_015=: ;;
+    *) MSGFMT_015=$MSGFMT ;;
+  esac
+changequote([,])dnl
+  AC_SUBST([MSGFMT_015])
+changequote(,)dnl
+  case `$GMSGFMT --version | sed 1q | sed -e 's,^[^0-9]*,,'` in
+    '' | 0.[0-9] | 0.[0-9].* | 0.1[0-4] | 0.1[0-4].*) GMSGFMT_015=: ;;
+    *) GMSGFMT_015=$GMSGFMT ;;
+  esac
+changequote([,])dnl
+  AC_SUBST([GMSGFMT_015])
+
+  dnl Search for GNU xgettext 0.12 or newer in the PATH.
+  dnl The first test excludes Solaris xgettext and early GNU xgettext versions.
+  dnl The second test excludes FreeBSD xgettext.
+  AM_PATH_PROG_WITH_TEST(XGETTEXT, xgettext,
+    [$ac_dir/$ac_word --omit-header --copyright-holder= --msgid-bugs-address= 
/dev/null >&]AS_MESSAGE_LOG_FD[ 2>&1 &&
+     (if $ac_dir/$ac_word --omit-header --copyright-holder= 
--msgid-bugs-address= /dev/null 2>&1 >/dev/null | grep usage >/dev/null; then 
exit 1; else exit 0; fi)],
+    :)
+  dnl Remove leftover from FreeBSD xgettext call.
+  rm -f messages.po
+
+  dnl Test whether it is GNU xgettext >= 0.15.
+changequote(,)dnl
+  case `$XGETTEXT --version | sed 1q | sed -e 's,^[^0-9]*,,'` in
+    '' | 0.[0-9] | 0.[0-9].* | 0.1[0-4] | 0.1[0-4].*) XGETTEXT_015=: ;;
+    *) XGETTEXT_015=$XGETTEXT ;;
+  esac
+changequote([,])dnl
+  AC_SUBST([XGETTEXT_015])
+
+  dnl Search for GNU msgmerge 0.11 or newer in the PATH.
+  AM_PATH_PROG_WITH_TEST(MSGMERGE, msgmerge,
+    [$ac_dir/$ac_word --update -q /dev/null /dev/null >&]AS_MESSAGE_LOG_FD[ 
2>&1], :)
+
+  dnl Installation directories.
+  dnl Autoconf >= 2.60 defines localedir. For older versions of autoconf, we
+  dnl have to define it here, so that it can be used in po/Makefile.
+  test -n "$localedir" || localedir='${datadir}/locale'
+  AC_SUBST([localedir])
+
+  dnl Support for AM_XGETTEXT_OPTION.
+  test -n "${XGETTEXT_EXTRA_OPTIONS+set}" || XGETTEXT_EXTRA_OPTIONS=
+  AC_SUBST([XGETTEXT_EXTRA_OPTIONS])
+
+  AC_CONFIG_COMMANDS([po-directories], [[
+    for ac_file in $CONFIG_FILES; do
+      # Support "outfile[:infile[:infile...]]"
+      case "$ac_file" in
+        *:*) ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
+      esac
+      # PO directories have a Makefile.in generated from Makefile.in.in.
+      case "$ac_file" in */Makefile.in)
+        # Adjust a relative srcdir.
+        ac_dir=`echo "$ac_file"|sed 's%/[^/][^/]*$%%'`
+        ac_dir_suffix="/`echo "$ac_dir"|sed 's%^\./%%'`"
+        ac_dots=`echo "$ac_dir_suffix"|sed 's%/[^/]*%../%g'`
+        # In autoconf-2.13 it is called $ac_given_srcdir.
+        # In autoconf-2.50 it is called $srcdir.
+        test -n "$ac_given_srcdir" || ac_given_srcdir="$srcdir"
+        case "$ac_given_srcdir" in
+          .)  top_srcdir=`echo $ac_dots|sed 's%/$%%'` ;;
+          /*) top_srcdir="$ac_given_srcdir" ;;
+          *)  top_srcdir="$ac_dots$ac_given_srcdir" ;;
+        esac
+        # Treat a directory as a PO directory if and only if it has a
+        # POTFILES.in file. This allows packages to have multiple PO
+        # directories under different names or in different locations.
+        if test -f "$ac_given_srcdir/$ac_dir/POTFILES.in"; then
+          rm -f "$ac_dir/POTFILES"
+          test -n "$as_me" && echo "$as_me: creating $ac_dir/POTFILES" || echo 
"creating $ac_dir/POTFILES"
+          cat "$ac_given_srcdir/$ac_dir/POTFILES.in" | sed -e "/^#/d" -e "/^[  
]*\$/d" -e "s,.*,     $top_srcdir/& \\\\," | sed -e "\$s/\(.*\) \\\\/\1/" > 
"$ac_dir/POTFILES"
+          POMAKEFILEDEPS="POTFILES.in"
+          # ALL_LINGUAS, POFILES, UPDATEPOFILES, DUMMYPOFILES, GMOFILES depend
+          # on $ac_dir but don't depend on user-specified configuration
+          # parameters.
+          if test -f "$ac_given_srcdir/$ac_dir/LINGUAS"; then
+            # The LINGUAS file contains the set of available languages.
+            if test -n "$OBSOLETE_ALL_LINGUAS"; then
+              test -n "$as_me" && echo "$as_me: setting ALL_LINGUAS in 
configure.in is obsolete" || echo "setting ALL_LINGUAS in configure.in is 
obsolete"
+            fi
+            ALL_LINGUAS_=`sed -e "/^#/d" -e "s/#.*//" 
"$ac_given_srcdir/$ac_dir/LINGUAS"`
+            # Hide the ALL_LINGUAS assigment from automake < 1.5.
+            eval 'ALL_LINGUAS''=$ALL_LINGUAS_'
+            POMAKEFILEDEPS="$POMAKEFILEDEPS LINGUAS"
+          else
+            # The set of available languages was given in configure.in.
+            # Hide the ALL_LINGUAS assigment from automake < 1.5.
+            eval 'ALL_LINGUAS''=$OBSOLETE_ALL_LINGUAS'
+          fi
+          # Compute POFILES
+          # as      $(foreach lang, $(ALL_LINGUAS), $(srcdir)/$(lang).po)
+          # Compute UPDATEPOFILES
+          # as      $(foreach lang, $(ALL_LINGUAS), $(lang).po-update)
+          # Compute DUMMYPOFILES
+          # as      $(foreach lang, $(ALL_LINGUAS), $(lang).nop)
+          # Compute GMOFILES
+          # as      $(foreach lang, $(ALL_LINGUAS), $(srcdir)/$(lang).gmo)
+          case "$ac_given_srcdir" in
+            .) srcdirpre= ;;
+            *) srcdirpre='$(srcdir)/' ;;
+          esac
+          POFILES=
+          UPDATEPOFILES=
+          DUMMYPOFILES=
+          GMOFILES=
+          for lang in $ALL_LINGUAS; do
+            POFILES="$POFILES $srcdirpre$lang.po"
+            UPDATEPOFILES="$UPDATEPOFILES $lang.po-update"
+            DUMMYPOFILES="$DUMMYPOFILES $lang.nop"
+            GMOFILES="$GMOFILES $srcdirpre$lang.gmo"
+          done
+          # CATALOGS depends on both $ac_dir and the user's LINGUAS
+          # environment variable.
+          INST_LINGUAS=
+          if test -n "$ALL_LINGUAS"; then
+            for presentlang in $ALL_LINGUAS; do
+              useit=no
+              if test "%UNSET%" != "$LINGUAS"; then
+                desiredlanguages="$LINGUAS"
+              else
+                desiredlanguages="$ALL_LINGUAS"
+              fi
+              for desiredlang in $desiredlanguages; do
+                # Use the presentlang catalog if desiredlang is
+                #   a. equal to presentlang, or
+                #   b. a variant of presentlang (because in this case,
+                #      presentlang can be used as a fallback for messages
+                #      which are not translated in the desiredlang catalog).
+                case "$desiredlang" in
+                  "$presentlang"*) useit=yes;;
+                esac
+              done
+              if test $useit = yes; then
+                INST_LINGUAS="$INST_LINGUAS $presentlang"
+              fi
+            done
+          fi
+          CATALOGS=
+          if test -n "$INST_LINGUAS"; then
+            for lang in $INST_LINGUAS; do
+              CATALOGS="$CATALOGS $lang.gmo"
+            done
+          fi
+          test -n "$as_me" && echo "$as_me: creating $ac_dir/Makefile" || echo 
"creating $ac_dir/Makefile"
+          sed -e "/^POTFILES =/r $ac_dir/POTFILES" -e "/^# Makevars/r 
$ac_given_srcdir/$ac_dir/Makevars" -e "s|@POFILES@|$POFILES|g" -e 
"s|@UPDATEPOFILES@|$UPDATEPOFILES|g" -e "s|@DUMMYPOFILES@|$DUMMYPOFILES|g" -e 
"s|@GMOFILES@|$GMOFILES|g" -e "s|@CATALOGS@|$CATALOGS|g" -e 
"s|@POMAKEFILEDEPS@|$POMAKEFILEDEPS|g" "$ac_dir/Makefile.in" > 
"$ac_dir/Makefile"
+          for f in "$ac_given_srcdir/$ac_dir"/Rules-*; do
+            if test -f "$f"; then
+              case "$f" in
+                *.orig | *.bak | *~) ;;
+                *) cat "$f" >> "$ac_dir/Makefile" ;;
+              esac
+            fi
+          done
+        fi
+        ;;
+      esac
+    done]],
+   [# Capture the value of obsolete ALL_LINGUAS because we need it to compute
+    # POFILES, UPDATEPOFILES, DUMMYPOFILES, GMOFILES, CATALOGS. But hide it
+    # from automake < 1.5.
+    eval 'OBSOLETE_ALL_LINGUAS''="$ALL_LINGUAS"'
+    # Capture the value of LINGUAS because we need it to compute CATALOGS.
+    LINGUAS="${LINGUAS-%UNSET%}"
+   ])
+])
+
+dnl Postprocesses a Makefile in a directory containing PO files.
+AC_DEFUN([AM_POSTPROCESS_PO_MAKEFILE],
+[
+  # When this code is run, in config.status, two variables have already been
+  # set:
+  # - OBSOLETE_ALL_LINGUAS is the value of LINGUAS set in configure.in,
+  # - LINGUAS is the value of the environment variable LINGUAS at configure
+  #   time.
+
+changequote(,)dnl
+  # Adjust a relative srcdir.
+  ac_dir=`echo "$ac_file"|sed 's%/[^/][^/]*$%%'`
+  ac_dir_suffix="/`echo "$ac_dir"|sed 's%^\./%%'`"
+  ac_dots=`echo "$ac_dir_suffix"|sed 's%/[^/]*%../%g'`
+  # In autoconf-2.13 it is called $ac_given_srcdir.
+  # In autoconf-2.50 it is called $srcdir.
+  test -n "$ac_given_srcdir" || ac_given_srcdir="$srcdir"
+  case "$ac_given_srcdir" in
+    .)  top_srcdir=`echo $ac_dots|sed 's%/$%%'` ;;
+    /*) top_srcdir="$ac_given_srcdir" ;;
+    *)  top_srcdir="$ac_dots$ac_given_srcdir" ;;
+  esac
+
+  # Find a way to echo strings without interpreting backslash.
+  if test "X`(echo '\t') 2>/dev/null`" = 'X\t'; then
+    gt_echo='echo'
+  else
+    if test "X`(printf '%s\n' '\t') 2>/dev/null`" = 'X\t'; then
+      gt_echo='printf %s\n'
+    else
+      echo_func () {
+        cat <<EOT
+$*
+EOT
+      }
+      gt_echo='echo_func'
+    fi
+  fi
+
+  # A sed script that extracts the value of VARIABLE from a Makefile.
+  sed_x_variable='
+# Test if the hold space is empty.
+x
+s/P/P/
+x
+ta
+# Yes it was empty. Look if we have the expected variable definition.
+/^[     ]*VARIABLE[     ]*=/{
+  # Seen the first line of the variable definition.
+  s/^[  ]*VARIABLE[     ]*=//
+  ba
+}
+bd
+:a
+# Here we are processing a line from the variable definition.
+# Remove comment, more precisely replace it with a space.
+s/#.*$/ /
+# See if the line ends in a backslash.
+tb
+:b
+s/\\$//
+# Print the line, without the trailing backslash.
+p
+tc
+# There was no trailing backslash. The end of the variable definition is
+# reached. Clear the hold space.
+s/^.*$//
+x
+bd
+:c
+# A trailing backslash means that the variable definition continues in the
+# next line. Put a nonempty string into the hold space to indicate this.
+s/^.*$/P/
+x
+:d
+'
+changequote([,])dnl
+
+  # Set POTFILES to the value of the Makefile variable POTFILES.
+  sed_x_POTFILES=`$gt_echo "$sed_x_variable" | sed -e '/^ *#/d' -e 
's/VARIABLE/POTFILES/g'`
+  POTFILES=`sed -n -e "$sed_x_POTFILES" < "$ac_file"`
+  # Compute POTFILES_DEPS as
+  #   $(foreach file, $(POTFILES), $(top_srcdir)/$(file))
+  POTFILES_DEPS=
+  for file in $POTFILES; do
+    POTFILES_DEPS="$POTFILES_DEPS "'$(top_srcdir)/'"$file"
+  done
+  POMAKEFILEDEPS=""
+
+  if test -n "$OBSOLETE_ALL_LINGUAS"; then
+    test -n "$as_me" && echo "$as_me: setting ALL_LINGUAS in configure.in is 
obsolete" || echo "setting ALL_LINGUAS in configure.in is obsolete"
+  fi
+  if test -f "$ac_given_srcdir/$ac_dir/LINGUAS"; then
+    # The LINGUAS file contains the set of available languages.
+    ALL_LINGUAS_=`sed -e "/^#/d" -e "s/#.*//" 
"$ac_given_srcdir/$ac_dir/LINGUAS"`
+    POMAKEFILEDEPS="$POMAKEFILEDEPS LINGUAS"
+  else
+    # Set ALL_LINGUAS to the value of the Makefile variable LINGUAS.
+    sed_x_LINGUAS=`$gt_echo "$sed_x_variable" | sed -e '/^ *#/d' -e 
's/VARIABLE/LINGUAS/g'`
+    ALL_LINGUAS_=`sed -n -e "$sed_x_LINGUAS" < "$ac_file"`
+  fi
+  # Hide the ALL_LINGUAS assigment from automake < 1.5.
+  eval 'ALL_LINGUAS''=$ALL_LINGUAS_'
+  # Compute POFILES
+  # as      $(foreach lang, $(ALL_LINGUAS), $(srcdir)/$(lang).po)
+  # Compute UPDATEPOFILES
+  # as      $(foreach lang, $(ALL_LINGUAS), $(lang).po-update)
+  # Compute DUMMYPOFILES
+  # as      $(foreach lang, $(ALL_LINGUAS), $(lang).nop)
+  # Compute GMOFILES
+  # as      $(foreach lang, $(ALL_LINGUAS), $(srcdir)/$(lang).gmo)
+  # Compute PROPERTIESFILES
+  # as      $(foreach lang, $(ALL_LINGUAS), 
$(top_srcdir)/$(DOMAIN)_$(lang).properties)
+  # Compute CLASSFILES
+  # as      $(foreach lang, $(ALL_LINGUAS), 
$(top_srcdir)/$(DOMAIN)_$(lang).class)
+  # Compute QMFILES
+  # as      $(foreach lang, $(ALL_LINGUAS), $(srcdir)/$(lang).qm)
+  # Compute MSGFILES
+  # as      $(foreach lang, $(ALL_LINGUAS), $(srcdir)/$(frob $(lang)).msg)
+  # Compute RESOURCESDLLFILES
+  # as      $(foreach lang, $(ALL_LINGUAS), $(srcdir)/$(frob 
$(lang))/$(DOMAIN).resources.dll)
+  case "$ac_given_srcdir" in
+    .) srcdirpre= ;;
+    *) srcdirpre='$(srcdir)/' ;;
+  esac
+  POFILES=
+  UPDATEPOFILES=
+  DUMMYPOFILES=
+  GMOFILES=
+  PROPERTIESFILES=
+  CLASSFILES=
+  QMFILES=
+  MSGFILES=
+  RESOURCESDLLFILES=
+  for lang in $ALL_LINGUAS; do
+    POFILES="$POFILES $srcdirpre$lang.po"
+    UPDATEPOFILES="$UPDATEPOFILES $lang.po-update"
+    DUMMYPOFILES="$DUMMYPOFILES $lang.nop"
+    GMOFILES="$GMOFILES $srcdirpre$lang.gmo"
+    PROPERTIESFILES="$PROPERTIESFILES 
\$(top_srcdir)/\$(DOMAIN)_$lang.properties"
+    CLASSFILES="$CLASSFILES \$(top_srcdir)/\$(DOMAIN)_$lang.class"
+    QMFILES="$QMFILES $srcdirpre$lang.qm"
+    frobbedlang=`echo $lang | sed -e 's/\..*$//' -e 
'y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/'`
+    MSGFILES="$MSGFILES $srcdirpre$frobbedlang.msg"
+    frobbedlang=`echo $lang | sed -e 's/_/-/g' -e 's/^sr-CS/sr-SP/' -e 
's/@latin$/-Latn/' -e 's/@cyrillic$/-Cyrl/' -e 's/^sr-SP$/sr-SP-Latn/' -e 
's/^uz-UZ$/uz-UZ-Latn/'`
+    RESOURCESDLLFILES="$RESOURCESDLLFILES 
$srcdirpre$frobbedlang/\$(DOMAIN).resources.dll"
+  done
+  # CATALOGS depends on both $ac_dir and the user's LINGUAS
+  # environment variable.
+  INST_LINGUAS=
+  if test -n "$ALL_LINGUAS"; then
+    for presentlang in $ALL_LINGUAS; do
+      useit=no
+      if test "%UNSET%" != "$LINGUAS"; then
+        desiredlanguages="$LINGUAS"
+      else
+        desiredlanguages="$ALL_LINGUAS"
+      fi
+      for desiredlang in $desiredlanguages; do
+        # Use the presentlang catalog if desiredlang is
+        #   a. equal to presentlang, or
+        #   b. a variant of presentlang (because in this case,
+        #      presentlang can be used as a fallback for messages
+        #      which are not translated in the desiredlang catalog).
+        case "$desiredlang" in
+          "$presentlang"*) useit=yes;;
+        esac
+      done
+      if test $useit = yes; then
+        INST_LINGUAS="$INST_LINGUAS $presentlang"
+      fi
+    done
+  fi
+  CATALOGS=
+  JAVACATALOGS=
+  QTCATALOGS=
+  TCLCATALOGS=
+  CSHARPCATALOGS=
+  if test -n "$INST_LINGUAS"; then
+    for lang in $INST_LINGUAS; do
+      CATALOGS="$CATALOGS $lang.gmo"
+      JAVACATALOGS="$JAVACATALOGS \$(DOMAIN)_$lang.properties"
+      QTCATALOGS="$QTCATALOGS $lang.qm"
+      frobbedlang=`echo $lang | sed -e 's/\..*$//' -e 
'y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/'`
+      TCLCATALOGS="$TCLCATALOGS $frobbedlang.msg"
+      frobbedlang=`echo $lang | sed -e 's/_/-/g' -e 's/^sr-CS/sr-SP/' -e 
's/@latin$/-Latn/' -e 's/@cyrillic$/-Cyrl/' -e 's/^sr-SP$/sr-SP-Latn/' -e 
's/^uz-UZ$/uz-UZ-Latn/'`
+      CSHARPCATALOGS="$CSHARPCATALOGS $frobbedlang/\$(DOMAIN).resources.dll"
+    done
+  fi
+
+  sed -e "s|@POTFILES_DEPS@|$POTFILES_DEPS|g" -e "s|@POFILES@|$POFILES|g" -e 
"s|@UPDATEPOFILES@|$UPDATEPOFILES|g" -e "s|@DUMMYPOFILES@|$DUMMYPOFILES|g" -e 
"s|@GMOFILES@|$GMOFILES|g" -e "s|@PROPERTIESFILES@|$PROPERTIESFILES|g" -e 
"s|@CLASSFILES@|$CLASSFILES|g" -e "s|@QMFILES@|$QMFILES|g" -e 
"s|@MSGFILES@|$MSGFILES|g" -e "s|@RESOURCESDLLFILES@|$RESOURCESDLLFILES|g" -e 
"s|@CATALOGS@|$CATALOGS|g" -e "s|@JAVACATALOGS@|$JAVACATALOGS|g" -e 
"s|@QTCATALOGS@|$QTCATALOGS|g" -e "s|@TCLCATALOGS@|$TCL [...]
+  if grep -l '@TCLCATALOGS@' "$ac_file" > /dev/null; then
+    # Add dependencies that cannot be formulated as a simple suffix rule.
+    for lang in $ALL_LINGUAS; do
+      frobbedlang=`echo $lang | sed -e 's/\..*$//' -e 
'y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/'`
+      cat >> "$ac_file.tmp" <<EOF
+$frobbedlang.msg: $lang.po
+       @echo "\$(MSGFMT) -c --tcl -d \$(srcdir) -l $lang $srcdirpre$lang.po"; \
+       \$(MSGFMT) -c --tcl -d "\$(srcdir)" -l $lang $srcdirpre$lang.po || { rm 
-f "\$(srcdir)/$frobbedlang.msg"; exit 1; }
+EOF
+    done
+  fi
+  if grep -l '@CSHARPCATALOGS@' "$ac_file" > /dev/null; then
+    # Add dependencies that cannot be formulated as a simple suffix rule.
+    for lang in $ALL_LINGUAS; do
+      frobbedlang=`echo $lang | sed -e 's/_/-/g' -e 's/^sr-CS/sr-SP/' -e 
's/@latin$/-Latn/' -e 's/@cyrillic$/-Cyrl/' -e 's/^sr-SP$/sr-SP-Latn/' -e 
's/^uz-UZ$/uz-UZ-Latn/'`
+      cat >> "$ac_file.tmp" <<EOF
+$frobbedlang/\$(DOMAIN).resources.dll: $lang.po
+       @echo "\$(MSGFMT) -c --csharp -d \$(srcdir) -l $lang $srcdirpre$lang.po 
-r \$(DOMAIN)"; \
+       \$(MSGFMT) -c --csharp -d "\$(srcdir)" -l $lang $srcdirpre$lang.po -r 
"\$(DOMAIN)" || { rm -f "\$(srcdir)/$frobbedlang.msg"; exit 1; }
+EOF
+    done
+  fi
+  if test -n "$POMAKEFILEDEPS"; then
+    cat >> "$ac_file.tmp" <<EOF
+Makefile: $POMAKEFILEDEPS
+EOF
+  fi
+  mv "$ac_file.tmp" "$ac_file"
+])
+
+dnl Initializes the accumulator used by AM_XGETTEXT_OPTION.
+AC_DEFUN([AM_XGETTEXT_OPTION_INIT],
+[
+  XGETTEXT_EXTRA_OPTIONS=
+])
+
+dnl Registers an option to be passed to xgettext in the po subdirectory.
+AC_DEFUN([AM_XGETTEXT_OPTION],
+[
+  AC_REQUIRE([AM_XGETTEXT_OPTION_INIT])
+  XGETTEXT_EXTRA_OPTIONS="$XGETTEXT_EXTRA_OPTIONS $1"
+])
diff --git a/m4/progtest.m4 b/m4/progtest.m4
new file mode 100644
index 0000000..2d804ac
--- /dev/null
+++ b/m4/progtest.m4
@@ -0,0 +1,92 @@
+# progtest.m4 serial 6 (gettext-0.18)
+dnl Copyright (C) 1996-2003, 2005, 2008-2010 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+dnl
+dnl This file can can be used in projects which are not available under
+dnl the GNU General Public License or the GNU Library General Public
+dnl License but which still want to provide support for the GNU gettext
+dnl functionality.
+dnl Please note that the actual code of the GNU gettext library is covered
+dnl by the GNU Library General Public License, and the rest of the GNU
+dnl gettext package package is covered by the GNU General Public License.
+dnl They are *not* in the public domain.
+
+dnl Authors:
+dnl   Ulrich Drepper <address@hidden>, 1996.
+
+AC_PREREQ([2.50])
+
+# Search path for a program which passes the given test.
+
+dnl AM_PATH_PROG_WITH_TEST(VARIABLE, PROG-TO-CHECK-FOR,
+dnl   TEST-PERFORMED-ON-FOUND_PROGRAM [, VALUE-IF-NOT-FOUND [, PATH]])
+AC_DEFUN([AM_PATH_PROG_WITH_TEST],
+[
+# Prepare PATH_SEPARATOR.
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+  echo "#! /bin/sh" >conf$$.sh
+  echo  "exit 0"   >>conf$$.sh
+  chmod +x conf$$.sh
+  if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+    PATH_SEPARATOR=';'
+  else
+    PATH_SEPARATOR=:
+  fi
+  rm -f conf$$.sh
+fi
+
+# Find out how to test for executable files. Don't use a zero-byte file,
+# as systems may use methods other than mode bits to determine executability.
+cat >conf$$.file <<_ASEOF
+#! /bin/sh
+exit 0
+_ASEOF
+chmod +x conf$$.file
+if test -x conf$$.file >/dev/null 2>&1; then
+  ac_executable_p="test -x"
+else
+  ac_executable_p="test -f"
+fi
+rm -f conf$$.file
+
+# Extract the first word of "$2", so it can be a program name with args.
+set dummy $2; ac_word=[$]2
+AC_MSG_CHECKING([for $ac_word])
+AC_CACHE_VAL([ac_cv_path_$1],
+[case "[$]$1" in
+  [[\\/]]* | ?:[[\\/]]*)
+    ac_cv_path_$1="[$]$1" # Let the user override the test with a path.
+    ;;
+  *)
+    ac_save_IFS="$IFS"; IFS=$PATH_SEPARATOR
+    for ac_dir in ifelse([$5], , $PATH, [$5]); do
+      IFS="$ac_save_IFS"
+      test -z "$ac_dir" && ac_dir=.
+      for ac_exec_ext in '' $ac_executable_extensions; do
+        if $ac_executable_p "$ac_dir/$ac_word$ac_exec_ext"; then
+          echo "$as_me: trying $ac_dir/$ac_word..." >&AS_MESSAGE_LOG_FD
+          if [$3]; then
+            ac_cv_path_$1="$ac_dir/$ac_word$ac_exec_ext"
+            break 2
+          fi
+        fi
+      done
+    done
+    IFS="$ac_save_IFS"
+dnl If no 4th arg is given, leave the cache variable unset,
+dnl so AC_PATH_PROGS will keep looking.
+ifelse([$4], , , [  test -z "[$]ac_cv_path_$1" && ac_cv_path_$1="$4"
+])dnl
+    ;;
+esac])dnl
+$1="$ac_cv_path_$1"
+if test ifelse([$4], , [-n "[$]$1"], ["[$]$1" != "$4"]); then
+  AC_MSG_RESULT([$][$1])
+else
+  AC_MSG_RESULT([no])
+fi
+AC_SUBST([$1])dnl
+])
diff --git a/pkgconfig/Makefile.am b/pkgconfig/Makefile.am
new file mode 100644
index 0000000..bc1eca5
--- /dev/null
+++ b/pkgconfig/Makefile.am
@@ -0,0 +1,25 @@
+# This Makefile.am is in the public domain
+pcfiles = \
+       gnunetmulticast.pc \
+       gnunetpsyc.pc \
+       gnunetpsycstore.pc
+
+all-local: $(pcfiles)
+
+cp_verbose = $(cp_verbose_$(V))
+cp_verbose_ = $(cp_verbose_$(AM_DEFAULT_VERBOSITY))
+cp_verbose_0 = @echo "  CP     $@";
+
+%.pc: %.pc
+       $(cp_verbose_0)cp $< $@
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = $(pcfiles)
+
+EXTRA_DIST = \
+       gnunetmulticast.pc.in \
+       gnunetpsyc.pc.in \
+       gnunetpsycstore.pc.in
+
+CLEANFILES = $(pcfiles)
+AM_CPPFLAGS = -I$(top_srcdir)/src/include
diff --git a/pkgconfig/gnunetmulticast.pc.in b/pkgconfig/gnunetmulticast.pc.in
new file mode 100644
index 0000000..2045555
--- /dev/null
+++ b/pkgconfig/gnunetmulticast.pc.in
@@ -0,0 +1,12 @@
address@hidden@
address@hidden@
address@hidden@
address@hidden@
+
+Name: GNUnet MULTICAST
+Description: library to multicast messages to a group of peers
+URL: https://gnunet.org
+Version: @VERSION@
+Requires:
+Libs: -L${libdir} -lgnunetmulticast
+Cflags: -I${includedir}
diff --git a/pkgconfig/gnunetpsyc.pc.in b/pkgconfig/gnunetpsyc.pc.in
new file mode 100644
index 0000000..9cfabdf
--- /dev/null
+++ b/pkgconfig/gnunetpsyc.pc.in
@@ -0,0 +1,12 @@
address@hidden@
address@hidden@
address@hidden@
address@hidden@
+
+Name: GNUnet PSYC
+Description: library for PSYC multicast channel management
+URL: https://gnunet.org
+Version: @VERSION@
+Requires:
+Libs: -L${libdir} -lgnunetpsyc
+Cflags: -I${includedir}
diff --git a/pkgconfig/gnunetpsycstore.pc.in b/pkgconfig/gnunetpsycstore.pc.in
new file mode 100644
index 0000000..765abdc
--- /dev/null
+++ b/pkgconfig/gnunetpsycstore.pc.in
@@ -0,0 +1,12 @@
address@hidden@
address@hidden@
address@hidden@
address@hidden@
+
+Name: GNUnet PSYCSTORE
+Description: library to for persistent storage of PSYC messages
+URL: https://gnunet.org
+Version: @VERSION@
+Requires:
+Libs: -L${libdir} -lgnunetpsycstore
+Cflags: -I${includedir}
diff --git a/po/.gitignore b/po/.gitignore
new file mode 100644
index 0000000..3fbf582
--- /dev/null
+++ b/po/.gitignore
@@ -0,0 +1,6 @@
+Makefile.in
+Makefile
+POTFILES
+gnunet-ext.pot
+remove-potcdate.sed
+stamp-po
diff --git a/po/ChangeLog b/po/ChangeLog
new file mode 100644
index 0000000..16bda42
--- /dev/null
+++ b/po/ChangeLog
@@ -0,0 +1,12 @@
+2012-03-07  gettextize  <address@hidden>
+
+       * Makefile.in.in: New file, from gettext-0.18.1.
+       * Rules-quot: New file, from gettext-0.18.1.
+       * boldquot.sed: New file, from gettext-0.18.1.
+       * address@hidden: New file, from gettext-0.18.1.
+       * address@hidden: New file, from gettext-0.18.1.
+       * insert-header.sin: New file, from gettext-0.18.1.
+       * quot.sed: New file, from gettext-0.18.1.
+       * remove-potcdate.sin: New file, from gettext-0.18.1.
+       * POTFILES.in: New file.
+
diff --git a/po/Makefile.in.in b/po/Makefile.in.in
new file mode 100644
index 0000000..83d8838
--- /dev/null
+++ b/po/Makefile.in.in
@@ -0,0 +1,444 @@
+# Makefile for PO directory in any package using GNU gettext.
+# Copyright (C) 1995-1997, 2000-2007, 2009-2010 by Ulrich Drepper 
<address@hidden>
+#
+# This file can be copied and used freely without restrictions.  It can
+# be used in projects which are not available under the GNU General Public
+# License but which still want to provide support for the GNU gettext
+# functionality.
+# Please note that the actual code of GNU gettext is covered by the GNU
+# General Public License and is *not* in the public domain.
+#
+# Origin: gettext-0.18
+GETTEXT_MACRO_VERSION = 0.18
+
+PACKAGE = @PACKAGE@
+VERSION = @VERSION@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+
+SHELL = /bin/sh
address@hidden@
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+datarootdir = @datarootdir@
+datadir = @datadir@
+localedir = @localedir@
+gettextsrcdir = $(datadir)/gettext/po
+
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+
+# We use $(mkdir_p).
+# In automake <= 1.9.x, $(mkdir_p) is defined either as "mkdir -p --" or as
+# "$(mkinstalldirs)" or as "$(install_sh) -d". For these automake versions,
+# @install_sh@ does not start with $(SHELL), so we add it.
+# In automake >= 1.10, @mkdir_p@ is derived from ${MKDIR_P}, which is defined
+# either as "/path/to/mkdir -p" or ".../install-sh -c -d". For these automake
+# versions, $(mkinstalldirs) and $(install_sh) are unused.
+mkinstalldirs = $(SHELL) @install_sh@ -d
+install_sh = $(SHELL) @install_sh@
+MKDIR_P = @MKDIR_P@
+mkdir_p = @mkdir_p@
+
+GMSGFMT_ = @GMSGFMT@
+GMSGFMT_no = @GMSGFMT@
+GMSGFMT_yes = @GMSGFMT_015@
+GMSGFMT = $(GMSGFMT_$(USE_MSGCTXT))
+MSGFMT_ = @MSGFMT@
+MSGFMT_no = @MSGFMT@
+MSGFMT_yes = @MSGFMT_015@
+MSGFMT = $(MSGFMT_$(USE_MSGCTXT))
+XGETTEXT_ = @XGETTEXT@
+XGETTEXT_no = @XGETTEXT@
+XGETTEXT_yes = @XGETTEXT_015@
+XGETTEXT = $(XGETTEXT_$(USE_MSGCTXT))
+MSGMERGE = msgmerge
+MSGMERGE_UPDATE = @MSGMERGE@ --update
+MSGINIT = msginit
+MSGCONV = msgconv
+MSGFILTER = msgfilter
+
+POFILES = @POFILES@
+GMOFILES = @GMOFILES@
+UPDATEPOFILES = @UPDATEPOFILES@
+DUMMYPOFILES = @DUMMYPOFILES@
+DISTFILES.common = Makefile.in.in remove-potcdate.sin \
+$(DISTFILES.common.extra1) $(DISTFILES.common.extra2) 
$(DISTFILES.common.extra3)
+DISTFILES = $(DISTFILES.common) Makevars POTFILES.in \
+$(POFILES) $(GMOFILES) \
+$(DISTFILES.extra1) $(DISTFILES.extra2) $(DISTFILES.extra3)
+
+POTFILES = \
+
+CATALOGS = @CATALOGS@
+
+# Makevars gets inserted here. (Don't remove this line!)
+
+.SUFFIXES:
+.SUFFIXES: .po .gmo .mo .sed .sin .nop .po-create .po-update
+
+.po.mo:
+       @echo "$(MSGFMT) -c -o $@ $<"; \
+       $(MSGFMT) -c -o t-$@ $< && mv t-$@ $@
+
+.po.gmo:
+       @lang=`echo $* | sed -e 's,.*/,,'`; \
+       test "$(srcdir)" = . && cdcmd="" || cdcmd="cd $(srcdir) && "; \
+       echo "$${cdcmd}rm -f $${lang}.gmo && $(GMSGFMT) -c --statistics 
--verbose -o $${lang}.gmo $${lang}.po"; \
+       cd $(srcdir) && rm -f $${lang}.gmo && $(GMSGFMT) -c --statistics 
--verbose -o t-$${lang}.gmo $${lang}.po && mv t-$${lang}.gmo $${lang}.gmo
+
+.sin.sed:
+       sed -e '/^#/d' $< > t-$@
+       mv t-$@ $@
+
+
+all: check-macro-version address@hidden@
+
+all-yes: stamp-po
+all-no:
+
+# Ensure that the gettext macros and this Makefile.in.in are in sync.
+check-macro-version:
+       @test "$(GETTEXT_MACRO_VERSION)" = "@GETTEXT_MACRO_VERSION@" \
+         || { echo "*** error: gettext infrastructure mismatch: using a 
Makefile.in.in from gettext version $(GETTEXT_MACRO_VERSION) but the autoconf 
macros are from gettext version @GETTEXT_MACRO_VERSION@" 1>&2; \
+              exit 1; \
+            }
+
+# $(srcdir)/$(DOMAIN).pot is only created when needed. When xgettext finds no
+# internationalized messages, no $(srcdir)/$(DOMAIN).pot is created (because
+# we don't want to bother translators with empty POT files). We assume that
+# LINGUAS is empty in this case, i.e. $(POFILES) and $(GMOFILES) are empty.
+# In this case, stamp-po is a nop (i.e. a phony target).
+
+# stamp-po is a timestamp denoting the last time at which the CATALOGS have
+# been loosely updated. Its purpose is that when a developer or translator
+# checks out the package via CVS, and the $(DOMAIN).pot file is not in CVS,
+# "make" will update the $(DOMAIN).pot and the $(CATALOGS), but subsequent
+# invocations of "make" will do nothing. This timestamp would not be necessary
+# if updating the $(CATALOGS) would always touch them; however, the rule for
+# $(POFILES) has been designed to not touch files that don't need to be
+# changed.
+stamp-po: $(srcdir)/$(DOMAIN).pot
+       test ! -f $(srcdir)/$(DOMAIN).pot || \
+         test -z "$(GMOFILES)" || $(MAKE) $(GMOFILES)
+       @test ! -f $(srcdir)/$(DOMAIN).pot || { \
+         echo "touch stamp-po" && \
+         echo timestamp > stamp-poT && \
+         mv stamp-poT stamp-po; \
+       }
+
+# Note: Target 'all' must not depend on target '$(DOMAIN).pot-update',
+# otherwise packages like GCC can not be built if only parts of the source
+# have been downloaded.
+
+# This target rebuilds $(DOMAIN).pot; it is an expensive operation.
+# Note that $(DOMAIN).pot is not touched if it doesn't need to be changed.
+$(DOMAIN).pot-update: $(POTFILES) $(srcdir)/POTFILES.in remove-potcdate.sed
+       if LC_ALL=C grep 'GNU @PACKAGE@' $(top_srcdir)/* 2>/dev/null | grep -v 
'libtool:' >/dev/null; then \
+         package_gnu='GNU '; \
+       else \
+         package_gnu=''; \
+       fi; \
+       if test -n '$(MSGID_BUGS_ADDRESS)' || test '$(PACKAGE_BUGREPORT)' = 
'@'PACKAGE_BUGREPORT'@'; then \
+         msgid_bugs_address='$(MSGID_BUGS_ADDRESS)'; \
+       else \
+         msgid_bugs_address='$(PACKAGE_BUGREPORT)'; \
+       fi; \
+       case `$(XGETTEXT) --version | sed 1q | sed -e 's,^[^0-9]*,,'` in \
+         '' | 0.[0-9] | 0.[0-9].* | 0.1[0-5] | 0.1[0-5].* | 0.16 | 
0.16.[0-1]*) \
+           $(XGETTEXT) --default-domain=$(DOMAIN) --directory=$(top_srcdir) \
+             --add-comments=TRANSLATORS: $(XGETTEXT_OPTIONS) 
@XGETTEXT_EXTRA_OPTIONS@ \
+             --files-from=$(srcdir)/POTFILES.in \
+             --copyright-holder='$(COPYRIGHT_HOLDER)' \
+             --msgid-bugs-address="$$msgid_bugs_address" \
+           ;; \
+         *) \
+           $(XGETTEXT) --default-domain=$(DOMAIN) --directory=$(top_srcdir) \
+             --add-comments=TRANSLATORS: $(XGETTEXT_OPTIONS) 
@XGETTEXT_EXTRA_OPTIONS@ \
+             --files-from=$(srcdir)/POTFILES.in \
+             --copyright-holder='$(COPYRIGHT_HOLDER)' \
+             --package-name="address@hidden@" \
+             --package-version='@VERSION@' \
+             --msgid-bugs-address="$$msgid_bugs_address" \
+           ;; \
+       esac
+       test ! -f $(DOMAIN).po || { \
+         if test -f $(srcdir)/$(DOMAIN).pot; then \
+           sed -f remove-potcdate.sed < $(srcdir)/$(DOMAIN).pot > 
$(DOMAIN).1po && \
+           sed -f remove-potcdate.sed < $(DOMAIN).po > $(DOMAIN).2po && \
+           if cmp $(DOMAIN).1po $(DOMAIN).2po >/dev/null 2>&1; then \
+             rm -f $(DOMAIN).1po $(DOMAIN).2po $(DOMAIN).po; \
+           else \
+             rm -f $(DOMAIN).1po $(DOMAIN).2po $(srcdir)/$(DOMAIN).pot && \
+             mv $(DOMAIN).po $(srcdir)/$(DOMAIN).pot; \
+           fi; \
+         else \
+           mv $(DOMAIN).po $(srcdir)/$(DOMAIN).pot; \
+         fi; \
+       }
+
+# This rule has no dependencies: we don't need to update $(DOMAIN).pot at
+# every "make" invocation, only create it when it is missing.
+# Only "make $(DOMAIN).pot-update" or "make dist" will force an update.
+$(srcdir)/$(DOMAIN).pot:
+       $(MAKE) $(DOMAIN).pot-update
+
+# This target rebuilds a PO file if $(DOMAIN).pot has changed.
+# Note that a PO file is not touched if it doesn't need to be changed.
+$(POFILES): $(srcdir)/$(DOMAIN).pot
+       @lang=`echo $@ | sed -e 's,.*/,,' -e 's/\.po$$//'`; \
+       if test -f "$(srcdir)/$${lang}.po"; then \
+         test "$(srcdir)" = . && cdcmd="" || cdcmd="cd $(srcdir) && "; \
+         echo "$${cdcmd}$(MSGMERGE_UPDATE) $(MSGMERGE_OPTIONS) --lang=$${lang} 
$${lang}.po $(DOMAIN).pot"; \
+         cd $(srcdir) \
+           && { case `$(MSGMERGE_UPDATE) --version | sed 1q | sed -e 
's,^[^0-9]*,,'` in \
+                  '' | 0.[0-9] | 0.[0-9].* | 0.1[0-7] | 0.1[0-7].*) \
+                    $(MSGMERGE_UPDATE) $(MSGMERGE_OPTIONS) $${lang}.po 
$(DOMAIN).pot;; \
+                  *) \
+                    $(MSGMERGE_UPDATE) $(MSGMERGE_OPTIONS) --lang=$${lang} 
$${lang}.po $(DOMAIN).pot;; \
+                esac; \
+              }; \
+       else \
+         $(MAKE) $${lang}.po-create; \
+       fi
+
+
+install: install-exec install-data
+install-exec:
+install-data: address@hidden@
+       if test "$(PACKAGE)" = "gettext-tools"; then \
+         $(mkdir_p) $(DESTDIR)$(gettextsrcdir); \
+         for file in $(DISTFILES.common) Makevars.template; do \
+           $(INSTALL_DATA) $(srcdir)/$$file \
+                           $(DESTDIR)$(gettextsrcdir)/$$file; \
+         done; \
+         for file in Makevars; do \
+           rm -f $(DESTDIR)$(gettextsrcdir)/$$file; \
+         done; \
+       else \
+         : ; \
+       fi
+install-data-no: all
+install-data-yes: all
+       @catalogs='$(CATALOGS)'; \
+       for cat in $$catalogs; do \
+         cat=`basename $$cat`; \
+         lang=`echo $$cat | sed -e 's/\.gmo$$//'`; \
+         dir=$(localedir)/$$lang/LC_MESSAGES; \
+         $(mkdir_p) $(DESTDIR)$$dir; \
+         if test -r $$cat; then realcat=$$cat; else realcat=$(srcdir)/$$cat; 
fi; \
+         $(INSTALL_DATA) $$realcat $(DESTDIR)$$dir/$(DOMAIN).mo; \
+         echo "installing $$realcat as $(DESTDIR)$$dir/$(DOMAIN).mo"; \
+         for lc in '' $(EXTRA_LOCALE_CATEGORIES); do \
+           if test -n "$$lc"; then \
+             if (cd $(DESTDIR)$(localedir)/$$lang && LC_ALL=C ls -l -d $$lc 
2>/dev/null) | grep ' -> ' >/dev/null; then \
+               link=`cd $(DESTDIR)$(localedir)/$$lang && LC_ALL=C ls -l -d 
$$lc | sed -e 's/^.* -> //'`; \
+               mv $(DESTDIR)$(localedir)/$$lang/$$lc 
$(DESTDIR)$(localedir)/$$lang/$$lc.old; \
+               mkdir $(DESTDIR)$(localedir)/$$lang/$$lc; \
+               (cd $(DESTDIR)$(localedir)/$$lang/$$lc.old && \
+                for file in *; do \
+                  if test -f $$file; then \
+                    ln -s ../$$link/$$file 
$(DESTDIR)$(localedir)/$$lang/$$lc/$$file; \
+                  fi; \
+                done); \
+               rm -f $(DESTDIR)$(localedir)/$$lang/$$lc.old; \
+             else \
+               if test -d $(DESTDIR)$(localedir)/$$lang/$$lc; then \
+                 :; \
+               else \
+                 rm -f $(DESTDIR)$(localedir)/$$lang/$$lc; \
+                 mkdir $(DESTDIR)$(localedir)/$$lang/$$lc; \
+               fi; \
+             fi; \
+             rm -f $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo; \
+             ln -s ../LC_MESSAGES/$(DOMAIN).mo 
$(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo 2>/dev/null || \
+             ln $(DESTDIR)$(localedir)/$$lang/LC_MESSAGES/$(DOMAIN).mo 
$(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo 2>/dev/null || \
+             cp -p $(DESTDIR)$(localedir)/$$lang/LC_MESSAGES/$(DOMAIN).mo 
$(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo; \
+             echo "installing $$realcat link as 
$(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo"; \
+           fi; \
+         done; \
+       done
+
+install-strip: install
+
+installdirs: installdirs-exec installdirs-data
+installdirs-exec:
+installdirs-data: address@hidden@
+       if test "$(PACKAGE)" = "gettext-tools"; then \
+         $(mkdir_p) $(DESTDIR)$(gettextsrcdir); \
+       else \
+         : ; \
+       fi
+installdirs-data-no:
+installdirs-data-yes:
+       @catalogs='$(CATALOGS)'; \
+       for cat in $$catalogs; do \
+         cat=`basename $$cat`; \
+         lang=`echo $$cat | sed -e 's/\.gmo$$//'`; \
+         dir=$(localedir)/$$lang/LC_MESSAGES; \
+         $(mkdir_p) $(DESTDIR)$$dir; \
+         for lc in '' $(EXTRA_LOCALE_CATEGORIES); do \
+           if test -n "$$lc"; then \
+             if (cd $(DESTDIR)$(localedir)/$$lang && LC_ALL=C ls -l -d $$lc 
2>/dev/null) | grep ' -> ' >/dev/null; then \
+               link=`cd $(DESTDIR)$(localedir)/$$lang && LC_ALL=C ls -l -d 
$$lc | sed -e 's/^.* -> //'`; \
+               mv $(DESTDIR)$(localedir)/$$lang/$$lc 
$(DESTDIR)$(localedir)/$$lang/$$lc.old; \
+               mkdir $(DESTDIR)$(localedir)/$$lang/$$lc; \
+               (cd $(DESTDIR)$(localedir)/$$lang/$$lc.old && \
+                for file in *; do \
+                  if test -f $$file; then \
+                    ln -s ../$$link/$$file 
$(DESTDIR)$(localedir)/$$lang/$$lc/$$file; \
+                  fi; \
+                done); \
+               rm -f $(DESTDIR)$(localedir)/$$lang/$$lc.old; \
+             else \
+               if test -d $(DESTDIR)$(localedir)/$$lang/$$lc; then \
+                 :; \
+               else \
+                 rm -f $(DESTDIR)$(localedir)/$$lang/$$lc; \
+                 mkdir $(DESTDIR)$(localedir)/$$lang/$$lc; \
+               fi; \
+             fi; \
+           fi; \
+         done; \
+       done
+
+# Define this as empty until I found a useful application.
+installcheck:
+
+uninstall: uninstall-exec uninstall-data
+uninstall-exec:
+uninstall-data: address@hidden@
+       if test "$(PACKAGE)" = "gettext-tools"; then \
+         for file in $(DISTFILES.common) Makevars.template; do \
+           rm -f $(DESTDIR)$(gettextsrcdir)/$$file; \
+         done; \
+       else \
+         : ; \
+       fi
+uninstall-data-no:
+uninstall-data-yes:
+       catalogs='$(CATALOGS)'; \
+       for cat in $$catalogs; do \
+         cat=`basename $$cat`; \
+         lang=`echo $$cat | sed -e 's/\.gmo$$//'`; \
+         for lc in LC_MESSAGES $(EXTRA_LOCALE_CATEGORIES); do \
+           rm -f $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo; \
+         done; \
+       done
+
+check: all
+
+info dvi ps pdf html tags TAGS ctags CTAGS ID:
+
+mostlyclean:
+       rm -f remove-potcdate.sed
+       rm -f stamp-poT
+       rm -f core core.* $(DOMAIN).po $(DOMAIN).1po $(DOMAIN).2po *.new.po
+       rm -fr *.o
+
+clean: mostlyclean
+
+distclean: clean
+       rm -f Makefile Makefile.in POTFILES *.mo
+
+maintainer-clean: distclean
+       @echo "This command is intended for maintainers to use;"
+       @echo "it deletes files that may require special tools to rebuild."
+       rm -f stamp-po $(GMOFILES)
+
+distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir)
+dist distdir:
+       $(MAKE) update-po
+       @$(MAKE) dist2
+# This is a separate target because 'update-po' must be executed before.
+dist2: stamp-po $(DISTFILES)
+       dists="$(DISTFILES)"; \
+       if test "$(PACKAGE)" = "gettext-tools"; then \
+         dists="$$dists Makevars.template"; \
+       fi; \
+       if test -f $(srcdir)/$(DOMAIN).pot; then \
+         dists="$$dists $(DOMAIN).pot stamp-po"; \
+       fi; \
+       if test -f $(srcdir)/ChangeLog; then \
+         dists="$$dists ChangeLog"; \
+       fi; \
+       for i in 0 1 2 3 4 5 6 7 8 9; do \
+         if test -f $(srcdir)/ChangeLog.$$i; then \
+           dists="$$dists ChangeLog.$$i"; \
+         fi; \
+       done; \
+       if test -f $(srcdir)/LINGUAS; then dists="$$dists LINGUAS"; fi; \
+       for file in $$dists; do \
+         if test -f $$file; then \
+           cp -p $$file $(distdir) || exit 1; \
+         else \
+           cp -p $(srcdir)/$$file $(distdir) || exit 1; \
+         fi; \
+       done
+
+update-po: Makefile
+       $(MAKE) $(DOMAIN).pot-update
+       test -z "$(UPDATEPOFILES)" || $(MAKE) $(UPDATEPOFILES)
+       $(MAKE) update-gmo
+
+# General rule for creating PO files.
+
+.nop.po-create:
+       @lang=`echo $@ | sed -e 's/\.po-create$$//'`; \
+       echo "File $$lang.po does not exist. If you are a translator, you can 
create it through 'msginit'." 1>&2; \
+       exit 1
+
+# General rule for updating PO files.
+
+.nop.po-update:
+       @lang=`echo $@ | sed -e 's/\.po-update$$//'`; \
+       if test "$(PACKAGE)" = "gettext-tools"; then PATH=`pwd`/../src:$$PATH; 
fi; \
+       tmpdir=`pwd`; \
+       echo "$$lang:"; \
+       test "$(srcdir)" = . && cdcmd="" || cdcmd="cd $(srcdir) && "; \
+       echo "$${cdcmd}$(MSGMERGE) $(MSGMERGE_OPTIONS) --lang=$$lang $$lang.po 
$(DOMAIN).pot -o $$lang.new.po"; \
+       cd $(srcdir); \
+       if { case `$(MSGMERGE) --version | sed 1q | sed -e 's,^[^0-9]*,,'` in \
+              '' | 0.[0-9] | 0.[0-9].* | 0.1[0-7] | 0.1[0-7].*) \
+                $(MSGMERGE) $(MSGMERGE_OPTIONS) -o $$tmpdir/$$lang.new.po 
$$lang.po $(DOMAIN).pot;; \
+              *) \
+                $(MSGMERGE) $(MSGMERGE_OPTIONS) --lang=$$lang -o 
$$tmpdir/$$lang.new.po $$lang.po $(DOMAIN).pot;; \
+            esac; \
+          }; then \
+         if cmp $$lang.po $$tmpdir/$$lang.new.po >/dev/null 2>&1; then \
+           rm -f $$tmpdir/$$lang.new.po; \
+         else \
+           if mv -f $$tmpdir/$$lang.new.po $$lang.po; then \
+             :; \
+           else \
+             echo "msgmerge for $$lang.po failed: cannot move 
$$tmpdir/$$lang.new.po to $$lang.po" 1>&2; \
+             exit 1; \
+           fi; \
+         fi; \
+       else \
+         echo "msgmerge for $$lang.po failed!" 1>&2; \
+         rm -f $$tmpdir/$$lang.new.po; \
+       fi
+
+$(DUMMYPOFILES):
+
+update-gmo: Makefile $(GMOFILES)
+       @:
+
+# Recreate Makefile by invoking config.status. Explicitly invoke the shell,
+# because execution permission bits may not work on the current file system.
+# Use @SHELL@, which is the shell determined by autoconf for the use by its
+# scripts, not $(SHELL) which is hardwired to /bin/sh and may be deficient.
+Makefile: Makefile.in.in Makevars $(top_builddir)/config.status 
@POMAKEFILEDEPS@
+       cd $(top_builddir) \
+         && @SHELL@ ./config.status $(subdir)/address@hidden po-directories
+
+force:
+
+# Tell versions [3.59,3.63) of GNU make not to export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/po/Makevars b/po/Makevars
new file mode 100644
index 0000000..32692ab
--- /dev/null
+++ b/po/Makevars
@@ -0,0 +1,41 @@
+# Makefile variables for PO directory in any package using GNU gettext.
+
+# Usually the message domain is the same as the package name.
+DOMAIN = $(PACKAGE)
+
+# These two variables depend on the location of this directory.
+subdir = po
+top_builddir = ..
+
+# These options get passed to xgettext.
+XGETTEXT_OPTIONS = --keyword=_ --keyword=N_
+
+# This is the copyright holder that gets inserted into the header of the
+# $(DOMAIN).pot file.  Set this to the copyright holder of the surrounding
+# package.  (Note that the msgstr strings, extracted from the package's
+# sources, belong to the copyright holder of the package.)  Translators are
+# expected to transfer the copyright for their translations to this person
+# or entity, or to disclaim their copyright.  The empty string stands for
+# the public domain; in this case the translators are expected to disclaim
+# their copyright.
+COPYRIGHT_HOLDER = Free Software Foundation, Inc.
+
+# This is the email address or URL to which the translators shall report
+# bugs in the untranslated strings:
+# - Strings which are not entire sentences, see the maintainer guidelines
+#   in the GNU gettext documentation, section 'Preparing Strings'.
+# - Strings which use unclear terms or require additional context to be
+#   understood.
+# - Strings which make invalid assumptions about notation of date, time or
+#   money.
+# - Pluralisation problems.
+# - Incorrect English spelling.
+# - Incorrect formatting.
+# It can be your email address, or a mailing list address where translators
+# can write to without being subscribed, or the URL of a web page through
+# which the translators can contact you.
+MSGID_BUGS_ADDRESS =
+
+# This is the list of locale categories, beyond LC_MESSAGES, for which the
+# message catalogs shall be used.  It is usually empty.
+EXTRA_LOCALE_CATEGORIES =
diff --git a/po/POTFILES.in b/po/POTFILES.in
new file mode 100644
index 0000000..6fb7e2c
--- /dev/null
+++ b/po/POTFILES.in
@@ -0,0 +1,2 @@
+# List of source files which contain translatable strings.
+src/ext/gnunet-ext.c
diff --git a/po/Rules-quot b/po/Rules-quot
new file mode 100644
index 0000000..af52487
--- /dev/null
+++ b/po/Rules-quot
@@ -0,0 +1,47 @@
+# Special Makefile rules for English message catalogs with quotation marks.
+
+DISTFILES.common.extra1 = quot.sed boldquot.sed address@hidden address@hidden 
insert-header.sin Rules-quot
+
+.SUFFIXES: .insert-header .po-update-en
+
address@hidden:
+       $(MAKE) address@hidden
address@hidden:
+       $(MAKE) address@hidden
+
address@hidden: address@hidden
address@hidden: address@hidden
+
+.insert-header.po-update-en:
+       @lang=`echo $@ | sed -e 's/\.po-update-en$$//'`; \
+       if test "$(PACKAGE)" = "gettext"; then PATH=`pwd`/../src:$$PATH; 
GETTEXTLIBDIR=`cd $(top_srcdir)/src && pwd`; export GETTEXTLIBDIR; fi; \
+       tmpdir=`pwd`; \
+       echo "$$lang:"; \
+       ll=`echo $$lang | sed -e 's/@.*//'`; \
+       LC_ALL=C; export LC_ALL; \
+       cd $(srcdir); \
+       if $(MSGINIT) -i $(DOMAIN).pot --no-translator -l $$lang -o - 
2>/dev/null | sed -f $$tmpdir/$$lang.insert-header | $(MSGCONV) -t UTF-8 | 
$(MSGFILTER) sed -f `echo $$lang | sed -e 's/.*@//'`.sed 2>/dev/null > 
$$tmpdir/$$lang.new.po; then \
+         if cmp $$lang.po $$tmpdir/$$lang.new.po >/dev/null 2>&1; then \
+           rm -f $$tmpdir/$$lang.new.po; \
+         else \
+           if mv -f $$tmpdir/$$lang.new.po $$lang.po; then \
+             :; \
+           else \
+             echo "creation of $$lang.po failed: cannot move 
$$tmpdir/$$lang.new.po to $$lang.po" 1>&2; \
+             exit 1; \
+           fi; \
+         fi; \
+       else \
+         echo "creation of $$lang.po failed!" 1>&2; \
+         rm -f $$tmpdir/$$lang.new.po; \
+       fi
+
address@hidden: insert-header.sin
+       sed -e '/^#/d' -e 's/HEADER/address@hidden/g' 
$(srcdir)/insert-header.sin > address@hidden
+
address@hidden: insert-header.sin
+       sed -e '/^#/d' -e 's/HEADER/address@hidden/g' 
$(srcdir)/insert-header.sin > address@hidden
+
+mostlyclean: mostlyclean-quot
+mostlyclean-quot:
+       rm -f *.insert-header
diff --git a/po/boldquot.sed b/po/boldquot.sed
new file mode 100644
index 0000000..4b937aa
--- /dev/null
+++ b/po/boldquot.sed
@@ -0,0 +1,10 @@
+s/"\([^"]*\)"/“\1”/g
+s/`\([^`']*\)'/‘\1’/g
+s/ '\([^`']*\)' / ‘\1’ /g
+s/ '\([^`']*\)'$/ ‘\1’/g
+s/^'\([^`']*\)' /‘\1’ /g
+s/“”/""/g
+s/“/“/g
+s/”/”/g
+s/‘/‘/g
+s/’/’/g
diff --git a/po/address@hidden b/po/address@hidden
new file mode 100644
index 0000000..fedb6a0
--- /dev/null
+++ b/po/address@hidden
@@ -0,0 +1,25 @@
+# All this catalog "translates" are quotation characters.
+# The msgids must be ASCII and therefore cannot contain real quotation
+# characters, only substitutes like grave accent (0x60), apostrophe (0x27)
+# and double quote (0x22). These substitutes look strange; see
+# http://www.cl.cam.ac.uk/~mgk25/ucs/quotes.html
+#
+# This catalog translates grave accent (0x60) and apostrophe (0x27) to
+# left single quotation mark (U+2018) and right single quotation mark (U+2019).
+# It also translates pairs of apostrophe (0x27) to
+# left single quotation mark (U+2018) and right single quotation mark (U+2019)
+# and pairs of quotation mark (0x22) to
+# left double quotation mark (U+201C) and right double quotation mark (U+201D).
+#
+# When output to an UTF-8 terminal, the quotation characters appear perfectly.
+# When output to an ISO-8859-1 terminal, the single quotation marks are
+# transliterated to apostrophes (by iconv in glibc 2.2 or newer) or to
+# grave/acute accent (by libiconv), and the double quotation marks are
+# transliterated to 0x22.
+# When output to an ASCII terminal, the single quotation marks are
+# transliterated to apostrophes, and the double quotation marks are
+# transliterated to 0x22.
+#
+# This catalog furthermore displays the text between the quotation marks in
+# bold face, assuming the VT100/XTerm escape sequences.
+#
diff --git a/po/address@hidden b/po/address@hidden
new file mode 100644
index 0000000..a9647fc
--- /dev/null
+++ b/po/address@hidden
@@ -0,0 +1,22 @@
+# All this catalog "translates" are quotation characters.
+# The msgids must be ASCII and therefore cannot contain real quotation
+# characters, only substitutes like grave accent (0x60), apostrophe (0x27)
+# and double quote (0x22). These substitutes look strange; see
+# http://www.cl.cam.ac.uk/~mgk25/ucs/quotes.html
+#
+# This catalog translates grave accent (0x60) and apostrophe (0x27) to
+# left single quotation mark (U+2018) and right single quotation mark (U+2019).
+# It also translates pairs of apostrophe (0x27) to
+# left single quotation mark (U+2018) and right single quotation mark (U+2019)
+# and pairs of quotation mark (0x22) to
+# left double quotation mark (U+201C) and right double quotation mark (U+201D).
+#
+# When output to an UTF-8 terminal, the quotation characters appear perfectly.
+# When output to an ISO-8859-1 terminal, the single quotation marks are
+# transliterated to apostrophes (by iconv in glibc 2.2 or newer) or to
+# grave/acute accent (by libiconv), and the double quotation marks are
+# transliterated to 0x22.
+# When output to an ASCII terminal, the single quotation marks are
+# transliterated to apostrophes, and the double quotation marks are
+# transliterated to 0x22.
+#
diff --git a/po/insert-header.sin b/po/insert-header.sin
new file mode 100644
index 0000000..b26de01
--- /dev/null
+++ b/po/insert-header.sin
@@ -0,0 +1,23 @@
+# Sed script that inserts the file called HEADER before the header entry.
+#
+# At each occurrence of a line starting with "msgid ", we execute the following
+# commands. At the first occurrence, insert the file. At the following
+# occurrences, do nothing. The distinction between the first and the following
+# occurrences is achieved by looking at the hold space.
+/^msgid /{
+x
+# Test if the hold space is empty.
+s/m/m/
+ta
+# Yes it was empty. First occurrence. Read the file.
+r HEADER
+# Output the file's contents by reading the next line. But don't lose the
+# current line while doing this.
+g
+N
+bb
+:a
+# The hold space was nonempty. Following occurrences. Do nothing.
+x
+:b
+}
diff --git a/po/quot.sed b/po/quot.sed
new file mode 100644
index 0000000..0122c46
--- /dev/null
+++ b/po/quot.sed
@@ -0,0 +1,6 @@
+s/"\([^"]*\)"/“\1”/g
+s/`\([^`']*\)'/‘\1’/g
+s/ '\([^`']*\)' / ‘\1’ /g
+s/ '\([^`']*\)'$/ ‘\1’/g
+s/^'\([^`']*\)' /‘\1’ /g
+s/“”/""/g
diff --git a/po/remove-potcdate.sin b/po/remove-potcdate.sin
new file mode 100644
index 0000000..2436c49
--- /dev/null
+++ b/po/remove-potcdate.sin
@@ -0,0 +1,19 @@
+# Sed script that remove the POT-Creation-Date line in the header entry
+# from a POT file.
+#
+# The distinction between the first and the following occurrences of the
+# pattern is achieved by looking at the hold space.
+/^"POT-Creation-Date: .*"$/{
+x
+# Test if the hold space is empty.
+s/P/P/
+ta
+# Yes it was empty. First occurrence. Remove the line.
+g
+d
+bb
+:a
+# The hold space was nonempty. Following occurrences. Do nothing.
+x
+:b
+}
diff --git a/src/.gitignore b/src/.gitignore
new file mode 100644
index 0000000..282522d
--- /dev/null
+++ b/src/.gitignore
@@ -0,0 +1,2 @@
+Makefile
+Makefile.in
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..42185e6
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,7 @@
+# This Makefile.am is in the public domain
+SUBDIRS = include \
+  multicast \
+  psycutil \
+  psycstore \
+  psyc \
+  social
diff --git a/src/include/.gitignore b/src/include/.gitignore
new file mode 100644
index 0000000..282522d
--- /dev/null
+++ b/src/include/.gitignore
@@ -0,0 +1,2 @@
+Makefile
+Makefile.in
diff --git a/src/include/Makefile.am b/src/include/Makefile.am
new file mode 100644
index 0000000..8b8bcba
--- /dev/null
+++ b/src/include/Makefile.am
@@ -0,0 +1,15 @@
+# This Makefile.am is in the public domain
+SUBDIRS = .
+
+gnunetincludedir = $(includedir)/gnunet
+
+gnunetinclude_HEADERS = \
+  gnunet_multicast_service.h \
+  gnunet_psycstore_plugin.h \
+  gnunet_psycstore_service.h \
+  gnunet_psyc_service.h \
+  gnunet_psyc_util_lib.h \
+  gnunet_psyc_env.h \
+  gnunet_psyc_message.h \
+  gnunet_psyc_slicer.h \
+  gnunet_social_service.h
diff --git a/src/include/gnunet_multicast_service.h 
b/src/include/gnunet_multicast_service.h
new file mode 100644
index 0000000..58fca0b
--- /dev/null
+++ b/src/include/gnunet_multicast_service.h
@@ -0,0 +1,925 @@
+/*
+     This file is part of GNUnet.
+     Copyright (C) 2012, 2013 GNUnet e.V.
+
+     GNUnet is free software: you can redistribute it and/or modify it
+     under the terms of the GNU Affero General Public License as published
+     by the Free Software Foundation, either version 3 of the License,
+     or (at your option) any later version.
+
+     GNUnet 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
+     Affero General Public License for more details.
+    
+     You should have received a copy of the GNU Affero General Public License
+     along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+     SPDX-License-Identifier: AGPL3.0-or-later
+*/
+
+/**
+ * @author Gabor X Toth
+ * @author Christian Grothoff
+ *
+ * @file
+ * Multicast service; multicast messaging via CADET
+ *
+ * @defgroup multicast  Multicast service
+ * Multicast messaging via CADET.
+ * @{
+ */
+
+#ifndef GNUNET_MULTICAST_SERVICE_H
+#define GNUNET_MULTICAST_SERVICE_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#if 0                           /* keep Emacsens' auto-indent happy */
+}
+#endif
+#endif
+
+#include "gnunet_util_lib.h"
+#include "gnunet_transport_service.h"
+
+/**
+ * Version number of GNUnet-multicast API.
+ */
+#define GNUNET_MULTICAST_VERSION 0x00000000
+
+/**
+ * Opaque handle for a multicast group member.
+ */
+struct GNUNET_MULTICAST_Member;
+
+/**
+ * Handle for the origin of a multicast group.
+ */
+struct GNUNET_MULTICAST_Origin;
+
+
+enum GNUNET_MULTICAST_MessageFlags
+{
+  /**
+   * First fragment of a message.
+   */
+  GNUNET_MULTICAST_MESSAGE_FIRST_FRAGMENT = 1 << 0,
+
+  /**
+   * Last fragment of a message.
+   */
+  GNUNET_MULTICAST_MESSAGE_LAST_FRAGMENT = 1 << 1,
+
+  /**
+   * OR'ed flags if message is not fragmented.
+   */
+  GNUNET_MULTICAST_MESSAGE_NOT_FRAGMENTED
+    = GNUNET_MULTICAST_MESSAGE_FIRST_FRAGMENT
+  | GNUNET_MULTICAST_MESSAGE_LAST_FRAGMENT,
+
+  /**
+   * Historic message, used only locally when replaying messages from local
+   * storage.
+   */
+  GNUNET_MULTICAST_MESSAGE_HISTORIC = 1 << 30
+
+};
+
+
+GNUNET_NETWORK_STRUCT_BEGIN
+
+/**
+ * Header of a multicast message fragment.
+ *
+ * This format is public as the replay mechanism must replay message fragments 
using the
+ * same format.  This is needed as we want to integrity-check message 
fragments within
+ * the multicast layer to avoid multicasting mal-formed messages.
+ */
+struct GNUNET_MULTICAST_MessageHeader
+{
+
+  /**
+   * Header for all multicast message fragments from the origin.
+   */
+  struct GNUNET_MessageHeader header;
+
+  /**
+   * Number of hops this message fragment has taken since the origin.
+   *
+   * Helpful to determine shortest paths to the origin among honest peers for
+   * unicast requests from members.  Updated at each hop and thus not signed 
and
+   * not secure.
+   */
+  uint32_t hop_counter GNUNET_PACKED;
+
+  /**
+   * ECC signature of the message fragment.
+   *
+   * Signature must match the public key of the multicast group.
+   */
+  struct GNUNET_CRYPTO_EddsaSignature signature;
+
+  /**
+   * Purpose for the signature and size of the signed data.
+   */
+  struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
+
+  /**
+   * Number of the message fragment, monotonically increasing starting from 1.
+   */
+  uint64_t fragment_id GNUNET_PACKED;
+
+  /**
+   * Byte offset of this @e fragment of the @e message.
+   */
+  uint64_t fragment_offset GNUNET_PACKED;
+
+  /**
+   * Number of the message this fragment belongs to.
+   *
+   * Set in GNUNET_MULTICAST_origin_to_all().
+   */
+  uint64_t message_id GNUNET_PACKED;
+
+  /**
+   * Counter that monotonically increases whenever a member parts the group.
+   *
+   * Set in GNUNET_MULTICAST_origin_to_all().
+   *
+   * It has significance in case of replay requests: when a member has missed
+   * messages and gets a replay request: in this case if the @a 
group_generation
+   * is still the same before and after the missed messages, it means that no
+   * @e join or @e part operations happened during the missed messages.
+   */
+  uint64_t group_generation GNUNET_PACKED;
+
+  /**
+   * Flags for this message fragment.
+   *
+   * @see enum GNUNET_MULTICAST_MessageFlags
+   */
+  uint32_t flags GNUNET_PACKED;
+
+  /* Followed by message body. */
+};
+
+
+/**
+ * Header of a request from a member to the origin.
+ */
+struct GNUNET_MULTICAST_RequestHeader
+{
+  /**
+   * Header for all requests from a member to the origin.
+   */
+  struct GNUNET_MessageHeader header;
+
+  /**
+   * Public key of the sending member.
+   */
+  struct GNUNET_CRYPTO_EcdsaPublicKey member_pub_key;
+
+  /**
+   * ECC signature of the request fragment.
+   *
+   * Signature must match the public key of the multicast group.
+   */
+  struct GNUNET_CRYPTO_EcdsaSignature signature;
+
+  /**
+   * Purpose for the signature and size of the signed data.
+   */
+  struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
+
+  /**
+   * Number of the request fragment.
+   * Monotonically increasing from 1.
+   */
+  uint64_t fragment_id GNUNET_PACKED;
+
+  /**
+   * Byte offset of this @e fragment of the @e request.
+   */
+  uint64_t fragment_offset GNUNET_PACKED;
+
+  /**
+   * Number of the request this fragment belongs to.
+   *
+   * Set in GNUNET_MULTICAST_origin_to_all().
+   */
+  uint64_t request_id GNUNET_PACKED;
+
+  /**
+   * Flags for this request.
+   */
+  enum GNUNET_MULTICAST_MessageFlags flags GNUNET_PACKED;
+
+  /* Followed by request body. */
+};
+
+GNUNET_NETWORK_STRUCT_END
+
+
+/**
+ * Maximum size of a multicast message fragment.
+ */
+#define GNUNET_MULTICAST_FRAGMENT_MAX_SIZE (63 * 1024)
+
+#define GNUNET_MULTICAST_FRAGMENT_MAX_PAYLOAD           \
+  (GNUNET_MULTICAST_FRAGMENT_MAX_SIZE                   \
+   - sizeof (struct GNUNET_MULTICAST_MessageHeader))
+
+
+/**
+ * Handle that identifies a join request.
+ *
+ * Used to match calls to #GNUNET_MULTICAST_JoinRequestCallback to the
+ * corresponding calls to #GNUNET_MULTICAST_join_decision().
+ */
+struct GNUNET_MULTICAST_JoinHandle;
+
+
+/**
+ * Function to call with the decision made for a join request.
+ *
+ * Must be called once and only once in response to an invocation of the
+ * #GNUNET_MULTICAST_JoinRequestCallback.
+ *
+ * @param jh
+ *        Join request handle.
+ * @param is_admitted
+ *        #GNUNET_YES    if the join is approved,
+ *        #GNUNET_NO     if it is disapproved,
+ *        #GNUNET_SYSERR if we cannot answer the request.
+ * @param relay_count
+ *        Number of relays given.
+ * @param relays
+ *        Array of suggested peers that might be useful relays to use
+ *        when joining the multicast group (essentially a list of peers that
+ *        are already part of the multicast group and might thus be willing
+ *        to help with routing).  If empty, only this local peer (which must
+ *        be the multicast origin) is a good candidate for building the
+ *        multicast tree.  Note that it is unnecessary to specify our own
+ *        peer identity in this array.
+ * @param join_resp
+ *        Message to send in response to the joining peer;
+ *        can also be used to redirect the peer to a different group at the
+ *        application layer; this response is to be transmitted to the
+ *        peer that issued the request even if admission is denied.
+ */
+struct GNUNET_MULTICAST_ReplayHandle *
+GNUNET_MULTICAST_join_decision (struct GNUNET_MULTICAST_JoinHandle *jh,
+                                int is_admitted,
+                                uint16_t relay_count,
+                                const struct GNUNET_PeerIdentity *relays,
+                                const struct GNUNET_MessageHeader *join_resp);
+
+
+/**
+ * Method called whenever another peer wants to join the multicast group.
+ *
+ * Implementations of this function must call GNUNET_MULTICAST_join_decision()
+ * with the decision.
+ *
+ * @param cls
+ *        Closure.
+ * @param member_pub_key
+ *        Public key of the member requesting join.
+ * @param join_msg
+ *        Application-dependent join message from the new member.
+ * @param jh
+ *        Join handle to pass to GNUNET_MULTICAST_join_decison().
+ */
+typedef void
+(*GNUNET_MULTICAST_JoinRequestCallback) (void *cls,
+                                         const struct 
GNUNET_CRYPTO_EcdsaPublicKey *member_pub_key,
+                                         const struct GNUNET_MessageHeader 
*join_msg,
+                                         struct GNUNET_MULTICAST_JoinHandle 
*jh);
+
+
+/**
+ * Method called to inform about the decision in response to a join request.
+ *
+ * If @a is_admitted is not #GNUNET_YES, then the multicast service disconnects
+ * the client and the multicast member handle returned by
+ * GNUNET_MULTICAST_member_join() is invalidated.
+ *
+ * @param cls
+ *         Closure.
+ * @param is_admitted
+ *         #GNUNET_YES or #GNUNET_NO or #GNUNET_SYSERR
+ * @param peer
+ *        The peer we are connected to and the join decision is from.
+ * @param relay_count
+ *        Number of peers in the @a relays array.
+ * @param relays
+ *        Peer identities of members of the group, which serve as relays
+ *        and can be used to join the group at.  If empty, only the origin can
+ *        be used to connect to the group.
+ * @param join_msg
+ *        Application-dependent join message from the origin.
+ */
+typedef void
+(*GNUNET_MULTICAST_JoinDecisionCallback) (void *cls,
+                                          int is_admitted,
+                                          const struct GNUNET_PeerIdentity 
*peer,
+                                          uint16_t relay_count,
+                                          const struct GNUNET_PeerIdentity 
*relays,
+                                          const struct GNUNET_MessageHeader 
*join_msg);
+
+
+/**
+ * Function called whenever a group member has transmitted a request
+ * to the origin (other than joining or leaving).
+ *
+ * FIXME: need to distinguish between origin cancelling a message (some 
fragments
+ * were sent, then the rest 'discarded') and the case where we got 
disconnected;
+ * right now, both would mean 'msg' is NULL, but they could be quite 
different...
+ * So the semantics from the receiver side of
+ * GNUNET_MULTICAST_member_to_origin_cancel() are not clear here.   Maybe we
+ * should do something with the flags in this case?
+ *
+ * @param cls
+ *        Closure (set from GNUNET_MULTICAST_origin_start).
+ * @param sender
+ *        Identity of the sender.
+ * @param req
+ *        Request to the origin.
+ * @param flags
+ *        Flags for the request.
+ */
+typedef void
+(*GNUNET_MULTICAST_RequestCallback) (void *cls,
+                                     const struct 
GNUNET_MULTICAST_RequestHeader *req);
+
+
+/**
+ * Function called whenever a group member is receiving a message fragment from
+ * the origin.
+ *
+ * If admission to the group is denied, this function is called once with the
+ * response of the @e origin (as given to GNUNET_MULTICAST_join_decision()) and
+ * then a second time with NULL to indicate that the connection failed for 
good.
+ *
+ * FIXME: need to distinguish between origin cancelling a message (some 
fragments
+ * were sent, then the rest 'discarded') and the case where we got 
disconnected;
+ * right now, both would mean 'msg' is NULL, but they could be quite 
different...
+ * So the semantics from the receiver side of
+ * GNUNET_MULTICAST_origin_to_all_cancel() are not clear here.
+ *
+ * @param cls
+ *        Closure (set from GNUNET_MULTICAST_member_join())
+ * @param msg
+ *        Message from the origin, NULL if the origin shut down
+ *        (or we were kicked out, and we should thus call
+ *        GNUNET_MULTICAST_member_part() next)
+ */
+typedef void
+(*GNUNET_MULTICAST_MessageCallback) (void *cls,
+                                     const struct 
GNUNET_MULTICAST_MessageHeader *msg);
+
+
+/**
+ * Opaque handle to a replay request from the multicast service.
+ */
+struct GNUNET_MULTICAST_ReplayHandle;
+
+
+/**
+ * Functions with this signature are called whenever the multicast service 
needs
+ * a message fragment to be replayed by fragment_id.
+ *
+ * Implementations of this function MUST call GNUNET_MULTICAST_replay() ONCE
+ * (with a message or an error); however, if the origin is destroyed or the
+ * group is left, the replay handle must no longer be used.
+ *
+ * @param cls
+ *        Closure (set from GNUNET_MULTICAST_origin_start()
+ *        or GNUNET_MULTICAST_member_join()).
+ * @param member_pub_key
+ *        The member requesting replay.
+ * @param fragment_id
+ *        Which message fragment should be replayed.
+ * @param flags
+ *        Flags for the replay.
+ * @param rh
+ *        Handle to pass to message transmit function.
+ */
+typedef void
+(*GNUNET_MULTICAST_ReplayFragmentCallback) (void *cls,
+                                            const struct 
GNUNET_CRYPTO_EcdsaPublicKey *member_pub_key,
+                                            uint64_t fragment_id,
+                                            uint64_t flags,
+                                            struct 
GNUNET_MULTICAST_ReplayHandle *rh);
+
+/**
+ * Functions with this signature are called whenever the multicast service 
needs
+ * a message fragment to be replayed by message_id and fragment_offset.
+ *
+ * Implementations of this function MUST call GNUNET_MULTICAST_replay() ONCE
+ * (with a message or an error); however, if the origin is destroyed or the
+ * group is left, the replay handle must no longer be used.
+ *
+ * @param cls
+ *        Closure (set from GNUNET_MULTICAST_origin_start()
+ *        or GNUNET_MULTICAST_member_join()).
+ * @param member_pub_key
+ *        The member requesting replay.
+ * @param message_id
+ *        Which message should be replayed.
+ * @param fragment_offset
+ *        Offset of the fragment within of @a message_id to be replayed.
+ * @param flags
+ *        Flags for the replay.
+ * @param rh
+ *        Handle to pass to message transmit function.
+ */
+typedef void
+(*GNUNET_MULTICAST_ReplayMessageCallback) (void *cls,
+                                           const struct 
GNUNET_CRYPTO_EcdsaPublicKey *member_pub_key,
+                                           uint64_t message_id,
+                                           uint64_t fragment_offset,
+                                           uint64_t flags,
+                                           struct 
GNUNET_MULTICAST_ReplayHandle *rh);
+
+
+/**
+ * Possible error codes during replay.
+ */
+enum GNUNET_MULTICAST_ReplayErrorCode
+{
+
+  /**
+   * Everything is fine.
+   */
+  GNUNET_MULTICAST_REC_OK = 0,
+
+  /**
+   * Message fragment not found in the message store.
+   *
+   * Either discarded if it is too old, or not arrived yet if this member has
+   * missed some messages.
+   */
+  GNUNET_MULTICAST_REC_NOT_FOUND = 1,
+
+  /**
+   * Fragment ID counter was larger than the highest counter this
+   * replay function has ever encountered; thus it is likely the
+   * origin never sent it and we're at the HEAD of the multicast
+   * stream as far as this node is concerned.
+   *
+   * FIXME: needed?
+   */
+  GNUNET_MULTICAST_REC_PAST_HEAD = 2,
+
+  /**
+   * Access is denied to the requested fragment, membership test did not pass.
+   */
+  GNUNET_MULTICAST_REC_ACCESS_DENIED = 3,
+
+  /**
+   * Internal error (i.e. database error).  Try some other peer.
+   */
+  GNUNET_MULTICAST_REC_INTERNAL_ERROR = 4
+
+};
+
+
+/**
+ * Replay a message fragment for the multicast group.
+ *
+ * @param rh
+ *        Replay handle identifying which replay operation was requested.
+ * @param msg
+ *        Replayed message fragment, NULL if not found / an error occurred.
+ * @param ec
+ *        Error code.  See enum GNUNET_MULTICAST_ReplayErrorCode
+ *        If not #GNUNET_MULTICAST_REC_OK, the replay handle is invalidated.
+ */
+void
+GNUNET_MULTICAST_replay_response (struct GNUNET_MULTICAST_ReplayHandle *rh,
+                                  const struct GNUNET_MessageHeader *msg,
+                                  enum GNUNET_MULTICAST_ReplayErrorCode ec);
+
+
+/**
+ * Indicate the end of the replay session.
+ *
+ * Invalidates the replay handle.
+ *
+ * @param rh Replay session to end.
+ */
+void
+GNUNET_MULTICAST_replay_response_end (struct GNUNET_MULTICAST_ReplayHandle 
*rh);
+
+
+/**
+ * Function called to provide data for a transmission for a replay.
+ *
+ * @see GNUNET_MULTICAST_replay2()
+ */
+typedef int
+(*GNUNET_MULTICAST_ReplayTransmitNotify) (void *cls,
+                                          size_t *data_size,
+                                          void *data);
+
+
+/**
+ * Replay a message for the multicast group.
+ *
+ * @param rh
+ *        Replay handle identifying which replay operation was requested.
+ * @param notify
+ *        Function to call to get the message.
+ * @param notify_cls
+ *        Closure for @a notify.
+ */
+void
+GNUNET_MULTICAST_replay_response2 (struct GNUNET_MULTICAST_ReplayHandle *rh,
+                                   GNUNET_MULTICAST_ReplayTransmitNotify 
notify,
+                                   void *notify_cls);
+
+
+/**
+ * Start a multicast group.
+ *
+ * Peers that issue GNUNET_MULTICAST_member_join() can transmit a join request
+ * to either an existing group member or to the origin.  If the joining is
+ * approved, the member is cleared for @e replay and will begin to receive
+ * messages transmitted to the group.  If joining is disapproved, the failed
+ * candidate will be given a response.  Members in the group can send messages
+ * to the origin.
+ *
+ * TODO: This function could optionally offer to advertise the origin in the
+ * P2P overlay network(where?) under the respective public key so that other
+ * peers can find an alternate PeerId to join it. Higher level protocols may
+ * however provide other means of solving the problem of the offline host
+ * (see secushare specs about that) and therefore merely need a way to provide
+ * a list of possible PeerIds.
+ *
+ * @param cfg
+ *        Configuration to use.
+ * @param priv_key
+ *        ECC key that will be used to sign messages for this
+ *        multicast session; public key is used to identify the multicast 
group;
+ * @param max_fragment_id
+ *        Maximum fragment ID already sent to the group.
+ *        0 for a new group.
+ * @param join_request_cb
+ *        Function called to approve / disapprove joining of a peer.
+ * @param replay_frag_cb
+ *        Function that can be called to replay a message fragment.
+ * @param replay_msg_cb
+ *        Function that can be called to replay a message.
+ * @param request_cb
+ *        Function called with message fragments from group members.
+ * @param message_cb
+ *        Function called with the message fragments sent to the
+ *        network by GNUNET_MULTICAST_origin_to_all().  These message fragments
+ *        should be stored for answering replay requests later.
+ * @param cls
+ *        Closure for the various callbacks that follow.
+ *
+ * @return Handle for the origin, NULL on error.
+ */
+struct GNUNET_MULTICAST_Origin *
+GNUNET_MULTICAST_origin_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
+                               const struct GNUNET_CRYPTO_EddsaPrivateKey 
*priv_key,
+                               uint64_t max_fragment_id,
+                               GNUNET_MULTICAST_JoinRequestCallback 
join_request_cb,
+                               GNUNET_MULTICAST_ReplayFragmentCallback 
replay_frag_cb,
+                               GNUNET_MULTICAST_ReplayMessageCallback 
replay_msg_cb,
+                               GNUNET_MULTICAST_RequestCallback request_cb,
+                               GNUNET_MULTICAST_MessageCallback message_cb,
+                               void *cls);
+
+/**
+ * Function called to provide data for a transmission from the origin to all
+ * members.
+ *
+ * Note that returning #GNUNET_OK or #GNUNET_SYSERR (but not #GNUNET_NO)
+ * invalidates the respective transmission handle.
+ *
+ * @param cls
+ *        Closure.
+ * @param[in,out] data_size
+ *        Initially set to the number of bytes available in
+ *        @a data, should be set to the number of bytes written to data.
+ * @param[out] data
+ *        Where to write the body of the message to give to the
+ *         method. The function must copy at most @a data_size bytes to @a 
data.
+ *
+ * @return #GNUNET_SYSERR on error (fatal, aborts transmission)
+ *         #GNUNET_NO on success, if more data is to be transmitted later.
+ *         Should be used if @a data_size was not big enough to take all the
+ *         data.  If 0 is returned in @a data_size the transmission is paused,
+ *         and can be resumed with GNUNET_MULTICAST_origin_to_all_resume().
+ *         #GNUNET_YES if this completes the transmission (all data supplied)
+ * @deprecated should move to MQ-style API!
+ */
+typedef int
+(*GNUNET_MULTICAST_OriginTransmitNotify) (void *cls,
+                                          size_t *data_size,
+                                          void *data);
+
+
+/**
+ * Handle for a request to send a message to all multicast group members
+ * (from the origin).
+ */
+struct GNUNET_MULTICAST_OriginTransmitHandle;
+
+
+/**
+ * Send a message to the multicast group.
+ *
+ * @param origin
+ *        Handle to the multicast group.
+ * @param message_id
+ *        Application layer ID for the message.  Opaque to multicast.
+ * @param group_generation
+ *        Group generation of the message.  Documented in
+ *        struct GNUNET_MULTICAST_MessageHeader.
+ * @param notify
+ *        Function to call to get the message.
+ * @param notify_cls
+ *        Closure for @a notify.
+ *
+ * @return NULL on error (i.e. request already pending).
+ * @deprecated should move to MQ-style API!
+ */
+struct GNUNET_MULTICAST_OriginTransmitHandle *
+GNUNET_MULTICAST_origin_to_all (struct GNUNET_MULTICAST_Origin *origin,
+                                uint64_t message_id,
+                                uint64_t group_generation,
+                                GNUNET_MULTICAST_OriginTransmitNotify notify,
+                                void *notify_cls);
+
+
+
+/**
+ * Resume message transmission to multicast group.
+ *
+ * @param th  Transmission to cancel.
+ */
+void
+GNUNET_MULTICAST_origin_to_all_resume (struct 
GNUNET_MULTICAST_OriginTransmitHandle *th);
+
+
+/**
+ * Cancel request for message transmission to multicast group.
+ *
+ * @param th  Transmission to cancel.
+ */
+void
+GNUNET_MULTICAST_origin_to_all_cancel (struct 
GNUNET_MULTICAST_OriginTransmitHandle *th);
+
+
+/**
+ * Stop a multicast group.
+ *
+ * @param origin Multicast group to stop.
+ */
+void
+GNUNET_MULTICAST_origin_stop (struct GNUNET_MULTICAST_Origin *origin,
+                              GNUNET_ContinuationCallback stop_cb,
+                              void *stop_cls);
+
+
+/**
+ * Join a multicast group.
+ *
+ * The entity joining is always the local peer.  Further information about the
+ * candidate can be provided in @a join_msg.  If the join fails, the
+ * @a message_cb is invoked with a (failure) response and then with NULL.  If
+ * the join succeeds, outstanding (state) messages and ongoing multicast
+ * messages will be given to the @a message_cb until the member decides to part
+ * the group.  The @a mem_test_cb and @a replay_cb functions may be called at
+ * anytime by the multicast service to support relaying messages to other
+ * members of the group.
+ *
+ * @param cfg
+ *        Configuration to use.
+ * @param group_key
+ *        ECC public key that identifies the group to join.
+ * @param member_pub_key
+ *        ECC key that identifies the member
+ *        and used to sign requests sent to the origin.
+ * @param origin
+ *        Peer ID of the origin to send unicast requsets to.  If NULL,
+ *        unicast requests are sent back via multiple hops on the reverse path
+ *        of multicast messages.
+ * @param relay_count
+ *        Number of peers in the @a relays array.
+ * @param relays
+ *        Peer identities of members of the group, which serve as relays
+ *        and can be used to join the group at. and send the @a join_request 
to.
+ *        If empty, the @a join_request is sent directly to the @a origin.
+ * @param join_msg
+ *        Application-dependent join message to be passed to the peer @a 
origin.
+ * @param join_request_cb
+ *        Function called to approve / disapprove joining of a peer.
+ * @param join_decision_cb
+ *        Function called to inform about the join decision.
+ * @param replay_frag_cb
+ *        Function that can be called to replay message fragments
+ *        this peer already knows from this group. NULL if this
+ *        client is unable to support replay.
+ * @param replay_msg_cb
+ *        Function that can be called to replay message fragments
+ *        this peer already knows from this group. NULL if this
+ *        client is unable to support replay.
+ * @param message_cb
+ *        Function to be called for all message fragments we
+ *        receive from the group, excluding those our @a replay_cb
+ *        already has.
+ * @param cls
+ *        Closure for callbacks.
+ *
+ * @return Handle for the member, NULL on error.
+ */
+struct GNUNET_MULTICAST_Member *
+GNUNET_MULTICAST_member_join (const struct GNUNET_CONFIGURATION_Handle *cfg,
+                              const struct GNUNET_CRYPTO_EddsaPublicKey 
*group_key,
+                              const struct GNUNET_CRYPTO_EcdsaPrivateKey 
*member_pub_key,
+                              const struct GNUNET_PeerIdentity *origin,
+                              uint16_t relay_count,
+                              const struct GNUNET_PeerIdentity *relays,
+                              const struct GNUNET_MessageHeader *join_request,
+                              GNUNET_MULTICAST_JoinRequestCallback 
join_request_cb,
+                              GNUNET_MULTICAST_JoinDecisionCallback 
join_decision_cb,
+                              GNUNET_MULTICAST_ReplayFragmentCallback 
replay_frag_cb,
+                              GNUNET_MULTICAST_ReplayMessageCallback 
replay_msg_cb,
+                              GNUNET_MULTICAST_MessageCallback message_cb,
+                              void *cls);
+
+/**
+ * Handle for a replay request.
+ */
+struct GNUNET_MULTICAST_MemberReplayHandle;
+
+
+/**
+ * Request a fragment to be replayed by fragment ID.
+ *
+ * Useful if messages below the @e max_known_fragment_id given when joining are
+ * needed and not known to the client.
+ *
+ * @param member
+ *        Membership handle.
+ * @param fragment_id
+ *        ID of a message fragment that this client would like to see replayed.
+ * @param flags
+ *        Additional flags for the replay request.
+ *        It is used and defined by GNUNET_MULTICAST_ReplayFragmentCallback
+ *
+ * @return Replay request handle, NULL on error.
+ */
+struct GNUNET_MULTICAST_MemberReplayHandle *
+GNUNET_MULTICAST_member_replay_fragment (struct GNUNET_MULTICAST_Member 
*member,
+                                         uint64_t fragment_id,
+                                         uint64_t flags);
+
+
+/**
+ * Request a message fr to be replayed.
+ *
+ * Useful if messages below the @e max_known_fragment_id given when joining are
+ * needed and not known to the client.
+ *
+ * @param member
+ *        Membership handle.
+ * @param message_id
+ *        ID of the message this client would like to see replayed.
+ * @param fragment_offset
+ *        Offset of the fragment within the message to replay.
+ * @param flags
+ *        Additional flags for the replay request.
+ *        It is used & defined by GNUNET_MULTICAST_ReplayMessageCallback
+ *
+ * @return Replay request handle, NULL on error.
+ */
+struct GNUNET_MULTICAST_MemberReplayHandle *
+GNUNET_MULTICAST_member_replay_message (struct GNUNET_MULTICAST_Member *member,
+                                        uint64_t message_id,
+                                        uint64_t fragment_offset,
+                                        uint64_t flags);
+
+
+/**
+ * Cancel a replay request.
+ *
+ * @param rh
+ *        Request to cancel.
+ */
+void
+GNUNET_MULTICAST_member_replay_cancel (struct 
GNUNET_MULTICAST_MemberReplayHandle *rh);
+
+
+/**
+ * Part a multicast group.
+ *
+ * Disconnects from all group members and invalidates the @a member handle.
+ *
+ * An application-dependent part message can be transmitted beforehand using
+ * #GNUNET_MULTICAST_member_to_origin())
+ *
+ * @param member
+ *        Membership handle.
+ */
+void
+GNUNET_MULTICAST_member_part (struct GNUNET_MULTICAST_Member *member,
+                              GNUNET_ContinuationCallback part_cb,
+                              void *part_cls);
+
+
+/**
+ * Function called to provide data for a transmission from a member to the 
origin.
+ *
+ * Note that returning #GNUNET_OK or #GNUNET_SYSERR (but not #GNUNET_NO)
+ * invalidates the respective transmission handle.
+ *
+ * @param cls
+ *        Closure.
+ * @param[in,out] data_size
+ *        Initially set to the number of bytes available in
+ *        @a data, should be set to the number of bytes written to data.
+ * @param[out] data
+ *        Where to write the body of the message to give to the
+ *        method. The function must copy at most @a data_size bytes to @a data.
+ *
+ * @return #GNUNET_SYSERR on error (fatal, aborts transmission)
+ *         #GNUNET_NO on success, if more data is to be transmitted later.
+ *         Should be used if @a data_size was not big enough to take all the
+ *         data.  If 0 is returned in @a data_size the transmission is paused,
+ *         and can be resumed with GNUNET_MULTICAST_member_to_origin_resume().
+ *         #GNUNET_YES if this completes the transmission (all data supplied)
+ * @deprecated should move to MQ-style API!
+ */
+typedef int
+(*GNUNET_MULTICAST_MemberTransmitNotify) (void *cls,
+                                          size_t *data_size,
+                                          void *data);
+
+
+/**
+ * Handle for a message to be delivered from a member to the origin.
+ */
+struct GNUNET_MULTICAST_MemberTransmitHandle;
+
+
+/**
+ * Send a message to the origin of the multicast group.
+ *
+ * @param member
+ *        Membership handle.
+ * @param request_id
+ *        Application layer ID for the request.  Opaque to multicast.
+ * @param notify
+ *        Callback to call to get the message.
+ * @param notify_cls
+ *        Closure for @a notify.
+ *
+ * @return Handle to cancel request, NULL on error (i.e. request already 
pending).
+ * @deprecated should move to MQ-style API!
+ */
+struct GNUNET_MULTICAST_MemberTransmitHandle *
+GNUNET_MULTICAST_member_to_origin (struct GNUNET_MULTICAST_Member *member,
+                                   uint64_t request_id,
+                                   GNUNET_MULTICAST_MemberTransmitNotify 
notify,
+                                   void *notify_cls);
+
+
+/**
+ * Resume message transmission to origin.
+ *
+ * @param th
+ *        Transmission to cancel.
+ */
+void
+GNUNET_MULTICAST_member_to_origin_resume (struct 
GNUNET_MULTICAST_MemberTransmitHandle *th);
+
+
+/**
+ * Cancel request for message transmission to origin.
+ *
+ * @param th
+ *        Transmission to cancel.
+ */
+void
+GNUNET_MULTICAST_member_to_origin_cancel (struct 
GNUNET_MULTICAST_MemberTransmitHandle *th);
+
+
+#if 0                           /* keep Emacsens' auto-indent happy */
+{
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+/* ifndef GNUNET_MULTICAST_SERVICE_H */
+#endif
+
+/** @} */  /* end of group */
diff --git a/src/include/gnunet_psyc_env.h b/src/include/gnunet_psyc_env.h
new file mode 100644
index 0000000..0d878cb
--- /dev/null
+++ b/src/include/gnunet_psyc_env.h
@@ -0,0 +1,340 @@
+/*
+ * This file is part of GNUnet.
+ * Copyright (C) 2013 GNUnet e.V.
+ *
+ * GNUnet is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * GNUnet 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
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+     SPDX-License-Identifier: AGPL3.0-or-later
+ */
+
+/**
+ * @author Gabor X Toth
+ *
+ * @file
+ * PSYC Environment library
+ *
+ * @defgroup psyc-util-env  PSYC Utilities library: Environment
+ * Environment data structure operations for PSYC and Social messages.
+ *
+ * Library providing operations for the @e environment of
+ * PSYC and Social messages, and for (de)serializing variable values.
+ *
+ * @{
+ */
+
+
+#ifndef GNUNET_PSYC_ENV_H
+#define GNUNET_PSYC_ENV_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#if 0                           /* keep Emacsens' auto-indent happy */
+}
+#endif
+#endif
+
+
+/**
+ * Possible operations on PSYC state (persistent) and transient variables (per 
message).
+ */
+enum GNUNET_PSYC_Operator
+{
+  /**
+   * Set value of a transient variable.
+   */
+  GNUNET_PSYC_OP_SET = ':',
+
+  /**
+   * Assign value for a persistent state variable.
+   *
+   * If an assigned value is NULL, the variable is deleted.
+   */
+  GNUNET_PSYC_OP_ASSIGN = '=',
+
+  /**
+   * Augment state variable.
+   *
+   * Used for appending strings, adding numbers, and adding new items to a 
list or dictionary.
+   */
+  GNUNET_PSYC_OP_AUGMENT = '+',
+
+  /**
+   * Diminish state variable.
+   *
+   * Used for subtracting numbers, and removing items from a list or 
dictionary.
+   */
+  GNUNET_PSYC_OP_DIMINISH = '-',
+
+  /**
+   * Update state variable.
+   *
+   * Used for modifying a single item of a list or dictionary.
+   */
+  GNUNET_PSYC_OP_UPDATE = '@',
+};
+
+
+/**
+ * PSYC variable types.
+ */
+enum GNUNET_PSYC_Type
+{
+  GNUNET_PSYC_TYPE_DATA = 0,
+  GNUNET_PSYC_TYPE_NUMBER,
+  GNUNET_PSYC_TYPE_LIST,
+  GNUNET_PSYC_TYPE_DICT
+};
+
+
+/**
+ * PSYC state modifier.
+ */
+struct GNUNET_PSYC_Modifier
+{
+  /**
+   * State operation.
+   */
+  enum GNUNET_PSYC_Operator oper;
+
+  /**
+   * Variable name.
+   */
+  const char *name;
+
+  /**
+   * Size of @a value.
+   */
+  size_t value_size;
+
+  /**
+   * Value of variable.
+   */
+  const void *value;
+
+  /**
+   * Next modifier.
+   */
+  struct GNUNET_PSYC_Modifier *next;
+
+  /**
+   * Previous modifier.
+   */
+  struct GNUNET_PSYC_Modifier *prev;
+};
+
+
+/**
+ * Environment for a message.
+ *
+ * Contains modifiers.
+ */
+struct GNUNET_PSYC_Environment;
+
+
+/**
+ * Create an environment.
+ *
+ * @return A newly allocated environment.
+ */
+struct GNUNET_PSYC_Environment *
+GNUNET_PSYC_env_create ();
+
+
+/**
+ * Add a modifier to the environment.
+ *
+ * @param env The environment.
+ * @param oper Operation to perform.
+ * @param name Name of the variable.
+ * @param value Value of the variable.
+ * @param value_size Size of @a value.
+ */
+void
+GNUNET_PSYC_env_add (struct GNUNET_PSYC_Environment *env,
+                     enum GNUNET_PSYC_Operator oper, const char *name,
+                     const void *value, size_t value_size);
+
+
+/**
+ * Get the first modifier of the environment.
+ */
+struct GNUNET_PSYC_Modifier *
+GNUNET_PSYC_env_head (const struct GNUNET_PSYC_Environment *env);
+
+
+
+/**
+ * Get the last modifier of the environment.
+ */
+struct GNUNET_PSYC_Modifier *
+GNUNET_PSYC_env_tail (const struct GNUNET_PSYC_Environment *env);
+
+
+/**
+ * Remove a modifier from the environment.
+ */
+void
+GNUNET_PSYC_env_remove (struct GNUNET_PSYC_Environment *env,
+                        struct GNUNET_PSYC_Modifier *mod);
+
+
+/**
+ * Remove a modifier at the beginning of the environment.
+ */
+int
+GNUNET_PSYC_env_shift (struct GNUNET_PSYC_Environment *env,
+                       enum GNUNET_PSYC_Operator *oper, const char **name,
+                       const void **value, size_t *value_size);
+
+
+/**
+ * Iterator for modifiers in the environment.
+ *
+ * @param cls Closure.
+ * @param mod Modifier.
+ *
+ * @return #GNUNET_YES to continue iterating,
+ *         #GNUNET_NO to stop.
+ */
+typedef int
+(*GNUNET_PSYC_Iterator) (void *cls, enum GNUNET_PSYC_Operator oper,
+                         const char *name, const char *value,
+                         uint32_t value_size);
+
+
+/**
+ * Iterate through all modifiers in the environment.
+ *
+ * @param env The environment.
+ * @param it Iterator.
+ * @param it_cls Closure for iterator.
+ */
+void
+GNUNET_PSYC_env_iterate (const struct GNUNET_PSYC_Environment *env,
+                         GNUNET_PSYC_Iterator it, void *it_cls);
+
+
+/**
+ * Get the number of modifiers in the environment.
+ *
+ * @param env The environment.
+ *
+ * @return Number of modifiers.
+ */
+size_t
+GNUNET_PSYC_env_get_count (const struct GNUNET_PSYC_Environment *env);
+
+
+/**
+ * Destroy an environment.
+ *
+ * @param env The environment to destroy.
+ */
+void
+GNUNET_PSYC_env_destroy (struct GNUNET_PSYC_Environment *env);
+
+
+/**
+ * Get the type of variable.
+ *
+ * @param name Name of the variable.
+ *
+ * @return Variable type.
+ */
+enum GNUNET_PSYC_Type
+GNUNET_PSYC_var_get_type (char *name);
+
+
+/**
+ * Perform an operation on a variable.
+ *
+ * @param name Name of variable.
+ * @param current_value Current value of variable.
+ * @param current_value_size Size of @a current_value.
+ * @param oper Operator.
+ * @param args Arguments for the operation.
+ * @param args_size Size of @a args.
+ * @param return_value Return value.
+ * @param return_value_size Size of @a return_value.
+ *
+ * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+ */
+int
+GNUNET_PSYC_operation (char *name, void *current_value, size_t 
current_value_size,
+                       enum GNUNET_PSYC_Operator oper, void *args, size_t 
args_size,
+                       void **return_value, size_t *return_value_size);
+
+
+/**
+ * Get the variable's value as an integer.
+ *
+ * @param size Size of value.
+ * @param value Raw value of variable.
+ * @param[out] number Value converted to a 64-bit integer.
+ *
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR if an error occurred (e.g. 
the value is invalid).
+ */
+int
+GNUNET_PSYC_value_to_number (size_t size, const void *value, int64_t *number);
+
+
+/**
+ * Get the variable's value as a dictionary.
+ *
+ * @param size Size of value.
+ * @param value Raw value of variable.
+ * @param[out] dict A newly created hashmap holding the elements of the 
dictionary.
+ *
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR if an error occurred (e.g. 
the value is invalid).
+ */
+int
+GNUNET_PSYC_value_to_dict (size_t size, const void *value, struct 
GNUNET_CONTAINER_MultiHashMap **dict);
+
+
+/**
+ * Create a PSYC variable value from an integer.
+ *
+ * @param number The number to convert.
+ * @param[out] value_size Size of returned value.
+ *
+ * @return A newly allocated value or NULL on error.
+ */
+void *
+GNUNET_PSYC_value_from_number (int64_t number, size_t *value_size);
+
+
+/**
+ * Create a PSYC variable value from a dictionary.
+ *
+ * @param dict The dict to convert.
+ * @param[out] value_size Size of returned value.
+ *
+ * @return A newly allocated value or NULL on error.
+ */
+void *
+GNUNET_PSYC_value_from_dict (struct GNUNET_CONTAINER_MultiHashMap *dict, 
size_t *value_size);
+
+
+#if 0                           /* keep Emacsens' auto-indent happy */
+{
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+/* ifndef GNUNET_PSYC_ENV_H */
+#endif
+
+/** @} */  /* end of group */
diff --git a/src/include/gnunet_psyc_message.h 
b/src/include/gnunet_psyc_message.h
new file mode 100644
index 0000000..d0cf9cc
--- /dev/null
+++ b/src/include/gnunet_psyc_message.h
@@ -0,0 +1,278 @@
+/*
+     This file is part of GNUnet.
+     Copyright (C) 2012, 2013 GNUnet e.V.
+
+     GNUnet is free software: you can redistribute it and/or modify it
+     under the terms of the GNU Affero General Public License as published
+     by the Free Software Foundation, either version 3 of the License,
+     or (at your option) any later version.
+
+     GNUnet 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
+     Affero General Public License for more details.
+    
+     You should have received a copy of the GNU Affero General Public License
+     along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+     SPDX-License-Identifier: AGPL3.0-or-later
+*/
+
+/**
+ * @author Gabor X Toth
+ *
+ * @file
+ * PSYC message utilities; receiving/transmitting/logging PSYC messages
+ *
+ * @defgroup psyc-util-message  PSYC Utilities library: Messages
+ * Receiving, transmitting, logging PSYC messages.
+ * @{
+ */
+
+#ifndef GNUNET_PSYC_MESSAGE_H
+#define GNUNET_PSYC_MESSAGE_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#if 0                           /* keep Emacsens' auto-indent happy */
+}
+#endif
+#endif
+
+
+#include "gnunet_util_lib.h"
+#include "gnunet_psyc_util_lib.h"
+#include "gnunet_psyc_service.h"
+
+
+/**
+ * Create a PSYC message.
+ *
+ * @param method_name
+ *        PSYC method for the message.
+ * @param env
+ *        Environment for the message.
+ * @param data
+ *        Data payload for the message.
+ * @param data_size
+ *        Size of @a data.
+ *
+ * @return Message header with size information,
+ *         followed by the message parts.
+ *
+ * FIXME: arg order
+ */
+struct GNUNET_PSYC_Message *
+GNUNET_PSYC_message_create (const char *method_name,
+                            const struct GNUNET_PSYC_Environment *env,
+                            const void *data,
+                            size_t data_size);
+
+/**
+ * Parse PSYC message.
+ *
+ * @param msg
+ *        The PSYC message to parse.
+ * @param env
+ *        The environment for the message with a list of modifiers.
+ * @param[out] method_name
+ *        Pointer to the method name inside @a pmsg.
+ * @param[out] data
+ *        Pointer to data inside @a pmsg.
+ * @param[out] data_size
+ *        Size of @data is written here.
+ *
+ * @return #GNUNET_OK on success,
+ *         #GNUNET_SYSERR on parse error.
+ *
+ * FIXME: arg order
+ */
+int
+GNUNET_PSYC_message_parse (const struct GNUNET_PSYC_MessageHeader *msg,
+                           const char **method_name,
+                           struct GNUNET_PSYC_Environment *env,
+                           const void **data,
+                           uint16_t *data_size);
+
+
+void
+GNUNET_PSYC_log_message (enum GNUNET_ErrorType kind,
+                         const struct GNUNET_MessageHeader *msg);
+
+
+struct GNUNET_PSYC_TransmitHandle;
+
+/**
+ * Create a transmission handle.
+ */
+struct GNUNET_PSYC_TransmitHandle *
+GNUNET_PSYC_transmit_create (struct GNUNET_MQ_Handle *mq);
+
+
+/**
+ * Destroy a transmission handle.
+ */
+void
+GNUNET_PSYC_transmit_destroy (struct GNUNET_PSYC_TransmitHandle *tmit);
+
+
+/**
+ * Transmit a message.
+ *
+ * @param tmit
+ *        Transmission handle.
+ * @param method_name
+ *        Which method should be invoked.
+ * @param env
+ *        Environment for the message.
+ *        Should stay available until the first call to notify_data.
+ *        Can be NULL if there are no modifiers or @a notify_mod is
+ *        provided instead.
+ * @param notify_mod
+ *        Function to call to obtain modifiers.
+ *        Can be NULL if there are no modifiers or @a env is provided instead.
+ * @param notify_data
+ *        Function to call to obtain fragments of the data.
+ * @param notify_cls
+ *        Closure for @a notify_mod and @a notify_data.
+ * @param flags
+ *        Flags for the message being transmitted.
+ *
+ * @return #GNUNET_OK if the transmission was started.
+ *         #GNUNET_SYSERR if another transmission is already going on.
+ */
+int
+GNUNET_PSYC_transmit_message (struct GNUNET_PSYC_TransmitHandle *tmit,
+                              const char *method_name,
+                              const struct GNUNET_PSYC_Environment *env,
+                              GNUNET_PSYC_TransmitNotifyModifier notify_mod,
+                              GNUNET_PSYC_TransmitNotifyData notify_data,
+                              void *notify_cls,
+                              uint32_t flags);
+
+
+/**
+ * Resume transmission.
+ *
+ * @param tmit  Transmission handle.
+ */
+void
+GNUNET_PSYC_transmit_resume (struct GNUNET_PSYC_TransmitHandle *tmit);
+
+
+/**
+ * Abort transmission request.
+ *
+ * @param tmit  Transmission handle.
+ */
+void
+GNUNET_PSYC_transmit_cancel (struct GNUNET_PSYC_TransmitHandle *tmit);
+
+
+/**
+ * Got acknowledgement of a transmitted message part, continue transmission.
+ *
+ * @param tmit  Transmission handle.
+ */
+void
+GNUNET_PSYC_transmit_got_ack (struct GNUNET_PSYC_TransmitHandle *tmit);
+
+
+struct GNUNET_PSYC_ReceiveHandle;
+
+
+/**
+ * Create handle for receiving messages.
+ */
+struct GNUNET_PSYC_ReceiveHandle *
+GNUNET_PSYC_receive_create (GNUNET_PSYC_MessageCallback message_cb,
+                            GNUNET_PSYC_MessagePartCallback message_part_cb,
+                            void *cb_cls);
+
+
+/**
+ * Destroy handle for receiving messages.
+ */
+void
+GNUNET_PSYC_receive_destroy (struct GNUNET_PSYC_ReceiveHandle *recv);
+
+
+/**
+ * Reset stored data related to the last received message.
+ */
+void
+GNUNET_PSYC_receive_reset (struct GNUNET_PSYC_ReceiveHandle *recv);
+
+
+/**
+ * Handle incoming PSYC message.
+ *
+ * @param recv
+ *        Receive handle.
+ * @param msg
+ *        The message.
+ *
+ * @return #GNUNET_OK on success,
+ *         #GNUNET_SYSERR on receive error.
+ */
+int
+GNUNET_PSYC_receive_message (struct GNUNET_PSYC_ReceiveHandle *recv,
+                             const struct GNUNET_PSYC_MessageHeader *msg);
+
+
+/**
+ * Check if @a data contains a series of valid message parts.
+ *
+ * @param data_size
+ *        Size of @a data.
+ * @param data
+ *        Data.
+ * @param[out] first_ptype
+ *        Type of first message part.
+ * @param[out] last_ptype
+ *        Type of last message part.
+ *
+ * @return Number of message parts found in @a data.
+ *         or GNUNET_SYSERR if the message contains invalid parts.
+ */
+int
+GNUNET_PSYC_receive_check_parts (uint16_t data_size, const char *data,
+                                 uint16_t *first_ptype, uint16_t *last_ptype);
+
+
+/**
+ * Initialize PSYC message header.
+ */
+void
+GNUNET_PSYC_message_header_init (struct GNUNET_PSYC_MessageHeader *pmsg,
+                                 const struct GNUNET_MULTICAST_MessageHeader 
*mmsg,
+                                 uint32_t flags);
+
+
+/**
+ * Create a new PSYC message header from a multicast message for sending it to 
clients.
+ */
+struct GNUNET_PSYC_MessageHeader *
+GNUNET_PSYC_message_header_create (const struct GNUNET_MULTICAST_MessageHeader 
*mmsg,
+                                   uint32_t flags);
+
+
+/**
+ * Create a new PSYC message header from a PSYC message.
+ */
+struct GNUNET_PSYC_MessageHeader *
+GNUNET_PSYC_message_header_create_from_psyc (const struct GNUNET_PSYC_Message 
*msg);
+
+
+#if 0                           /* keep Emacsens' auto-indent happy */
+{
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+/* ifndef GNUNET_PSYC_MESSAGE_H */
+#endif
+
+/** @} */  /* end of group */
diff --git a/src/include/gnunet_psyc_service.h 
b/src/include/gnunet_psyc_service.h
new file mode 100644
index 0000000..3a3131e
--- /dev/null
+++ b/src/include/gnunet_psyc_service.h
@@ -0,0 +1,1364 @@
+/*
+     This file is part of GNUnet.
+     Copyright (C) 2012, 2013 GNUnet e.V.
+
+     GNUnet is free software: you can redistribute it and/or modify it
+     under the terms of the GNU Affero General Public License as published
+     by the Free Software Foundation, either version 3 of the License,
+     or (at your option) any later version.
+
+     GNUnet 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
+     Affero General Public License for more details.
+    
+     You should have received a copy of the GNU Affero General Public License
+     along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+     SPDX-License-Identifier: AGPL3.0-or-later
+*/
+
+/**
+ * @author Gabor X Toth
+ * @author Christian Grothoff
+ *
+ * @file
+ * PSYC service
+ *
+ * @defgroup psyc  PSYC service
+ * Send/receive messages in PSYC channels and access the PSYC Store.
+ *
+ * Note that clients of this API are NOT expected to understand the PSYC 
message
+ * format, only the semantics!  Parsing (and serializing) the PSYC stream 
format
+ * is done within the implementation of the libgnunetpsyc library, and this API
+ * deliberately exposes as little as possible of the actual data stream format
+ * to the application!
+ *
+ * NOTE:
+ * - this API does not know about PSYC's "root" and "places";
+ *   there is no 'root' in GNUnet-PSYC as we're decentralized;
+ *   'places' and 'persons' are combined within the same
+ *   abstraction, that of a "channel".  Channels are identified
+ *   and accessed in this API using a public/private key.
+ *   Higher-level applications should use NAMES within GNS
+ *   to obtain public keys, and the distinction between
+ *   'places' and 'persons' can then be made with the help
+ *   of the naming system (and/or conventions).
+ *   Channels are (as in PSYC) organized into a hierarchy; each
+ *   channel master (the one with the private key) is then
+ *   the operator of the multicast group (its Origin in
+ *   the terminology of the multicast API).
+ * - The API supports passing large amounts of data using
+ *   'streaming' for the argument passed to a method.  State
+ *   and variables must fit into memory and cannot be streamed
+ *   (thus, no passing of 4 GB of data in a variable;
+ *   once we implement this, we might want to create a
+ *   @c \#define for the maximum size of a variable).
+ * - PSYC defines standard variables, methods, etc.  This
+ *   library deliberately abstracts over all of these; a
+ *   higher-level API should combine the naming system (GNS)
+ *   and standard methods (_converse, _notice, _request,
+ *   _warning, _error etc) and variables (_action, _color,
+ *   _time, etc).  However, this API does take over the
+ *   routing variables, specifically '_context' (channel),
+ *   and '_source'.  We only kind-of support '_target', as
+ *   the target is either everyone in the group or the
+ *   origin, and never just a single member of the group;
+ *   for such individual messages, an application needs to
+ *   construct an 'inbox' channel where the master (only)
+ *   receives messages (but never forwards; private responses
+ *   would be transmitted by joining the senders 'inbox'
+ *   channel -- or a inbox#bob subchannel).  The
+ *   goal for all of this is to keep the abstractions in this
+ *   API minimal: interaction with multicast, try \& slice,
+ *   state/variable/channel management.  Higher-level
+ *   operations belong elsewhere (so maybe this API should
+ *   be called 'PSYC-low', whereas a higher-level API
+ *   implementing defaults for standard methods and
+ *   variables might be called 'PSYC-std' or 'PSYC-high'.
+ *
+ *   In PSYC terminology this is simply called the "PSYC
+ *   routing layer" and the abstractions, for instance in
+ *   psyced, are quite similar. The higher one is called
+ *   "PSYC entity layer." In the text rendering of the
+ *   protocol the two are separated by an empty line. See
+ *   http://about.psyc.eu/Spec:Packet and related.  --lynX
+ *
+ * @{
+ */
+
+#ifndef GNUNET_PSYC_SERVICE_H
+#define GNUNET_PSYC_SERVICE_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#if 0                           /* keep Emacsens' auto-indent happy */
+}
+#endif
+#endif
+
+#include "gnunet_util_lib.h"
+#include "gnunet_multicast_service.h"
+//Mingw work around
+#ifdef MINGW
+    # ifndef  UINT64_MAX
+    # define  UINT64_MAX 0xffffffffffffffffULL
+    # endif
+#endif
+
+/**
+ * Version number of GNUnet-PSYC API.
+ */
+#define GNUNET_PSYC_VERSION 0x00000000
+
+
+/**
+ * Policy flags for a channel.
+ */
+enum GNUNET_PSYC_ChannelFlags
+{
+  /**
+   * Admission must be confirmed by the master.
+   */
+  GNUNET_PSYC_CHANNEL_ADMISSION_CONTROL = 1 << 0,
+
+  /**
+   * Past messages are only available to slaves who were admitted at the time
+   * they were sent to the channel.
+   */
+  GNUNET_PSYC_CHANNEL_RESTRICTED_HISTORY = 1 << 1
+};
+
+
+/**
+ * PSYC channel policies.
+ */
+enum GNUNET_PSYC_Policy
+{
+  /**
+   * Anyone can join the channel, without announcing their presence;
+   * all messages are always public and can be distributed freely.
+   * Joins may be announced, but this is not required.
+   */
+  GNUNET_PSYC_CHANNEL_ANONYMOUS = 0,
+
+  /**
+   * The master must approve membership to the channel, messages must only be
+   * distributed to current channel slaves.  This includes the channel
+   * state as well as transient messages.
+   */
+  GNUNET_PSYC_CHANNEL_PRIVATE
+    = GNUNET_PSYC_CHANNEL_ADMISSION_CONTROL
+    | GNUNET_PSYC_CHANNEL_RESTRICTED_HISTORY
+
+#if IDEAS_FOR_FUTURE
+  /**
+   * Anyone can freely join the channel (no approval required);
+   * however, messages must only be distributed to current channel
+   * slaves, so the master must still acknowledge that the slave
+   * joined before transient messages are delivered.  As approval is
+   * guaranteed, the presistent channel state can be synchronized freely
+   * immediately, prior to master confirmation.
+   */
+  GNUNET_PSYC_CHANNEL_OPEN
+    = GNUNET_PSYC_CHANNEL_RESTRICTED_HISTORY,
+
+  /**
+   * The master must approve joins to the channel, but past messages can be
+   * freely distributed to slaves.
+   */
+  GNUNET_PSYC_CHANNEL_CLOSED
+    = GNUNET_PSYC_CHANNEL_ADMISSION_CONTROL,
+#endif
+};
+
+
+enum GNUNET_PSYC_MessageFlags
+{
+  /**
+   * Default / no flags.
+   */
+  GNUNET_PSYC_MESSAGE_DEFAULT = 0,
+
+  /**
+   * Historic message, retrieved from PSYCstore.
+   */
+  GNUNET_PSYC_MESSAGE_HISTORIC = 1 << 0,
+
+  /**
+   * Request from slave to master.
+   */
+  GNUNET_PSYC_MESSAGE_REQUEST = 1 << 1,
+
+  /**
+   * Message can be delivered out of order.
+   */
+  GNUNET_PSYC_MESSAGE_ORDER_ANY = 1 << 2
+};
+
+
+/**
+ * Values for the @a state_delta field of GNUNET_PSYC_MessageHeader.
+ */
+enum GNUNET_PSYC_StateDeltaValues
+{
+  GNUNET_PSYC_STATE_RESET = 0,
+
+  GNUNET_PSYC_STATE_NOT_MODIFIED = UINT64_MAX
+};
+
+
+GNUNET_NETWORK_STRUCT_BEGIN
+
+/**
+ * A PSYC message.
+ *
+ * Used for single-fragment messages e.g. in a join request or response.
+ */
+struct GNUNET_PSYC_Message
+{
+  /**
+   * Message header with size and type information.
+   */
+  struct GNUNET_MessageHeader header;
+
+  /* Followed by concatenated PSYC message parts:
+   * messages with GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_* types
+   */
+};
+
+
+/**
+ * Header of a PSYC message.
+ *
+ * The PSYC service adds this when delivering the message to local clients,
+ * not present on the multicast layer.
+ */
+struct GNUNET_PSYC_MessageHeader
+{
+  /**
+   * Generic message header with size and type information.
+   */
+  struct GNUNET_MessageHeader header;
+
+  /**
+   * Flags for this message fragment.
+   *
+   * @see enum GNUNET_PSYC_MessageFlags
+   */
+  uint32_t flags GNUNET_PACKED;
+
+  /**
+   * Number of the message this message part belongs to.
+   * Monotonically increasing from 1.
+   */
+  uint64_t message_id GNUNET_PACKED;
+
+  /**
+   * Byte offset of this @e fragment of the @e message.
+   */
+  uint64_t fragment_offset GNUNET_PACKED;
+
+  /**
+   * Sending slave's public key.
+   * Not set if the message is from the master.
+   */
+  struct GNUNET_CRYPTO_EcdsaPublicKey slave_pub_key;
+
+  /* Followed by concatenated PSYC message parts:
+   * messages with GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_* types
+   */
+};
+
+
+/**
+ * The method of a message.
+ */
+struct GNUNET_PSYC_MessageMethod
+{
+  /**
+   * Type: GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_METHOD
+   */
+  struct GNUNET_MessageHeader header;
+
+  /**
+   * OR'ed GNUNET_PSYC_MasterTransmitFlags
+   */
+  uint32_t flags GNUNET_PACKED;
+
+  /**
+   * Number of message IDs since the last message that contained state
+   * operations. @see enum GNUNET_PSYC_StateDeltaValues
+   */
+  uint64_t state_delta GNUNET_PACKED;
+
+  /* Followed by NUL-terminated method name. */
+};
+
+
+/**
+ * A modifier of a message.
+ */
+struct GNUNET_PSYC_MessageModifier
+{
+  /**
+   * Type: GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MODIFIER
+   */
+  struct GNUNET_MessageHeader header;
+
+  /**
+   * Size of value.
+   */
+  uint32_t value_size GNUNET_PACKED;
+
+  /**
+   * Size of name, including NUL terminator.
+   */
+  uint16_t name_size GNUNET_PACKED;
+
+  /**
+   * enum GNUNET_PSYC_Operator
+   */
+  uint8_t oper;
+
+  /* Followed by NUL-terminated name, then the value. */
+};
+
+
+struct GNUNET_PSYC_CountersResultMessage
+{
+  /**
+   * Type: GNUNET_MESSAGE_TYPE_PSYC_RESULT_COUNTERS
+   */
+  struct GNUNET_MessageHeader header;
+
+  /**
+   * Status code for the operation.
+   */
+  uint32_t result_code GNUNET_PACKED;
+
+  /**
+   * Last message ID sent to the channel.
+   */
+  uint64_t max_message_id GNUNET_PACKED;
+};
+
+
+/**
+ * Join request sent to a PSYC master.
+ */
+struct GNUNET_PSYC_JoinRequestMessage
+{
+  /**
+   * Type: GNUNET_MESSAGE_TYPE_PSYC_MASTER_JOIN_REQUEST
+   */
+  struct GNUNET_MessageHeader header;
+  /**
+   * Public key of the joining slave.
+   */
+  struct GNUNET_CRYPTO_EcdsaPublicKey slave_pub_key;
+
+  /* Followed by struct GNUNET_MessageHeader join_request */
+};
+
+
+/**
+ * Join decision sent in reply to a join request.
+ */
+struct GNUNET_PSYC_JoinDecisionMessage
+{
+  /**
+   * Type: GNUNET_MESSAGE_TYPE_PSYC_JOIN_DECISION
+   */
+  struct GNUNET_MessageHeader header;
+
+  /**
+   * #GNUNET_YES if the slave was admitted.
+   */
+  int32_t is_admitted;
+
+  /**
+   * Public key of the joining slave.
+   * Only set when the master is sending the decision,
+   * not set when a slave is receiving it.
+   */
+  struct GNUNET_CRYPTO_EcdsaPublicKey slave_pub_key;
+
+  /* Followed by struct GNUNET_MessageHeader join_response */
+};
+
+
+enum GNUNET_PSYC_HistoryReplayFlags
+{
+  /**
+   * Replay locally available messages.
+   */
+  GNUNET_PSYC_HISTORY_REPLAY_LOCAL  = 0,
+
+  /**
+   * Replay messages from remote peers if not found locally.
+   */
+  GNUNET_PSYC_HISTORY_REPLAY_REMOTE = 1,
+};
+
+
+struct GNUNET_PSYC_HistoryRequestMessage
+{
+  /**
+   * Type: GNUNET_MESSAGE_TYPE_PSYC_CHANNEL_HISTORY_REPLAY
+   */
+  struct GNUNET_MessageHeader header;
+
+  /**
+   * @see enum GNUNET_PSYC_HistoryReplayFlags
+   */
+  uint32_t flags GNUNET_PACKED;
+
+  /**
+   * ID for this operation.
+   */
+  uint64_t op_id GNUNET_PACKED;
+
+  uint64_t start_message_id GNUNET_PACKED;
+
+  uint64_t end_message_id GNUNET_PACKED;
+
+  uint64_t message_limit GNUNET_PACKED;
+
+  /* Followed by NUL-terminated method name prefix. */
+};
+
+
+struct GNUNET_PSYC_StateRequestMessage
+{
+  /**
+   * Types:
+   * - GNUNET_MESSAGE_TYPE_PSYC_CHANNEL_STATE_GET
+   * - GNUNET_MESSAGE_TYPE_PSYC_CHANNEL_STATE_GET_PREFIX
+   */
+  struct GNUNET_MessageHeader header;
+
+  uint32_t reserved GNUNET_PACKED;
+
+  /**
+   * ID for this operation.
+   */
+  uint64_t op_id GNUNET_PACKED;
+
+  /* Followed by NUL-terminated name. */
+};
+
+
+/**** service -> library ****/
+
+
+/**
+ * Answer from service to client about last operation.
+ */
+struct GNUNET_PSYC_OperationResultMessage
+{
+  /**
+   * Types:
+   * - GNUNET_MESSAGE_TYPE_PSYC_RESULT_CODE
+   * - GNUNET_MESSAGE_TYPE_PSYC_CHANNEL_STATE_RESULT
+   */
+  struct GNUNET_MessageHeader header;
+
+  uint32_t reserved GNUNET_PACKED;
+
+  /**
+   * Operation ID.
+   */
+  uint64_t op_id GNUNET_PACKED;
+
+  /**
+   * Status code for the operation.
+   */
+  uint64_t result_code GNUNET_PACKED;
+
+  /* Followed by:
+   * - on error: NUL-terminated error message
+   * - on success: one of the following message types
+   *
+   *   For a STATE_RESULT, one of:
+   *   - GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MODIFIER
+   *   - GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MOD_CONT
+   *   - GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END
+   */
+};
+
+GNUNET_NETWORK_STRUCT_END
+
+
+#define GNUNET_PSYC_MODIFIER_MAX_PAYLOAD        \
+  GNUNET_MULTICAST_FRAGMENT_MAX_PAYLOAD         \
+  - sizeof (struct GNUNET_PSYC_MessageModifier)
+
+#define GNUNET_PSYC_MOD_CONT_MAX_PAYLOAD        \
+  GNUNET_MULTICAST_FRAGMENT_MAX_PAYLOAD         \
+  - sizeof (struct GNUNET_MessageHeader)
+
+#define GNUNET_PSYC_DATA_MAX_PAYLOAD            \
+  GNUNET_MULTICAST_FRAGMENT_MAX_PAYLOAD         \
+  - sizeof (struct GNUNET_MessageHeader)
+
+
+/**
+ * PSYC message part processing states.
+ */
+enum GNUNET_PSYC_MessageState
+{
+  GNUNET_PSYC_MESSAGE_STATE_START    = 0,
+  GNUNET_PSYC_MESSAGE_STATE_HEADER   = 1,
+  GNUNET_PSYC_MESSAGE_STATE_METHOD   = 2,
+  GNUNET_PSYC_MESSAGE_STATE_MODIFIER = 3,
+  GNUNET_PSYC_MESSAGE_STATE_MOD_CONT = 4,
+  GNUNET_PSYC_MESSAGE_STATE_DATA     = 5,
+  GNUNET_PSYC_MESSAGE_STATE_END      = 6,
+  GNUNET_PSYC_MESSAGE_STATE_CANCEL   = 7,
+  GNUNET_PSYC_MESSAGE_STATE_ERROR    = 8,
+};
+
+
+/**
+ * Handle that identifies a join request.
+ *
+ * Used to match calls to #GNUNET_PSYC_JoinCallback to the
+ * corresponding calls to GNUNET_PSYC_join_decision().
+ */
+struct GNUNET_PSYC_JoinHandle;
+
+
+/**
+ * Method called from PSYC upon receiving a message.
+ *
+ * @param cls  Closure.
+ * @param message_id  Sequence number of the message.
+ * @param flags  OR'ed GNUNET_PSYC_MessageFlags
+ * @param msg  Message part, one of the following types:
+ */
+typedef void
+(*GNUNET_PSYC_MessageCallback) (void *cls,
+                                const struct GNUNET_PSYC_MessageHeader *msg);
+
+
+/**
+ * Method called from PSYC upon receiving part of a message.
+ *
+ * @param cls
+ *        Closure.
+ * @param slave_pub_key
+ *        Public key of the slave sending the message.
+ *        Only set for channel master.
+ * @param message_id
+ *        Sequence number of the message.
+ * @param flags
+ *        OR'ed GNUNET_PSYC_MessageFlags
+ * @param fragment_offset
+ *        Multicast message fragment offset.
+ * @param msg  Message part, one of the following types:
+ * - #GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_HEADER
+ * - #GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_METHOD
+ * - #GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MODIFIER
+ * - #GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MOD_CONT
+ * - #GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_DATA
+ * or NULL if an error occurred while receiving a message.
+ */
+typedef void
+(*GNUNET_PSYC_MessagePartCallback) (void *cls,
+                                    const struct GNUNET_PSYC_MessageHeader 
*msg,
+                                    const struct GNUNET_MessageHeader *pmsg);
+
+
+/**
+ * Method called from PSYC upon receiving a join request.
+ *
+ * @param cls
+ *        Closure.
+ * @param slave_pub_key
+ *        Public key of the slave requesting join.
+ * @param join_msg
+ *        Join message sent along with the request.
+ * @param jh
+ *        Join handle to use with GNUNET_PSYC_join_decision()
+ */
+typedef void
+(*GNUNET_PSYC_JoinRequestCallback) (void *cls,
+                                    const struct 
GNUNET_PSYC_JoinRequestMessage *req,
+                                    const struct GNUNET_CRYPTO_EcdsaPublicKey 
*slave_pub_key,
+                                    const struct GNUNET_PSYC_Message *join_msg,
+                                    struct GNUNET_PSYC_JoinHandle *jh);
+
+
+/**
+ * Function to call with the decision made for a join request.
+ *
+ * Must be called once and only once in response to an invocation of the
+ * #GNUNET_PSYC_JoinCallback.
+ *
+ * @param jh  Join request handle.
+ * @param is_admitted
+ *   #GNUNET_YES    if the join is approved,
+ *   #GNUNET_NO     if it is disapproved,
+ *   #GNUNET_SYSERR if we cannot answer the request.
+ * @param relay_count  Number of relays given.
+ * @param relays  Array of suggested peers that might be useful relays to use
+ *        when joining the multicast group (essentially a list of peers that
+ *        are already part of the multicast group and might thus be willing
+ *        to help with routing).  If empty, only this local peer (which must
+ *        be the multicast origin) is a good candidate for building the
+ *        multicast tree.  Note that it is unnecessary to specify our own
+ *        peer identity in this array.
+ * @param join_resp  Application-dependent join response message to send along
+ *        with the decision.
+ *
+ * @return #GNUNET_OK on success,
+ *         #GNUNET_SYSERR if @a join_resp is too large.
+ */
+int
+GNUNET_PSYC_join_decision (struct GNUNET_PSYC_JoinHandle *jh,
+                           int is_admitted,
+                           uint32_t relay_count,
+                           const struct GNUNET_PeerIdentity *relays,
+                           const struct GNUNET_PSYC_Message *join_resp);
+
+
+/**
+ * Handle for the master of a PSYC channel.
+ */
+struct GNUNET_PSYC_Master;
+
+
+/**
+ * Function called once we are connected to the PSYC service
+ * and the channel master is started.
+ *
+ * Also called when we reconnected to the service
+ * after the connection closed unexpectedly.
+ *
+ * @param cls
+ *        Closure.
+ * @param result
+ *        #GNUNET_YES if there were already messages sent to the channel,
+ *        #GNUNET_NO  if the message history is empty,
+ *        #GNUNET_SYSERR on error.
+ * @param max_message_id
+ *        Last message ID sent to the channel.
+ */
+typedef void
+(*GNUNET_PSYC_MasterStartCallback) (void *cls, int result,
+                                    uint64_t max_message_id);
+
+
+/**
+ * Start a PSYC master channel.
+ *
+ * Will start a multicast group identified by the given ECC key.  Messages
+ * received from group members will be given to the respective handler methods.
+ * If a new member wants to join a group, the "join" method handler will be
+ * invoked; the join handler must then generate a "join" message to approve the
+ * joining of the new member.  The channel can also change group membership
+ * without explicit requests.  Note that PSYC doesn't itself "understand" join
+ * or part messages, the respective methods must call other PSYC functions to
+ * inform PSYC about the meaning of the respective events.
+ *
+ * @param cfg  Configuration to use (to connect to PSYC service).
+ * @param channel_key  ECC key that will be used to sign messages for this
+ *        PSYC session. The public key is used to identify the PSYC channel.
+ *        Note that end-users will usually not use the private key directly, 
but
+ *        rather look it up in GNS for places managed by other users, or select
+ *        a file with the private key(s) when setting up their own channels
+ *        FIXME: we'll likely want to use NOT the p521 curve here, but a 
cheaper
+ *        one in the future.
+ * @param policy  Channel policy specifying join and history restrictions.
+ *        Used to automate join decisions.
+ * @param master_start_cb  Function to invoke after the channel master started.
+ * @param join_request_cb  Function to invoke when a slave wants to join.
+ * @param message_cb  Function to invoke on message parts sent to the channel
+ *        and received from slaves
+ * @param cls  Closure for @a method and @a join_cb.
+ *
+ * @return Handle for the channel master, NULL on error.
+ */
+struct GNUNET_PSYC_Master *
+GNUNET_PSYC_master_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
+                          const struct GNUNET_CRYPTO_EddsaPrivateKey 
*channel_key,
+                          enum GNUNET_PSYC_Policy policy,
+                          GNUNET_PSYC_MasterStartCallback master_start_cb,
+                          GNUNET_PSYC_JoinRequestCallback join_request_cb,
+                          GNUNET_PSYC_MessageCallback message_cb,
+                          GNUNET_PSYC_MessagePartCallback message_part_cb,
+                          void *cls);
+
+
+/**
+ * Function called to provide data for a transmission via PSYC.
+ *
+ * Note that returning #GNUNET_YES or #GNUNET_SYSERR (but not #GNUNET_NO)
+ * invalidates the respective transmission handle.
+ *
+ * @param cls Closure.
+ * @param[in,out] data_size Initially set to the number of bytes available in
+ *        @a data, should be set to the number of bytes written to data.
+ * @param[out] data Where to write the body of the message to give to the
+ *         method. The function must copy at most @a data_size bytes to @a 
data.
+ * @return #GNUNET_SYSERR on error (fatal, aborts transmission)
+ *         #GNUNET_NO on success, if more data is to be transmitted later.
+ *         Should be used if @a data_size was not big enough to take all the
+ *         data.  If 0 is returned in @a data_size the transmission is paused,
+ *         and can be resumed with GNUNET_PSYC_master_transmit_resume().
+ *         #GNUNET_YES if this completes the transmission (all data supplied)
+ */
+typedef int
+(*GNUNET_PSYC_TransmitNotifyData) (void *cls,
+                                   uint16_t *data_size,
+                                   void *data);
+
+/**
+ * Function called to provide a modifier for a transmission via PSYC.
+ *
+ * Note that returning #GNUNET_YES or #GNUNET_SYSERR (but not #GNUNET_NO)
+ * invalidates the respective transmission handle.
+ *
+ * @param cls Closure.
+ * @param[in,out] data_size  Initially set to the number of bytes available in
+ *         @a data, should be set to the number of bytes written to data.
+ * @param[out] data  Where to write the modifier's name and value.
+ *         The function must copy at most @a data_size bytes to @a data.
+ *         When this callback is first called for a modifier, @a data should
+ *         contain: "name\0value".  If the whole value does not fit, subsequent
+ *         calls to this function should write continuations of the value to
+ *         @a data.
+ * @param[out] oper  Where to write the operator of the modifier.
+ *         Only needed during the first call to this callback at the beginning
+ *         of the modifier.  In case of subsequent calls asking for value
+ *         continuations @a oper is set to #NULL.
+ * @param[out] full_value_size  Where to write the full size of the value.
+ *         Only needed during the first call to this callback at the beginning
+ *         of the modifier.  In case of subsequent calls asking for value
+ *         continuations @a value_size is set to #NULL.
+ * @return #GNUNET_SYSERR on error (fatal, aborts transmission)
+ *         #GNUNET_NO on success, if more data is to be transmitted later.
+ *         Should be used if @a data_size was not big enough to take all the
+ *         data for the modifier's value (the name must be always returned
+ *         during the first call to this callback).
+ *         If 0 is returned in @a data_size the transmission is paused,
+ *         and can be resumed with GNUNET_PSYC_master_transmit_resume().
+ *         #GNUNET_YES if this completes the modifier (the whole value is 
supplied).
+ */
+typedef int
+(*GNUNET_PSYC_TransmitNotifyModifier) (void *cls,
+                                       uint16_t *data_size,
+                                       void *data,
+                                       uint8_t *oper,
+                                       uint32_t *full_value_size);
+
+/**
+ * Flags for transmitting messages to a channel by the master.
+ */
+enum GNUNET_PSYC_MasterTransmitFlags
+{
+  GNUNET_PSYC_MASTER_TRANSMIT_NONE = 0,
+
+  /**
+   * Whether this message should reset the channel state,
+   * i.e. remove all previously stored state variables.
+   */
+
+  GNUNET_PSYC_MASTER_TRANSMIT_STATE_RESET = 1 << 0,
+
+  /**
+   * Whether this message contains any state modifiers.
+   */
+  GNUNET_PSYC_MASTER_TRANSMIT_STATE_MODIFY = 1 << 1,
+
+  /**
+   * Add PSYC header variable with the hash of the current channel state.
+   */
+  GNUNET_PSYC_MASTER_TRANSMIT_STATE_HASH = 1 << 2,
+
+  /**
+   * Whether we need to increment the group generation counter after
+   * transmitting this message.
+   */
+  GNUNET_PSYC_MASTER_TRANSMIT_INC_GROUP_GEN = 1 << 3
+};
+
+
+/**
+ * Handle for a pending PSYC transmission operation.
+ */
+struct GNUNET_PSYC_MasterTransmitHandle;
+
+
+/**
+ * Send a message to call a method to all members in the PSYC channel.
+ *
+ * @param master Handle to the PSYC channel.
+ * @param method_name Which method should be invoked.
+ * @param notify_mod Function to call to obtain modifiers.
+ * @param notify_data Function to call to obtain fragments of the data.
+ * @param notify_cls Closure for @a notify_mod and @a notify_data.
+ * @param flags Flags for the message being transmitted.
+ * @return Transmission handle, NULL on error (i.e. more than one request 
queued).
+ */
+struct GNUNET_PSYC_MasterTransmitHandle *
+GNUNET_PSYC_master_transmit (struct GNUNET_PSYC_Master *master,
+                             const char *method_name,
+                             GNUNET_PSYC_TransmitNotifyModifier notify_mod,
+                             GNUNET_PSYC_TransmitNotifyData notify_data,
+                             void *notify_cls,
+                             enum GNUNET_PSYC_MasterTransmitFlags flags);
+
+
+/**
+ * Resume transmission to the channel.
+ *
+ * @param th Handle of the request that is being resumed.
+ */
+void
+GNUNET_PSYC_master_transmit_resume (struct GNUNET_PSYC_MasterTransmitHandle 
*th);
+
+
+/**
+ * Abort transmission request to channel.
+ *
+ * @param th Handle of the request that is being aborted.
+ */
+void
+GNUNET_PSYC_master_transmit_cancel (struct GNUNET_PSYC_MasterTransmitHandle 
*th);
+
+
+/**
+ * Relay a message
+ *
+ * @param master Handle to the PSYC channel.
+ * @param method_name Which method should be invoked.
+ * @param notify_mod Function to call to obtain modifiers.
+ * @param notify_data Function to call to obtain fragments of the data.
+ * @param notify_cls Closure for @a notify_mod and @a notify_data.
+ * @param flags Flags for the message being transmitted.
+ * @return Transmission handle, NULL on error (i.e. more than one request 
queued).
+ */
+struct GNUNET_PSYC_MasterTransmitHandle *
+GNUNET_PSYC_master_relay (struct GNUNET_PSYC_Master *master,
+                          uint64_t message_id);
+
+
+/**
+ * Stop a PSYC master channel.
+ *
+ * @param master
+ *        PSYC channel master to stop.
+ * @param keep_active
+ *        Keep place active after last application disconnected.
+ * @param stop_cb
+ *        Function called after the master stopped
+ *        and disconnected from the psyc service.
+ * @param stop_cls
+ *        Closure for @a part_cb.
+ */
+void
+GNUNET_PSYC_master_stop (struct GNUNET_PSYC_Master *master,
+                         int keep_active,
+                         GNUNET_ContinuationCallback stop_cb,
+                         void *stop_cls);
+
+
+/**
+ * Handle for a PSYC channel slave.
+ */
+struct GNUNET_PSYC_Slave;
+
+
+/**
+ * Function called after the slave connected to the PSYC service.
+ *
+ * Also called when reconnected to the service
+ * after the connection closed unexpectedly.
+ *
+ * @param cls
+ *        Closure.
+ * @param result
+ *        #GNUNET_YES if there were already messages sent to the channel,
+ *        #GNUNET_NO  if the message history is empty,
+ *        #GNUNET_SYSERR on error.
+ * @param max_message_id
+ *        Last message ID sent to the channel.
+ */
+typedef void
+(*GNUNET_PSYC_SlaveConnectCallback) (void *cls, int result,
+                                     uint64_t max_message_id);
+
+
+/**
+ * Method called to inform about the decision in response to a join request.
+ *
+ * If @a is_admitted is not #GNUNET_YES, then sending messages to the channel 
is
+ * not possible, but earlier history can be still queried.
+ *
+ * @param cls  Closure.
+ * @param is_admitted  #GNUNET_YES or #GNUNET_NO or #GNUNET_SYSERR
+ * @param join_msg  Application-dependent join message from the origin.
+ */
+typedef void
+(*GNUNET_PSYC_JoinDecisionCallback) (void *cls,
+                                     const struct 
GNUNET_PSYC_JoinDecisionMessage *dcsn,
+                                     int is_admitted,
+                                     const struct GNUNET_PSYC_Message 
*join_msg);
+
+/**
+ * Flags for GNUNET_PSYC_slave_join()
+ */
+enum GNUNET_PSYC_SlaveJoinFlags
+{
+  GNUNET_PSYC_SLAVE_JOIN_NONE  = 0,
+
+  /**
+   * Local join for history access, no network connection is established.
+   */
+  GNUNET_PSYC_SLAVE_JOIN_LOCAL = 1,
+};
+
+
+/**
+ * Join a PSYC channel.
+ *
+ * The entity joining is always the local peer.  The user must immediately use
+ * the GNUNET_PSYC_slave_transmit() functions to transmit a @e join_msg to the
+ * channel; if the join request succeeds, the channel state (and @e recent
+ * method calls) will be replayed to the joining member.  There is no explicit
+ * notification on failure (as the channel may simply take days to approve,
+ * and disapproval is simply being ignored).
+ *
+ * @param cfg
+ *        Configuration to use.
+ * @param channel_pub_key
+ *        ECC public key that identifies the channel we wish to join.
+ * @param slave_pub_key
+ *        ECC private-public key pair that identifies the slave, and
+ *        used by multicast to sign the join request and subsequent unicast
+ *        requests sent to the master.
+ * @param flags
+ *        Join flags.
+ * @param origin
+ *        Peer identity of the origin.
+ * @param relay_count
+ *        Number of peers in the @a relays array.
+ * @param relays
+ *        Peer identities of members of the multicast group, which serve
+ *        as relays and used to join the group at.
+ * @param message_cb
+ *        Function to invoke on message fragments received from the channel.
+ * @param message_part_cb
+ *        Function to invoke on message parts received from the channel.
+ * @param slave_connect_cb
+ *        Function invoked once we have connected to the PSYC service.
+ * @param join_decision_cb
+ *        Function invoked once we have received a join decision.
+ * @param cls
+ *        Closure for @a message_cb and @a slave_joined_cb.
+ * @param join_msg
+ *        Join message.
+ *
+ * @return Handle for the slave, NULL on error.
+ */
+struct GNUNET_PSYC_Slave *
+GNUNET_PSYC_slave_join (const struct GNUNET_CONFIGURATION_Handle *cfg,
+                        const struct GNUNET_CRYPTO_EddsaPublicKey 
*channel_pub_key,
+                        const struct GNUNET_CRYPTO_EcdsaPrivateKey 
*slave_pub_key,
+                        enum GNUNET_PSYC_SlaveJoinFlags flags,
+                        const struct GNUNET_PeerIdentity *origin,
+                        uint32_t relay_count,
+                        const struct GNUNET_PeerIdentity *relays,
+                        GNUNET_PSYC_MessageCallback message_cb,
+                        GNUNET_PSYC_MessagePartCallback message_part_cb,
+                        GNUNET_PSYC_SlaveConnectCallback slave_connect_cb,
+                        GNUNET_PSYC_JoinDecisionCallback join_decision_cb,
+                        void *cls,
+                        const struct GNUNET_PSYC_Message *join_msg);
+
+
+/**
+ * Part a PSYC channel.
+ *
+ * Will terminate the connection to the PSYC service.  Polite clients should
+ * first explicitly send a part request (via GNUNET_PSYC_slave_transmit()).
+ *
+ * @param slave
+ *        Slave handle.
+ * @param keep_active
+ *        Keep place active after last application disconnected.
+ * @param part_cb
+ *        Function called after the slave parted the channel
+ *        and disconnected from the psyc service.
+ * @param part_cls
+ *        Closure for @a part_cb.
+ */
+void
+GNUNET_PSYC_slave_part (struct GNUNET_PSYC_Slave *slave,
+                        int keep_active,
+                        GNUNET_ContinuationCallback part_cb,
+                        void *part_cls);
+
+
+/**
+ * Flags for transmitting messages to the channel master by a slave.
+ */
+enum GNUNET_PSYC_SlaveTransmitFlags
+{
+  GNUNET_PSYC_SLAVE_TRANSMIT_NONE = 0
+};
+
+
+/**
+ * Handle for a pending PSYC transmission operation.
+ */
+struct GNUNET_PSYC_SlaveTransmitHandle;
+
+
+/**
+ * Request a message to be sent to the channel master.
+ *
+ * @param slave Slave handle.
+ * @param method_name Which (PSYC) method should be invoked (on host).
+ * @param notify_mod Function to call to obtain modifiers.
+ * @param notify_data Function to call to obtain fragments of the data.
+ * @param notify_cls Closure for @a notify.
+ * @param flags Flags for the message being transmitted.
+ * @return Transmission handle, NULL on error (i.e. more than one request 
queued).
+ */
+struct GNUNET_PSYC_SlaveTransmitHandle *
+GNUNET_PSYC_slave_transmit (struct GNUNET_PSYC_Slave *slave,
+                            const char *method_name,
+                            GNUNET_PSYC_TransmitNotifyModifier notify_mod,
+                            GNUNET_PSYC_TransmitNotifyData notify_data,
+                            void *notify_cls,
+                            enum GNUNET_PSYC_SlaveTransmitFlags flags);
+
+
+/**
+ * Resume transmission to the master.
+ *
+ * @param th Handle of the request that is being resumed.
+ */
+void
+GNUNET_PSYC_slave_transmit_resume (struct GNUNET_PSYC_SlaveTransmitHandle *th);
+
+
+/**
+ * Abort transmission request to master.
+ *
+ * @param th Handle of the request that is being aborted.
+ */
+void
+GNUNET_PSYC_slave_transmit_cancel (struct GNUNET_PSYC_SlaveTransmitHandle *th);
+
+
+/**
+ * Handle to access PSYC channel operations for both the master and slaves.
+ */
+struct GNUNET_PSYC_Channel;
+
+
+/**
+ * Convert a channel @a master to a @e channel handle to access the @e channel
+ * APIs.
+ *
+ * @param master Channel master handle.
+ * @return Channel handle, valid for as long as @a master is valid.
+ */
+struct GNUNET_PSYC_Channel *
+GNUNET_PSYC_master_get_channel (struct GNUNET_PSYC_Master *master);
+
+
+/**
+ * Convert @a slave to a @e channel handle to access the @e channel APIs.
+ *
+ * @param slave Slave handle.
+ * @return Channel handle, valid for as long as @a slave is valid.
+ */
+struct GNUNET_PSYC_Channel *
+GNUNET_PSYC_slave_get_channel (struct GNUNET_PSYC_Slave *slave);
+
+
+/**
+ * Add a slave to the channel's membership list.
+ *
+ * Note that this will NOT generate any PSYC traffic, it will merely update the
+ * local database to modify how we react to <em>membership test</em> queries.
+ * The channel master still needs to explicitly transmit a @e join message to
+ * notify other channel members and they then also must still call this 
function
+ * in their respective methods handling the @e join message.  This way, how @e
+ * join and @e part operations are exactly implemented is still up to the
+ * application; for example, there might be a @e part_all method to kick out
+ * everyone.
+ *
+ * Note that channel slaves are explicitly trusted to execute such methods
+ * correctly; not doing so correctly will result in either denying other slaves
+ * access or offering access to channel data to non-members.
+ *
+ * @param channel
+ *        Channel handle.
+ * @param slave_pub_key
+ *        Identity of channel slave to add.
+ * @param announced_at
+ *        ID of the message that announced the membership change.
+ * @param effective_since
+ *        Addition of slave is in effect since this message ID.
+ * @param result_cb
+ *        Function to call with the result of the operation.
+ *        The @e result_code argument is #GNUNET_OK on success, or
+ *        #GNUNET_SYSERR on error.  In case of an error, the @e data argument
+ *        can contain an optional error message.
+ * @param cls
+ *        Closure for @a result_cb.
+ */
+void
+GNUNET_PSYC_channel_slave_add (struct GNUNET_PSYC_Channel *channel,
+                               const struct GNUNET_CRYPTO_EcdsaPublicKey 
*slave_pub_key,
+                               uint64_t announced_at,
+                               uint64_t effective_since,
+                               GNUNET_ResultCallback result_cb,
+                               void *cls);
+
+
+/**
+ * Remove a slave from the channel's membership list.
+ *
+ * Note that this will NOT generate any PSYC traffic, it will merely update the
+ * local database to modify how we react to <em>membership test</em> queries.
+ * The channel master still needs to explicitly transmit a @e part message to
+ * notify other channel members and they then also must still call this 
function
+ * in their respective methods handling the @e part message.  This way, how
+ * @e join and @e part operations are exactly implemented is still up to the
+ * application; for example, there might be a @e part_all message to kick out
+ * everyone.
+ *
+ * Note that channel members are explicitly trusted to perform these
+ * operations correctly; not doing so correctly will result in either
+ * denying members access or offering access to channel data to
+ * non-members.
+ *
+ * @param channel
+ *        Channel handle.
+ * @param slave_pub_key
+ *        Identity of channel slave to remove.
+ * @param announced_at
+ *        ID of the message that announced the membership change.
+ * @param result_cb
+ *        Function to call with the result of the operation.
+ *        The @e result_code argument is #GNUNET_OK on success, or
+ *        #GNUNET_SYSERR on error.  In case of an error, the @e data argument
+ *        can contain an optional error message.
+ * @param cls
+ *        Closure for @a result_cb.
+ */
+void
+GNUNET_PSYC_channel_slave_remove (struct GNUNET_PSYC_Channel *channel,
+                                  const struct GNUNET_CRYPTO_EcdsaPublicKey
+                                  *slave_pub_key,
+                                  uint64_t announced_at,
+                                  GNUNET_ResultCallback result_cb,
+                                  void *cls);
+
+
+/**
+ * History request handle.
+ */
+struct GNUNET_PSYC_HistoryRequest;
+
+
+/**
+ * Request to replay a part of the message history of the channel.
+ *
+ * Historic messages (but NOT the state at the time) will be replayed (given to
+ * the normal method handlers) if available and if access is permitted.
+ *
+ * @param channel
+ *        Which channel should be replayed?
+ * @param start_message_id
+ *        Earliest interesting point in history.
+ * @param end_message_id
+ *        Last (inclusive) interesting point in history.
+ * @param method_prefix
+ *        Retrieve only messages with a matching method prefix.
+ * @param flags
+ *        OR'ed enum GNUNET_PSYC_HistoryReplayFlags
+ * @param result_cb
+ *        Function to call when the requested history has been fully replayed.
+ *        Once this function has been called, the client must not call
+ *        GNUNET_PSYC_channel_history_replay_cancel() anymore.
+ * @param cls
+ *        Closure for the callbacks.
+ *
+ * @return Handle to cancel history replay operation.
+ */
+struct GNUNET_PSYC_HistoryRequest *
+GNUNET_PSYC_channel_history_replay (struct GNUNET_PSYC_Channel *channel,
+                                    uint64_t start_message_id,
+                                    uint64_t end_message_id,
+                                    const char *method_prefix,
+                                    uint32_t flags,
+                                    GNUNET_PSYC_MessageCallback message_cb,
+                                    GNUNET_PSYC_MessagePartCallback 
message_part_cb,
+                                    GNUNET_ResultCallback result_cb,
+                                    void *cls);
+
+
+/**
+ * Request to replay the latest messages from the message history of the 
channel.
+ *
+ * Historic messages (but NOT the state at the time) will be replayed (given to
+ * the normal method handlers) if available and if access is permitted.
+ *
+ * @param channel
+ *        Which channel should be replayed?
+ * @param message_limit
+ *        Maximum number of messages to replay.
+ * @param flags
+ *        OR'ed enum GNUNET_PSYC_HistoryReplayFlags
+ * @param finish_cb
+ *        Function to call when the requested history has been fully replayed
+ *        (counting message IDs might not suffice, as some messages might be
+ *        secret and thus the listener would not know the story is finished
+ *        without being told explicitly)o once this function has been called, 
the
+ *        client must not call GNUNET_PSYC_channel_history_replay_cancel() 
anymore.
+ * @param cls
+ *        Closure for the callbacks.
+ *
+ * @return Handle to cancel history replay operation.
+ */
+struct GNUNET_PSYC_HistoryRequest *
+GNUNET_PSYC_channel_history_replay_latest (struct GNUNET_PSYC_Channel *channel,
+                                           uint64_t message_limit,
+                                           const char *method_prefix,
+                                           uint32_t flags,
+                                           GNUNET_PSYC_MessageCallback 
message_cb,
+                                           GNUNET_PSYC_MessagePartCallback 
message_part_cb,
+                                           GNUNET_ResultCallback result_cb,
+                                           void *cls);
+
+
+void
+GNUNET_PSYC_channel_history_replay_cancel (struct GNUNET_PSYC_Channel *channel,
+                                           struct GNUNET_PSYC_HistoryRequest 
*hr);
+
+
+/**
+ * Function called to inform a member about stored state values for a channel.
+ *
+ * If @a full_value_size > value_size then this function is called multiple
+ * times until the whole value arrived.
+ *
+ * @param cls
+ *        Closure.
+ * @param name
+ *        Name of the state variable.
+ *        NULL if there are no more state variables to be returned.
+ * @param value
+ *        Value of the state variable.
+ * @param value_size
+ *        Number of bytes in @a value.
+ * @param full_value_size
+ *        Number of bytes in the full value, including continuations.
+ *        Only set for the first part of a variable,
+ *        in case of a continuation it is 0.
+ */
+typedef void
+(*GNUNET_PSYC_StateVarCallback) (void *cls,
+                                 const struct GNUNET_MessageHeader *mod,
+                                 const char *name,
+                                 const void *value,
+                                 uint32_t value_size,
+                                 uint32_t full_value_size);
+
+
+/**
+ * State request handle.
+ */
+struct GNUNET_PSYC_StateRequest;
+
+
+/**
+ * Retrieve the best matching channel state variable.
+ *
+ * If the requested variable name is not present in the state, the nearest
+ * less-specific name is matched; for example, requesting "_a_b" will match 
"_a"
+ * if "_a_b" does not exist.
+ *
+ * @param channel
+ *        Channel handle.
+ * @param full_name
+ *        Full name of the requested variable.
+ *        The actual variable returned might have a shorter name.
+ * @param var_cb
+ *        Function called once when a matching state variable is found.
+ *        Not called if there's no matching state variable.
+ * @param result_cb
+ *        Function called after the operation finished.
+ *        (i.e. all state variables have been returned via @a state_cb)
+ * @param cls
+ *        Closure for the callbacks.
+ */
+struct GNUNET_PSYC_StateRequest *
+GNUNET_PSYC_channel_state_get (struct GNUNET_PSYC_Channel *channel,
+                               const char *full_name,
+                               GNUNET_PSYC_StateVarCallback var_cb,
+                               GNUNET_ResultCallback result_cb,
+                               void *cls);
+
+
+/**
+ * Return all channel state variables whose name matches a given prefix.
+ *
+ * A name matches if it starts with the given @a name_prefix, thus requesting
+ * the empty prefix ("") will match all values; requesting "_a_b" will also
+ * return values stored under "_a_b_c".
+ *
+ * The @a state_cb is invoked on all matching state variables asynchronously, 
as
+ * the state is stored in and retrieved from the PSYCstore,
+ *
+ * @param channel
+ *        Channel handle.
+ * @param name_prefix
+ *        Prefix of the state variable name to match.
+ * @param var_cb
+ *        Function called once when a matching state variable is found.
+ *        Not called if there's no matching state variable.
+ * @param result_cb
+ *        Function called after the operation finished.
+ *        (i.e. all state variables have been returned via @a state_cb)
+ * @param cls
+ *        Closure for the callbacks.
+ */
+struct GNUNET_PSYC_StateRequest *
+GNUNET_PSYC_channel_state_get_prefix (struct GNUNET_PSYC_Channel *channel,
+                                      const char *name_prefix,
+                                      GNUNET_PSYC_StateVarCallback var_cb,
+                                      GNUNET_ResultCallback result_cb,
+                                      void *cls);
+
+/**
+ * Cancel a state request operation.
+ *
+ * @param sr
+ *        Handle for the operation to cancel.
+ */
+void
+GNUNET_PSYC_channel_state_get_cancel (struct GNUNET_PSYC_StateRequest *sr);
+
+
+
+#if 0                           /* keep Emacsens' auto-indent happy */
+{
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+/* ifndef GNUNET_PSYC_SERVICE_H */
+#endif
+
+/** @} */  /* end of group */
diff --git a/src/include/gnunet_psyc_slicer.h b/src/include/gnunet_psyc_slicer.h
new file mode 100644
index 0000000..87f66d7
--- /dev/null
+++ b/src/include/gnunet_psyc_slicer.h
@@ -0,0 +1,378 @@
+/*
+     This file is part of GNUnet.
+     Copyright (C) 2013 GNUnet e.V.
+
+     GNUnet is free software: you can redistribute it and/or modify it
+     under the terms of the GNU Affero General Public License as published
+     by the Free Software Foundation, either version 3 of the License,
+     or (at your option) any later version.
+
+     GNUnet 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
+     Affero General Public License for more details.
+    
+     You should have received a copy of the GNU Affero General Public License
+     along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+     SPDX-License-Identifier: AGPL3.0-or-later
+*/
+
+/**
+ * @author Gabor X Toth
+ * @author Christian Grothoff
+ *
+ * @file
+ * PSYC Slicer library
+ *
+ * @defgroup psyc-util-slicer  PSYC Utilities library: Slicer
+ * Try-and-slice processing of PSYC method names and environment.
+ * @{
+ */
+
+#ifndef GNUNET_PSYC_SLICER_H
+#define GNUNET_PSYC_SLICER_H
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#if 0                           /* keep Emacsens' auto-indent happy */
+}
+#endif
+#endif
+
+#include "gnunet_util_lib.h"
+
+
+/**
+ * Handle to an implementation of try-and-slice.
+ */
+struct GNUNET_PSYC_Slicer;
+
+
+/**
+ * Function called upon receiving a message indicating a call to a @e method.
+ *
+ * This function is called one or more times for each message until all data
+ * fragments arrive from the network.
+ *
+ * @param cls
+ *        Closure.
+ * @param msg
+ *        Message part, as it arrived from the network.
+ * @param message_id
+ *        Message counter, monotonically increasing from 1.
+ * @param flags
+ *        OR'ed GNUNET_PSYC_MessageFlags
+ * @param fragment_offset
+ *        Multicast message fragment offset.
+ * @param tmit_flags
+ *        OR'ed GNUNET_PSYC_MasterTransmitFlags
+ * @param nym
+ *        The sender of the message.
+ *        Can be NULL if the message is not connected to a pseudonym.
+ * @param method_name
+ *        Original method name from PSYC.
+ *        May be more specific than the registered method name due to
+ *        try-and-slice matching.
+ */
+typedef void
+(*GNUNET_PSYC_MethodCallback) (void *cls,
+                               const struct GNUNET_PSYC_MessageHeader *msg,
+                               const struct GNUNET_PSYC_MessageMethod *meth,
+                               uint64_t message_id,
+                               const char *method_name);
+
+
+/**
+ * Function called upon receiving a modifier of a message.
+ *
+ * @param cls
+ *        Closure.
+ * @param message_id
+ *        Message ID this data fragment belongs to.
+ * @param flags
+ *        OR'ed GNUNET_PSYC_MessageFlags
+ * @param fragment_offset
+ *        Multicast message fragment offset.
+ * @param msg
+ *        Message part, as it arrived from the network.
+ * @param oper
+ *        Operation to perform.
+ *        0 in case of a modifier continuation.
+ * @param name
+ *        Name of the modifier.
+ *        NULL in case of a modifier continuation.
+ * @param value
+ *        Value of the modifier.
+ * @param value_size
+ *        Size of @value.
+ */
+typedef void
+(*GNUNET_PSYC_ModifierCallback) (void *cls,
+                                 const struct GNUNET_PSYC_MessageHeader *msg,
+                                 const struct GNUNET_MessageHeader *pmsg,
+                                 uint64_t message_id,
+                                 enum GNUNET_PSYC_Operator oper,
+                                 const char *name,
+                                 const void *value,
+                                 uint16_t value_size,
+                                 uint16_t full_value_size);
+
+
+/**
+ * Function called upon receiving a data fragment of a message.
+ *
+ * @param cls
+ *        Closure.
+ * @param msg
+ *        Message part, as it arrived from the network.
+ * @param message_id
+ *        Message ID this data fragment belongs to.
+ * @param flags
+ *        OR'ed GNUNET_PSYC_MessageFlags
+ * @param fragment_offset
+ *        Multicast message fragment offset.
+ * @param data
+ *        Data stream given to the method.
+ * @param data_size
+ *        Number of bytes in @a data.
+ * @param end
+ *        End of message?
+ *        #GNUNET_NO     if there are further fragments,
+ *        #GNUNET_YES    if this is the last fragment,
+ *        #GNUNET_SYSERR indicates the message was cancelled by the sender.
+ */
+typedef void
+(*GNUNET_PSYC_DataCallback) (void *cls,
+                             const struct GNUNET_PSYC_MessageHeader *msg,
+                             const struct GNUNET_MessageHeader *pmsg,
+                             uint64_t message_id,
+                             const void *data,
+                             uint16_t data_size);
+
+
+/**
+ * End of message.
+ *
+ * @param cls
+ *        Closure.
+ * @param msg
+ *        Message part, as it arrived from the network.
+ * @param message_id
+ *        Message ID this data fragment belongs to.
+ * @param flags
+ *        OR'ed GNUNET_PSYC_MessageFlags
+ * @param fragment_offset
+ *        Multicast message fragment offset.
+ * @param cancelled
+ *        #GNUNET_YES if the message was cancelled,
+ *        #GNUNET_NO  if the message is complete.
+ */
+typedef void
+(*GNUNET_PSYC_EndOfMessageCallback) (void *cls,
+                                     const struct GNUNET_PSYC_MessageHeader 
*msg,
+                                     const struct GNUNET_MessageHeader *pmsg,
+                                     uint64_t message_id,
+                                     uint8_t is_cancelled);
+
+
+/**
+ * Create a try-and-slice instance.
+ *
+ * A slicer processes incoming messages and notifies callbacks about matching
+ * methods or modifiers encountered.
+ *
+ * @return A new try-and-slice construct.
+ */
+struct GNUNET_PSYC_Slicer *
+GNUNET_PSYC_slicer_create (void);
+
+
+/**
+ * Add a method to the try-and-slice instance.
+ *
+ * The callbacks are called for messages with a matching @a method_name prefix.
+ *
+ * @param slicer
+ *        The try-and-slice instance to extend.
+ * @param method_name
+ *        Name of the given method, use empty string to match all.
+ * @param method_cb
+ *        Method handler invoked upon a matching message.
+ * @param modifier_cb
+ *        Modifier handler, invoked after @a method_cb
+ *        for each modifier in the message.
+ * @param data_cb
+ *        Data handler, invoked after @a modifier_cb for each data fragment.
+ * @param eom_cb
+ *        Invoked upon reaching the end of a matching message.
+ * @param cls
+ *        Closure for the callbacks.
+ */
+void
+GNUNET_PSYC_slicer_method_add (struct GNUNET_PSYC_Slicer *slicer,
+                               const char *method_name,
+                               GNUNET_PSYC_MessageCallback msg_cb,
+                               GNUNET_PSYC_MethodCallback method_cb,
+                               GNUNET_PSYC_ModifierCallback modifier_cb,
+                               GNUNET_PSYC_DataCallback data_cb,
+                               GNUNET_PSYC_EndOfMessageCallback eom_cb,
+                               void *cls);
+
+/**
+ * Remove a registered method from the try-and-slice instance.
+ *
+ * Removes one matching handler registered with the given
+ * @a method_name and callbacks.
+ *
+ * @param slicer
+ *        The try-and-slice instance.
+ * @param method_name
+ *        Name of the method to remove.
+ * @param method_cb
+ *        Only remove matching method handler, or NULL.
+ * @param modifier_cb
+ *        Only remove matching modifier handler, or NULL.
+ * @param data_cb
+ *        Only remove matching data handler, or NULL.
+ * @param eom_cb
+ *        Only remove matching End of Message handler, or NULL.
+ *
+ * @return #GNUNET_OK if a method handler was removed,
+ *         #GNUNET_NO if no handler matched the given method name and 
callbacks.
+ */
+int
+GNUNET_PSYC_slicer_method_remove (struct GNUNET_PSYC_Slicer *slicer,
+                                  const char *method_name,
+                                  GNUNET_PSYC_MessageCallback msg_cb,
+                                  GNUNET_PSYC_MethodCallback method_cb,
+                                  GNUNET_PSYC_ModifierCallback modifier_cb,
+                                  GNUNET_PSYC_DataCallback data_cb,
+                                  GNUNET_PSYC_EndOfMessageCallback eom_cb);
+
+
+/**
+ * Watch a place for changed objects.
+ *
+ * @param slicer
+ *        The try-and-slice instance.
+ * @param object_filter
+ *        Object prefix to match.
+ * @param modifier_cb
+ *        Function to call when encountering a state modifier.
+ * @param cls
+ *        Closure for callback.
+ */
+void
+GNUNET_PSYC_slicer_modifier_add (struct GNUNET_PSYC_Slicer *slicer,
+                                 const char *object_filter,
+                                 GNUNET_PSYC_ModifierCallback modifier_cb,
+                                 void *cls);
+
+
+/**
+ * Remove a registered modifier from the try-and-slice instance.
+ *
+ * Removes one matching handler registered with the given
+ * @a object_filter and callback.
+ *
+ * @param slicer
+ *        The try-and-slice instance.
+ * @param object_filter
+ *        Object prefix to match.
+ * @param modifier_cb
+ *        Function to call when encountering a state modifier changes.
+ */
+int
+GNUNET_PSYC_slicer_modifier_remove (struct GNUNET_PSYC_Slicer *slicer,
+                                    const char *object_filter,
+                                    GNUNET_PSYC_ModifierCallback modifier_cb);
+
+
+/**
+ * Process an incoming message and call matching handlers.
+ *
+ * @param slicer
+ *        The slicer to use.
+ * @param msg
+ *        The message as it arrived from the network.
+ */
+void
+GNUNET_PSYC_slicer_message (struct GNUNET_PSYC_Slicer *slicer,
+                            const struct GNUNET_PSYC_MessageHeader *msg);
+
+
+/**
+ * Process an incoming message part and call matching handlers.
+ *
+ * @param slicer
+ *        The slicer to use.
+ * @param message_id
+ *        ID of the message.
+ * @param flags
+ *        Flags for the message.
+ *        @see enum GNUNET_PSYC_MessageFlags
+ * @param fragment offset
+ *        Fragment offset of the message.
+ * @param msg
+ *        The message part as it arrived from the network.
+ */
+void
+GNUNET_PSYC_slicer_message_part (struct GNUNET_PSYC_Slicer *slicer,
+                                 const struct GNUNET_PSYC_MessageHeader *msg,
+                                 const struct GNUNET_MessageHeader *pmsg);
+
+
+/**
+ * Remove all registered method handlers.
+ *
+ * @param slicer
+ *        Slicer to clear.
+ */
+void
+GNUNET_PSYC_slicer_method_clear (struct GNUNET_PSYC_Slicer *slicer);
+
+
+/**
+ * Remove all registered modifier handlers.
+ *
+ * @param slicer
+ *        Slicer to clear.
+ */
+void
+GNUNET_PSYC_slicer_modifier_clear (struct GNUNET_PSYC_Slicer *slicer);
+
+
+/**
+ * Remove all registered method & modifier handlers.
+ *
+ * @param slicer
+ *        Slicer to clear.
+ */
+void
+GNUNET_PSYC_slicer_clear (struct GNUNET_PSYC_Slicer *slicer);
+
+
+/**
+ * Destroy a given try-and-slice instance.
+ *
+ * @param slicer
+ *        Slicer to destroy
+ */
+void
+GNUNET_PSYC_slicer_destroy (struct GNUNET_PSYC_Slicer *slicer);
+
+
+#if 0                           /* keep Emacsens' auto-indent happy */
+{
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+/* ifndef GNUNET_PSYC_SLICER_H */
+#endif
+
+/** @} */  /* end of group */
diff --git a/src/include/gnunet_psyc_util_lib.h 
b/src/include/gnunet_psyc_util_lib.h
new file mode 100644
index 0000000..57eec65
--- /dev/null
+++ b/src/include/gnunet_psyc_util_lib.h
@@ -0,0 +1,53 @@
+/*
+     This file is part of GNUnet.
+     Copyright (C) 2012, 2013 GNUnet e.V.
+
+     GNUnet is free software: you can redistribute it and/or modify it
+     under the terms of the GNU Affero General Public License as published
+     by the Free Software Foundation, either version 3 of the License,
+     or (at your option) any later version.
+
+     GNUnet 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
+     Affero General Public License for more details.
+    
+     You should have received a copy of the GNU Affero General Public License
+     along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+     SPDX-License-Identifier: AGPL3.0-or-later
+*/
+
+/**
+ * @author Gabor X Toth
+ *
+ * @file
+ * PSYC utilities: messages, environment, slicer
+ */
+
+#ifndef GNUNET_PSYC_UTIL_LIB_H
+#define GNUNET_PSYC_UTIL_LIB_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#if 0                           /* keep Emacsens' auto-indent happy */
+}
+#endif
+#endif
+
+
+#include "gnunet_psyc_env.h"
+#include "gnunet_psyc_message.h"
+#include "gnunet_psyc_slicer.h"
+
+
+#if 0                           /* keep Emacsens' auto-indent happy */
+{
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+/* ifndef GNUNET_PSYC_UTIL_LIB_H */
+#endif
diff --git a/src/include/gnunet_psycstore_plugin.h 
b/src/include/gnunet_psycstore_plugin.h
new file mode 100644
index 0000000..fac549f
--- /dev/null
+++ b/src/include/gnunet_psycstore_plugin.h
@@ -0,0 +1,383 @@
+/*
+     This file is part of GNUnet
+     Copyright (C) 2013 GNUnet e.V.
+
+     GNUnet is free software: you can redistribute it and/or modify it
+     under the terms of the GNU Affero General Public License as published
+     by the Free Software Foundation, either version 3 of the License,
+     or (at your option) any later version.
+
+     GNUnet 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
+     Affero General Public License for more details.
+    
+     You should have received a copy of the GNU Affero General Public License
+     along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+     SPDX-License-Identifier: AGPL3.0-or-later
+*/
+
+/**
+ * @author Gabor X Toth
+ *
+ * @file
+ * Plugin API for the PSYCstore database backend
+ *
+ * @defgroup psycstore-plugin  PSYC Store plugin API
+ * Plugin API for the PSYC Store database backend
+ * @{
+ */
+#ifndef GNUNET_PSYCSTORE_PLUGIN_H
+#define GNUNET_PSYCSTORE_PLUGIN_H
+
+#include "gnunet_util_lib.h"
+#include "gnunet_psycstore_service.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#if 0                           /* keep Emacsens' auto-indent happy */
+}
+#endif
+#endif
+
+
+/**
+ * Struct returned by the initialization function of the plugin.
+ */
+struct GNUNET_PSYCSTORE_PluginFunctions
+{
+
+  /**
+   * Closure to pass to all plugin functions.
+   */
+  void *cls;
+
+  /**
+   * Store join/leave events for a PSYC channel in order to be able to answer
+   * membership test queries later.
+   *
+   * @see GNUNET_PSYCSTORE_membership_store()
+   *
+   * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+   */
+  int
+  (*membership_store) (void *cls,
+                       const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+                       const struct GNUNET_CRYPTO_EcdsaPublicKey *slave_key,
+                       int did_join,
+                       uint64_t announced_at,
+                       uint64_t effective_since,
+                       uint64_t group_generation);
+
+  /**
+   * Test if a member was admitted to the channel at the given message ID.
+   *
+   * @see GNUNET_PSYCSTORE_membership_test()
+   *
+   * @return #GNUNET_YES if the member was admitted, #GNUNET_NO if not,
+   *         #GNUNET_SYSERR if there was en error.
+   */
+  int
+  (*membership_test) (void *cls,
+                      const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+                      const struct GNUNET_CRYPTO_EcdsaPublicKey *slave_key,
+                      uint64_t message_id);
+
+  /**
+   * Store a message fragment sent to a channel.
+   *
+   * @see GNUNET_PSYCSTORE_fragment_store()
+   *
+   * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+   */
+  int
+  (*fragment_store) (void *cls,
+                     const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+                     const struct GNUNET_MULTICAST_MessageHeader *message,
+                     uint32_t psycstore_flags);
+
+  /**
+   * Set additional flags for a given message.
+   *
+   * They are OR'd with any existing flags set.
+   *
+   * @param cls Closure.
+   * @param channel_key Public key of the channel.
+   * @param message_id ID of the message.
+   * @param psycstore_flags OR'd GNUNET_PSYCSTORE_MessageFlags.
+   *
+   * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+   */
+  int
+  (*message_add_flags) (void *cls,
+                        const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+                        uint64_t message_id,
+                        uint32_t psycstore_flags);
+
+  /**
+   * Retrieve a message fragment range by fragment ID.
+   *
+   * @see GNUNET_PSYCSTORE_fragment_get()
+   *
+   * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+   */
+  int
+  (*fragment_get) (void *cls,
+                   const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+                   uint64_t first_fragment_id,
+                   uint64_t last_fragment_id,
+                   uint64_t *returned_fragments,
+                   GNUNET_PSYCSTORE_FragmentCallback cb,
+                   void *cb_cls);
+
+  /**
+   * Retrieve latest message fragments.
+   *
+   * @see GNUNET_PSYCSTORE_fragment_get()
+   *
+   * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+   */
+  int
+  (*fragment_get_latest) (void *cls,
+                          const struct GNUNET_CRYPTO_EddsaPublicKey 
*channel_key,
+                          uint64_t fragment_limit,
+                          uint64_t *returned_fragments,
+                          GNUNET_PSYCSTORE_FragmentCallback cb,
+                          void *cb_cls);
+
+  /**
+   * Retrieve all fragments of a message ID range.
+   *
+   * @see GNUNET_PSYCSTORE_message_get()
+   *
+   * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+   */
+  int
+  (*message_get) (void *cls,
+                  const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+                  uint64_t first_fragment_id,
+                  uint64_t last_fragment_id,
+                  uint64_t fragment_limit,
+                  uint64_t *returned_fragments,
+                  GNUNET_PSYCSTORE_FragmentCallback cb,
+                  void *cb_cls);
+
+  /**
+   * Retrieve all fragments of the latest messages.
+   *
+   * @see GNUNET_PSYCSTORE_message_get()
+   *
+   * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+   */
+  int
+  (*message_get_latest) (void *cls,
+                         const struct GNUNET_CRYPTO_EddsaPublicKey 
*channel_key,
+                         uint64_t fragment_limit,
+                         uint64_t *returned_fragments,
+                         GNUNET_PSYCSTORE_FragmentCallback cb,
+                         void *cb_cls);
+
+  /**
+   * Retrieve a fragment of message specified by its message ID and fragment
+   * offset.
+   *
+   * @see GNUNET_PSYCSTORE_message_get_fragment()
+   *
+   * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+   */
+  int
+  (*message_get_fragment) (void *cls,
+                           const struct GNUNET_CRYPTO_EddsaPublicKey 
*channel_key,
+                           uint64_t message_id,
+                           uint64_t fragment_offset,
+                           GNUNET_PSYCSTORE_FragmentCallback cb,
+                           void *cb_cls);
+
+  /**
+   * Retrieve the max. values of message counters for a channel.
+   *
+   * @see GNUNET_PSYCSTORE_counters_get()
+   *
+   * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+   */
+  int
+  (*counters_message_get) (void *cls,
+                           const struct GNUNET_CRYPTO_EddsaPublicKey 
*channel_key,
+                           uint64_t *max_fragment_id,
+                           uint64_t *max_message_id,
+                           uint64_t *max_group_generation);
+
+  /**
+   * Retrieve the max. values of state counters for a channel.
+   *
+   * @see GNUNET_PSYCSTORE_counters_get()
+   *
+   * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+   */
+  int
+  (*counters_state_get) (void *cls,
+                         const struct GNUNET_CRYPTO_EddsaPublicKey 
*channel_key,
+                         uint64_t *max_state_message_id);
+
+
+  /**
+   * Begin modifying current state.
+   *
+   * @see GNUNET_PSYCSTORE_state_modify()
+   *
+   * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+   */
+  int
+  (*state_modify_begin) (void *cls,
+                         const struct GNUNET_CRYPTO_EddsaPublicKey 
*channel_key,
+                         uint64_t message_id, uint64_t state_delta);
+
+  /**
+   * Set the current value of a state variable.
+   *
+   * The state modification process is started with state_modify_begin(),
+   * which is followed by one or more calls to this function,
+   * and finished with state_modify_end().
+   *
+   * @see GNUNET_PSYCSTORE_state_modify()
+   *
+   * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+   */
+  int
+  (*state_modify_op) (void *cls,
+                      const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+                      enum GNUNET_PSYC_Operator op,
+                      const char *name, const void *value, size_t value_size);
+
+
+  /**
+   * End modifying current state.
+   *
+   * @see GNUNET_PSYCSTORE_state_modify()
+   *
+   * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+   */
+  int
+  (*state_modify_end) (void *cls,
+                       const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+                       uint64_t message_id);
+
+
+  /**
+   * Begin synchronizing state.
+   *
+   * @see GNUNET_PSYCSTORE_state_sync()
+   *
+   * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+   */
+  int
+  (*state_sync_begin) (void *cls,
+                         const struct GNUNET_CRYPTO_EddsaPublicKey 
*channel_key);
+
+  /**
+   * Assign value of a state variable while synchronizing state.
+   *
+   * The state synchronization process is started with state_sync_begin(),
+   * which is followed by one or more calls to this function,
+   * and finished using state_sync_end().
+   *
+   * @see GNUNET_PSYCSTORE_state_sync()
+   *
+   * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+   */
+  int
+  (*state_sync_assign) (void *cls,
+                        const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+                        const char *name, const void *value, size_t 
value_size);
+
+
+  /**
+   * End synchronizing state.
+   *
+   * @see GNUNET_PSYCSTORE_state_sync()
+   *
+   * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+   */
+  int
+  (*state_sync_end) (void *cls,
+                     const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+                     uint64_t max_state_message_id,
+                     uint64_t state_hash_message_id);
+
+
+  /**
+   * Reset the state of a channel.
+   *
+   * Delete all state variables stored for the given channel.
+   *
+   * @see GNUNET_PSYCSTORE_state_reset()
+   *
+   * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+   */
+  int
+  (*state_reset) (void *cls,
+                  const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key);
+
+  /**
+   * Update signed state values from the current ones.
+   *
+   * Sets value_signed = value_current for each variable for the given channel.
+   */
+  int
+  (*state_update_signed) (void *cls,
+                          const struct GNUNET_CRYPTO_EddsaPublicKey 
*channel_key);
+
+
+  /**
+   * Retrieve a state variable by name (exact match).
+   *
+   * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+   */
+  int
+  (*state_get) (void *cls,
+                const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+                const char *name,
+                GNUNET_PSYCSTORE_StateCallback cb,
+                void *cb_cls);
+
+  /**
+   * Retrieve all state variables for a channel with the given prefix.
+   *
+   * @see GNUNET_PSYCSTORE_state_get_prefix()
+   *
+   * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+   */
+  int
+  (*state_get_prefix) (void *cls,
+                       const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+                       const char *name,
+                       GNUNET_PSYCSTORE_StateCallback cb,
+                       void *cb_cls);
+
+
+  /**
+   * Retrieve all signed state variables for a channel.
+   *
+   * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+   */
+  int
+  (*state_get_signed) (void *cls,
+                       const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+                       GNUNET_PSYCSTORE_StateCallback cb,
+                       void *cb_cls);
+
+};
+
+
+#if 0                           /* keep Emacsens' auto-indent happy */
+{
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
+/** @} */  /* end of group */
diff --git a/src/include/gnunet_psycstore_service.h 
b/src/include/gnunet_psycstore_service.h
new file mode 100644
index 0000000..92516f4
--- /dev/null
+++ b/src/include/gnunet_psycstore_service.h
@@ -0,0 +1,701 @@
+/*
+     This file is part of GNUnet.
+     Copyright (C) 2013 GNUnet e.V.
+
+     GNUnet is free software: you can redistribute it and/or modify it
+     under the terms of the GNU Affero General Public License as published
+     by the Free Software Foundation, either version 3 of the License,
+     or (at your option) any later version.
+
+     GNUnet 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
+     Affero General Public License for more details.
+    
+     You should have received a copy of the GNU Affero General Public License
+     along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+     SPDX-License-Identifier: AGPL3.0-or-later
+*/
+
+/**
+ * @author Gabor X Toth
+ * @author Christian Grothoff
+ *
+ * @file
+ * PSYCstore service; implements persistent storage for the PSYC service
+ *
+ * @defgroup psycstore  PSYC Store service
+ * Persistent storage for the PSYC service.
+ * @{
+ */
+#ifndef GNUNET_PSYCSTORE_SERVICE_H
+#define GNUNET_PSYCSTORE_SERVICE_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#if 0                           /* keep Emacsens' auto-indent happy */
+}
+#endif
+#endif
+
+#include "gnunet_util_lib.h"
+#include "gnunet_psyc_util_lib.h"
+#include "gnunet_multicast_service.h"
+#include "gnunet_psyc_service.h"
+
+/**
+ * Version number of GNUnet PSYCstore API.
+ */
+#define GNUNET_PSYCSTORE_VERSION 0x00000000
+
+/**
+ * Membership test failed.
+ */
+#define GNUNET_PSYCSTORE_MEMBERSHIP_TEST_FAILED -2
+
+/**
+ * Flags for stored messages.
+ */
+enum GNUNET_PSYCSTORE_MessageFlags
+{
+  /**
+   * The message contains state modifiers.
+   */
+  GNUNET_PSYCSTORE_MESSAGE_STATE = 1 << 0,
+
+  /**
+   * The state modifiers have been applied to the state store.
+   */
+  GNUNET_PSYCSTORE_MESSAGE_STATE_APPLIED = 1 << 1,
+
+  /**
+   * The message contains a state hash.
+   */
+  GNUNET_PSYCSTORE_MESSAGE_STATE_HASH = 1 << 2
+};
+
+
+/**
+ * Handle for a PSYCstore
+ */
+struct GNUNET_PSYCSTORE_Handle;
+
+
+/**
+ * Connect to the PSYCstore service.
+ *
+ * @param cfg Configuration to use.
+ *
+ * @return Handle for the connecton.
+ */
+struct GNUNET_PSYCSTORE_Handle *
+GNUNET_PSYCSTORE_connect (const struct GNUNET_CONFIGURATION_Handle *cfg);
+
+
+/**
+ * Disconnect from the PSYCstore service.
+ *
+ * @param h Handle for the connection.
+ */
+void
+GNUNET_PSYCSTORE_disconnect (struct GNUNET_PSYCSTORE_Handle *h);
+
+
+/**
+ * Handle for an operation on the PSYCSTORE (useful to cancel the operation).
+ */
+struct GNUNET_PSYCSTORE_OperationHandle;
+
+
+/**
+ * Function called with the result of an asynchronous operation.
+ *
+ * @param cls
+ *        Closure.
+ * @param result
+ *        Result of the operation.
+ * @param err_msg
+ *        Error message, or NULL if there's no error.
+ * @param err_msg_size
+ *        Size of @a err_msg
+ */
+typedef void
+(*GNUNET_PSYCSTORE_ResultCallback) (void *cls,
+                                    int64_t result,
+                                    const char *err_msg,
+                                    uint16_t err_msg_size);
+
+
+/**
+ * Store join/leave events for a PSYC channel in order to be able to answer
+ * membership test queries later.
+ *
+ * @param h
+ *        Handle for the PSYCstore.
+ * @param channel_key
+ *        The channel where the event happened.
+ * @param slave_key
+ *        Public key of joining/leaving slave.
+ * @param did_join
+ *        #GNUNET_YES on join, #GNUNET_NO on part.
+ * @param announced_at
+ *        ID of the message that announced the membership change.
+ * @param effective_since
+ *        Message ID this membership change is in effect since.
+ *        For joins it is <= announced_at, for parts it is always 0.
+ * @param group_generation
+ *        In case of a part, the last group generation the slave has access to.
+ *        It has relevance when a larger message have fragments with different
+ *        group generations.
+ * @param result_cb
+ *        Callback to call with the result of the storage operation.
+ * @param cls
+ *        Closure for the callback.
+ *
+ * @return Operation handle that can be used to cancel the operation.
+ */
+struct GNUNET_PSYCSTORE_OperationHandle *
+GNUNET_PSYCSTORE_membership_store (struct GNUNET_PSYCSTORE_Handle *h,
+                                   const struct GNUNET_CRYPTO_EddsaPublicKey 
*channel_key,
+                                   const struct GNUNET_CRYPTO_EcdsaPublicKey 
*slave_key,
+                                   int did_join,
+                                   uint64_t announced_at,
+                                   uint64_t effective_since,
+                                   uint64_t group_generation,
+                                   GNUNET_PSYCSTORE_ResultCallback result_cb,
+                                   void *cls);
+
+
+/**
+ * Test if a member was admitted to the channel at the given message ID.
+ *
+ * This is useful when relaying and replaying messages to check if a particular
+ * slave has access to the message fragment with a given group generation.  It
+ * is also used when handling join requests to determine whether the slave is
+ * currently admitted to the channel.
+ *
+ * @param h
+ *        Handle for the PSYCstore.
+ * @param channel_key
+ *        The channel we are interested in.
+ * @param slave_key
+ *        Public key of slave whose membership to check.
+ * @param message_id
+ *        Message ID for which to do the membership test.
+ * @param group_generation
+ *        Group generation of the fragment of the message to test.
+ *        It has relevance if the message consists of multiple fragments with
+ *        different group generations.
+ * @param result_cb
+ *        Callback to call with the test result.
+ * @param cls
+ *        Closure for the callback.
+ *
+ * @return Operation handle that can be used to cancel the operation.
+ */
+struct GNUNET_PSYCSTORE_OperationHandle *
+GNUNET_PSYCSTORE_membership_test (struct GNUNET_PSYCSTORE_Handle *h,
+                                  const struct GNUNET_CRYPTO_EddsaPublicKey 
*channel_key,
+                                  const struct GNUNET_CRYPTO_EcdsaPublicKey 
*slave_key,
+                                  uint64_t message_id,
+                                  uint64_t group_generation,
+                                  GNUNET_PSYCSTORE_ResultCallback result_cb,
+                                  void *cls);
+
+
+/**
+ * Store a message fragment sent to a channel.
+ *
+ * @param h Handle for the PSYCstore.
+ * @param channel_key The channel the message belongs to.
+ * @param msg Message to store.
+ * @param psycstore_flags Flags indicating whether the PSYC message contains
+ *        state modifiers.
+ * @param result_cb Callback to call with the result of the operation.
+ * @param cls Closure for the callback.
+ *
+ * @return Handle that can be used to cancel the operation.
+ */
+struct GNUNET_PSYCSTORE_OperationHandle *
+GNUNET_PSYCSTORE_fragment_store (struct GNUNET_PSYCSTORE_Handle *h,
+                                 const struct GNUNET_CRYPTO_EddsaPublicKey 
*channel_key,
+                                 const struct GNUNET_MULTICAST_MessageHeader 
*msg,
+                                 enum GNUNET_PSYCSTORE_MessageFlags 
psycstore_flags,
+                                 GNUNET_PSYCSTORE_ResultCallback result_cb,
+                                 void *cls);
+
+
+/**
+ * Function called with one message fragment, as the result of a
+ * GNUNET_PSYCSTORE_fragment_get() or GNUNET_PSYCSTORE_message_get() call.
+ *
+ * @param cls Closure.
+ * @param message The retrieved message fragment.  A NULL value indicates that
+ *        there are no more results to be returned.
+ * @param psycstore_flags Flags stored with the message.
+ *
+ * @return #GNUNET_NO to stop calling this callback with further fragments,
+ *         #GNUNET_YES to continue.
+ */
+typedef int
+(*GNUNET_PSYCSTORE_FragmentCallback) (void *cls,
+                                      struct GNUNET_MULTICAST_MessageHeader 
*message,
+                                      enum GNUNET_PSYCSTORE_MessageFlags 
psycstore_flags);
+
+
+/**
+ * Retrieve message fragments by fragment ID range.
+ *
+ * @param h
+ *        Handle for the PSYCstore.
+ * @param channel_key
+ *        The channel we are interested in.
+ * @param slave_key
+ *        The slave requesting the fragment.  If not NULL, a membership test is
+ *        performed first and the fragment is only returned if the slave has
+ *        access to it.
+ * @param first_fragment_id
+ *        First fragment ID to retrieve.
+ *        Use 0 to get the latest message fragment.
+ * @param last_fragment_id
+ *        Last consecutive fragment ID to retrieve.
+ *        Use 0 to get the latest message fragment.
+ * @param fragment_cb
+ *        Callback to call with the retrieved fragments.
+ * @param result_cb
+ *        Callback to call with the result of the operation.
+ * @param cls
+ *        Closure for the callbacks.
+ *
+ * @return Handle that can be used to cancel the operation.
+ */
+struct GNUNET_PSYCSTORE_OperationHandle *
+GNUNET_PSYCSTORE_fragment_get (struct GNUNET_PSYCSTORE_Handle *h,
+                               const struct GNUNET_CRYPTO_EddsaPublicKey 
*channel_key,
+                               const struct GNUNET_CRYPTO_EcdsaPublicKey 
*slave_key,
+                               uint64_t first_message_id,
+                               uint64_t last_message_id,
+                               GNUNET_PSYCSTORE_FragmentCallback fragment_cb,
+                               GNUNET_PSYCSTORE_ResultCallback result_cb,
+                               void *cls);
+
+
+/**
+ * Retrieve latest message fragments.
+ *
+ * @param h
+ *        Handle for the PSYCstore.
+ * @param channel_key
+ *        The channel we are interested in.
+ * @param slave_key
+ *        The slave requesting the fragment.  If not NULL, a membership test is
+ *        performed first and the fragment is only returned if the slave has
+ *        access to it.
+ * @param first_fragment_id
+ *        First fragment ID to retrieve.
+ *        Use 0 to get the latest message fragment.
+ * @param last_fragment_id
+ *        Last consecutive fragment ID to retrieve.
+ *        Use 0 to get the latest message fragment.
+ * @param fragment_limit
+ *        Maximum number of fragments to retrieve.
+ * @param fragment_cb
+ *        Callback to call with the retrieved fragments.
+ * @param result_cb
+ *        Callback to call with the result of the operation.
+ * @param cls
+ *        Closure for the callbacks.
+ *
+ * @return Handle that can be used to cancel the operation.
+ */
+struct GNUNET_PSYCSTORE_OperationHandle *
+GNUNET_PSYCSTORE_fragment_get_latest (struct GNUNET_PSYCSTORE_Handle *h,
+                                      const struct 
GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+                                      const struct 
GNUNET_CRYPTO_EcdsaPublicKey *slave_key,
+                                      uint64_t fragment_limit,
+                                      GNUNET_PSYCSTORE_FragmentCallback 
fragment_cb,
+                                      GNUNET_PSYCSTORE_ResultCallback 
result_cb,
+                                      void *cls);
+
+
+/**
+ * Retrieve all fragments of messages in a message ID range.
+ *
+ * @param h
+ *        Handle for the PSYCstore.
+ * @param channel_key
+ *        The channel we are interested in.
+ * @param slave_key
+ *        The slave requesting the message.
+ *        If not NULL, a membership test is performed first
+ *        and the message is only returned if the slave has access to it.
+ * @param first_message_id
+ *        First message ID to retrieve.
+ * @param last_message_id
+ *        Last consecutive message ID to retrieve.
+ * @param fragment_limit
+ *        Maximum number of fragments to retrieve.
+ * @param method_prefix
+ *        Retrieve only messages with a matching method prefix.
+ * @param fragment_cb
+ *        Callback to call with the retrieved fragments.
+ * @param result_cb
+ *        Callback to call with the result of the operation.
+ * @param cls
+ *        Closure for the callbacks.
+ *
+ * @return Handle that can be used to cancel the operation.
+ */
+struct GNUNET_PSYCSTORE_OperationHandle *
+GNUNET_PSYCSTORE_message_get (struct GNUNET_PSYCSTORE_Handle *h,
+                              const struct GNUNET_CRYPTO_EddsaPublicKey 
*channel_key,
+                              const struct GNUNET_CRYPTO_EcdsaPublicKey 
*slave_key,
+                              uint64_t first_message_id,
+                              uint64_t last_message_id,
+                              uint64_t fragment_limit,
+                              const char *method_prefix,
+                              GNUNET_PSYCSTORE_FragmentCallback fragment_cb,
+                              GNUNET_PSYCSTORE_ResultCallback result_cb,
+                              void *cls);
+
+
+/**
+ * Retrieve all fragments of the latest messages.
+ *
+ * @param h
+ *        Handle for the PSYCstore.
+ * @param channel_key
+ *        The channel we are interested in.
+ * @param slave_key
+ *        The slave requesting the message.
+ *        If not NULL, a membership test is performed first
+ *        and the message is only returned if the slave has access to it.
+ * @param message_limit
+ *        Maximum number of messages to retrieve.
+ * @param method_prefix
+ *        Retrieve only messages with a matching method prefix.
+ * @param fragment_cb
+ *        Callback to call with the retrieved fragments.
+ * @param result_cb
+ *        Callback to call with the result of the operation.
+ * @param cls
+ *        Closure for the callbacks.
+ *
+ * @return Handle that can be used to cancel the operation.
+ */
+struct GNUNET_PSYCSTORE_OperationHandle *
+GNUNET_PSYCSTORE_message_get_latest (struct GNUNET_PSYCSTORE_Handle *h,
+                                     const struct GNUNET_CRYPTO_EddsaPublicKey 
*channel_key,
+                                     const struct GNUNET_CRYPTO_EcdsaPublicKey 
*slave_key,
+                                     uint64_t message_limit,
+                                     const char *method_prefix,
+                                     GNUNET_PSYCSTORE_FragmentCallback 
fragment_cb,
+                                     GNUNET_PSYCSTORE_ResultCallback result_cb,
+                                     void *cls);
+
+
+/**
+ * Retrieve a fragment of message specified by its message ID and fragment
+ * offset.
+ *
+ * @param h
+ *        Handle for the PSYCstore.
+ * @param channel_key
+ *        The channel we are interested in.
+ * @param slave_key
+ *        The slave requesting the message fragment.  If not NULL, a membership
+ *        test is performed first and the message fragment is only returned
+ *        if the slave has access to it.
+ * @param message_id
+ *        Message ID to retrieve.  Use 0 to get the latest message.
+ * @param fragment_offset
+ *        Offset of the fragment to retrieve.
+ * @param fragment_cb
+ *        Callback to call with the retrieved fragments.
+ * @param result_cb
+ *        Callback to call with the result of the operation.
+ * @param cls
+ *        Closure for the callbacks.
+ *
+ * @return Handle that can be used to cancel the operation.
+ */
+struct GNUNET_PSYCSTORE_OperationHandle *
+GNUNET_PSYCSTORE_message_get_fragment (struct GNUNET_PSYCSTORE_Handle *h,
+                                       const struct 
GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+                                       const struct 
GNUNET_CRYPTO_EcdsaPublicKey *slave_key,
+                                       uint64_t message_id,
+                                       uint64_t fragment_offset,
+                                       GNUNET_PSYCSTORE_FragmentCallback 
fragment_cb,
+                                       GNUNET_PSYCSTORE_ResultCallback 
result_cb,
+                                       void *cls);
+
+
+/**
+ * Callback used to return the latest value of counters for the channel master.
+ *
+ * @see GNUNET_PSYCSTORE_counters_get()
+ *
+ * @param cls Closure.
+ * @param result_code
+ *        Status code for the operation:
+ *        #GNUNET_OK: success, counter values are returned.
+ *        #GNUNET_NO: no message has been sent to the channel yet.
+ *        #GNUNET_SYSERR: an error occurred.
+ * @param max_fragment_id
+ *        Latest message fragment ID, used by multicast.
+ * @param max_message_id
+ *        Latest message ID, used by PSYC.
+ * @param max_group_generation
+ *        Latest group generation, used by PSYC.
+ * @param max_state_message_id
+ *        Latest message ID containing state modifiers that
+ *        was applied to the state store.  Used for the state sync process.
+ */
+typedef void
+(*GNUNET_PSYCSTORE_CountersCallback) (void *cls,
+                                      int result_code,
+                                      uint64_t max_fragment_id,
+                                      uint64_t max_message_id,
+                                      uint64_t max_group_generation,
+                                      uint64_t max_state_message_id);
+
+
+/**
+ * Retrieve latest values of counters for a channel.
+ *
+ * The current value of counters are needed
+ * - when a channel master is restarted, so that it can continue incrementing
+ *   the counters from their last value.
+ * - when a channel slave rejoins and starts the state synchronization process.
+ *
+ * @param h
+ *        Handle for the PSYCstore.
+ * @param channel_key
+ *        Public key that identifies the channel.
+ * @param counters_cb
+ *        Callback to call with the result.
+ * @param cls
+ *        Closure for the @a ccb callback.
+ *
+ * @return Handle that can be used to cancel the operation.
+ */
+struct GNUNET_PSYCSTORE_OperationHandle *
+GNUNET_PSYCSTORE_counters_get (struct GNUNET_PSYCSTORE_Handle *h,
+                               struct GNUNET_CRYPTO_EddsaPublicKey 
*channel_key,
+                               GNUNET_PSYCSTORE_CountersCallback counters_cb,
+                               void *cls);
+
+
+/**
+ * Apply modifiers of a message to the current channel state.
+ *
+ * An error is returned if there are missing messages containing state
+ * operations before the current one.
+ *
+ * @param h
+ *        Handle for the PSYCstore.
+ * @param channel_key
+ *        The channel we are interested in.
+ * @param message_id
+ *        ID of the message that contains the @a modifiers.
+ * @param state_delta
+ *        Value of the @e state_delta PSYC header variable of the message.
+ * @param result_cb
+ *        Callback to call with the result of the operation.
+ * @param cls
+ *        Closure for the @a result_cb callback.
+ *
+ * @return Handle that can be used to cancel the operation.
+ */
+struct GNUNET_PSYCSTORE_OperationHandle *
+GNUNET_PSYCSTORE_state_modify (struct GNUNET_PSYCSTORE_Handle *h,
+                               const struct GNUNET_CRYPTO_EddsaPublicKey 
*channel_key,
+                               uint64_t message_id,
+                               uint64_t state_delta,
+                               GNUNET_PSYCSTORE_ResultCallback result_cb,
+                               void *cls);
+
+
+/**
+ * Store synchronized state.
+ *
+ * @param h
+ *        Handle for the PSYCstore.
+ * @param channel_key
+ *        The channel we are interested in.
+ * @param max_state_message_id
+ *        ID of the last stateful message before @a state_hash_message_id.
+ * @param state_hash_message_id
+ *        ID of the message that contains the state_hash PSYC header variable.
+ * @param modifier_count
+ *        Number of elements in the @a modifiers array.
+ * @param modifiers
+ *        Full state to store.
+ * @param result_cb
+ *        Callback to call with the result of the operation.
+ * @param cls
+ *        Closure for the callback.
+ *
+ * @return Handle that can be used to cancel the operation.
+ */
+struct GNUNET_PSYCSTORE_OperationHandle *
+GNUNET_PSYCSTORE_state_sync (struct GNUNET_PSYCSTORE_Handle *h,
+                             const struct GNUNET_CRYPTO_EddsaPublicKey 
*channel_key,
+                             uint64_t max_state_message_id,
+                             uint64_t state_hash_message_id,
+                             size_t modifier_count,
+                             const struct GNUNET_PSYC_Modifier *modifiers,
+                             GNUNET_PSYCSTORE_ResultCallback result_cb,
+                             void *cls);
+
+
+
+/**
+ * Reset the state of a channel.
+ *
+ * Delete all state variables stored for the given channel.
+ *
+ * @param h
+ *        Handle for the PSYCstore.
+ * @param channel_key
+ *        The channel we are interested in.
+ * @param result_cb
+ *        Callback to call with the result of the operation.
+ * @param cls
+ *        Closure for the callback.
+ *
+ * @return Handle that can be used to cancel the operation.
+ */
+struct GNUNET_PSYCSTORE_OperationHandle *
+GNUNET_PSYCSTORE_state_reset (struct GNUNET_PSYCSTORE_Handle *h,
+                              const struct GNUNET_CRYPTO_EddsaPublicKey
+                              *channel_key,
+                              GNUNET_PSYCSTORE_ResultCallback result_cb,
+                              void *cls);
+
+
+/**
+ * Update signed values of state variables in the state store.
+ *
+ * @param h
+ *        Handle for the PSYCstore.
+ * @param channel_key
+ *        The channel we are interested in.
+ * @param message_id
+ *        Message ID that contained the state @a hash.
+ * @param hash
+ *        Hash of the serialized full state.
+ * @param result_cb
+ *        Callback to call with the result of the operation.
+ * @param cls
+ *        Closure for the callback.
+ *
+ */
+struct GNUNET_PSYCSTORE_OperationHandle *
+GNUNET_PSYCSTORE_state_hash_update (struct GNUNET_PSYCSTORE_Handle *h,
+                                    const struct GNUNET_CRYPTO_EddsaPublicKey 
*channel_key,
+                                    uint64_t message_id,
+                                    const struct GNUNET_HashCode *hash,
+                                    GNUNET_PSYCSTORE_ResultCallback result_cb,
+                                    void *cls);
+
+
+/**
+ * Function called with the value of a state variable.
+ *
+ * @param cls
+ *        Closure.
+ * @param name
+ *        Name of the state variable.  A NULL value indicates that there are 
no more
+ *        state variables to be returned.
+ * @param value
+ *        Value of the state variable.
+ * @param value_size
+ *        Number of bytes in @a value.
+ *
+ * @return #GNUNET_NO to stop calling this callback with further variables,
+ *         #GNUNET_YES to continue.
+ */;
+typedef int
+(*GNUNET_PSYCSTORE_StateCallback) (void *cls, const char *name,
+                                   const void *value, uint32_t value_size);
+
+
+/**
+ * Retrieve the best matching state variable.
+ *
+ * @param h
+ *        Handle for the PSYCstore.
+ * @param channel_key
+ *        The channel we are interested in.
+ * @param name
+ *        Name of variable to match, the returned variable might be less 
specific.
+ * @param state_cb
+ *        Callback to return the matching state variable.
+ * @param result_cb
+ *        Callback to call with the result of the operation.
+ * @param cls
+ *        Closure for the callbacks.
+ *
+ * @return Handle that can be used to cancel the operation.
+ */
+struct GNUNET_PSYCSTORE_OperationHandle *
+GNUNET_PSYCSTORE_state_get (struct GNUNET_PSYCSTORE_Handle *h,
+                            const struct GNUNET_CRYPTO_EddsaPublicKey 
*channel_key,
+                            const char *name,
+                            GNUNET_PSYCSTORE_StateCallback state_cb,
+                            GNUNET_PSYCSTORE_ResultCallback result_cb,
+                            void *cls);
+
+
+/**
+ * Retrieve all state variables for a channel with the given prefix.
+ *
+ * @param h
+ *        Handle for the PSYCstore.
+ * @param channel_key
+ *        The channel we are interested in.
+ * @param name_prefix
+ *        Prefix of state variable names to match.
+ * @param state_cb
+ *        Callback to return matching state variables.
+ * @param result_cb
+ *        Callback to call with the result of the operation.
+ * @param cls
+ *        Closure for the callbacks.
+ *
+ * @return Handle that can be used to cancel the operation.
+ */
+struct GNUNET_PSYCSTORE_OperationHandle *
+GNUNET_PSYCSTORE_state_get_prefix (struct GNUNET_PSYCSTORE_Handle *h,
+                                   const struct GNUNET_CRYPTO_EddsaPublicKey 
*channel_key,
+                                   const char *name_prefix,
+                                   GNUNET_PSYCSTORE_StateCallback state_cb,
+                                   GNUNET_PSYCSTORE_ResultCallback result_cb,
+                                   void *cls);
+
+
+/**
+ * Cancel an operation.
+ *
+ * @param op Handle for the operation to cancel.
+ */
+int
+GNUNET_PSYCSTORE_operation_cancel (struct GNUNET_PSYCSTORE_OperationHandle 
*op);
+
+
+
+
+#if 0                           /* keep Emacsens' auto-indent happy */
+{
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+/* ifndef GNUNET_PSYCSTORE_SERVICE_H */
+#endif
+
+/** @} */  /* end of group */
diff --git a/src/include/gnunet_social_service.h 
b/src/include/gnunet_social_service.h
new file mode 100644
index 0000000..7faa336
--- /dev/null
+++ b/src/include/gnunet_social_service.h
@@ -0,0 +1,1344 @@
+/*
+     This file is part of GNUnet.
+     Copyright (C) 2013 GNUnet e.V.
+
+     GNUnet is free software: you can redistribute it and/or modify it
+     under the terms of the GNU Affero General Public License as published
+     by the Free Software Foundation, either version 3 of the License,
+     or (at your option) any later version.
+
+     GNUnet 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
+     Affero General Public License for more details.
+    
+     You should have received a copy of the GNU Affero General Public License
+     along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+     SPDX-License-Identifier: AGPL3.0-or-later
+*/
+
+/**
+ * @author Gabor X Toth
+ * @author Christian Grothoff
+ *
+ * @file
+ * Social service; implements social interactions through the PSYC service.
+ */
+
+/** @defgroup social Social service
+Social interactions through the PSYC service.
+
+# Overview
+
+The social service provides an API for social interactions based on a 
one-to-many messaging model.
+It manages subscriptions of applications to places, provides messaging 
functionality in places,
+allows access to the local message history and manages the GNS zone of _egos_ 
(user identities).
+
+The service stores private and public keys of subscribed places, as well as 
files received in subscribed places.
+
+# Concepts and terminology
+
+## Ego, Nym
+
+An _ego_ is an identity of a user, a private-public key pair.
+A _nym_ is an identity of another user in the network, identified by its 
public key.
+Each user can have multiple identities.
+
+struct GNUNET_SOCIAL_Ego and struct GNUNET_SOCIAL_Nym represents one of these 
identities.
+
+## Place, Host, Guest
+
+A _place_ is where social interactions happen.  It is owned and created by an 
_ego_.
+Creating a new place happens by an _ego_ entering a new place as a _host_,
+where _guests_ can enter later to receive messages sent to the place.
+
+A place is identified by its public key.
+
+- struct GNUNET_SOCIAL_Host represents a place entered as host,
+- struct GNUNET_SOCIAL_Guest is used for a place entered as guest.
+- A struct GNUNET_SOCIAL_Place can be obtained for both a host and guest place
+  using GNUNET_SOCIAL_host_get_place() and GNUNET_SOCIAL_guest_get_place()
+  and can be used with API functions common to hosts and guests.
+
+## History
+
+Messages sent to places are stored locally by the PSYCstore service, and can 
be queried any time.
+GNUNET_SOCIAL_history_replay_latest() retrieves the latest N messages sent to 
the place,
+while GNUNET_SOCIAL_history_replay() is used to query a given message ID range.
+
+## GNU Name System
+
+The GNU Name System is used for assigning human-readable names to nyms and 
places.
+There's a _GNS zone_ corresponding to each _nym_.
+An _ego_ can publish PKEY and PLACE records in its own zone, pointing to nyms 
and places, respectively.
+
+## Announcement, talk request
+
+The host can _announce_ messages to the place, using 
GNUNET_SOCIAL_host_announce().
+Guests can send _talk_ requests to the host, using GNUNET_SOCIAL_guest_talk().
+The host receives talk requests of guests and can _relay_ them to the place,
+or process it using a message handler function.
+
+# Using the API
+
+## Connecting to the service
+
+A client first establishes an _application connection_ to the service using
+GNUNET_SOCIAL_app_connect() providing its _application ID_, then receives the
+public keys of subscribed places and available egos in response.
+
+## Reconnecting to places
+
+Then the application can reconnect to its subscribed places by establishing
+_place connections_ with GNUNET_SOCIAL_host_enter_reconnect() and
+GNUNET_SOCIAL_guest_enter_reconnect().
+
+## Subscribing to a place
+
+Entering and subscribing a new host or guest place is done using
+GNUNET_SOCIAL_host_enter() and GNUNET_SOCIAL_guest_enter().
+
+## Disconnecting from a place
+
+An application can disconnect from a place while the social service keeps its
+network connection active, using GNUNET_SOCIAL_host_disconnect() and
+GNUNET_SOCIAL_guest_disconnect().
+
+## Leaving a place
+
+To permanently leave a place, see GNUNET_SOCIAL_host_leave() and 
GNUNET_SOCIAL_guest_leave().
+When leaving a place its network connections are closed and all applications 
are unsubscribed from the place.
+
+# Message methods
+
+## _converse
+
+Human conversation in a private or public place.
+
+### Environment
+
+#### _id_reply
+Message ID this message is in reply to.
+
+#### _id_thread
+Thread ID, the first message ID in the thread.
+
+#### _nym_author
+Nym of the author.
+
+FIXME: Are nyms a different data type from egos and person entities?
+Do they have a different format than any other entity address?
+Questions and thoughts on how to fix this in "questions.org"
+
+#### _sig_author
+Signature of the message body and its variables by the author.
+
+### Data
+
+Message body.
+
+## _notice_place
+
+Notification about a place.
+
+TODO: Applications can decide to auto-subscribe to certain places,
+e.g. files under a given size.
+
+### Environment
+
+#### Using GNS
+
+##### _gns_place
+GNS name of the place in a globally unique .zkey zone
+
+FIXME: A custom _gns PSYC data type should be avoidable by parsing
+and interpreting PSYC uniforms appropriately.
+Thoughts on this in "questions.org"
+
+#### Without GNS
+
+##### _key_pub_place
+Public key of place
+
+FIXME: _key_pub can't be the data type for GNUnet-specific cryptographic
+addressing. Questions and thoughts on how to fix this in "questions.org"
+
+##### _peer_origin
+Peer ID of origin
+
+##### _list_peer_relays
+List of peer IDs of relays
+
+## _notice_place_file
+
+Notification about a place hosting a file.
+
+### Environment
+
+The environment of _notice_place above, plus the following:
+
+#### _size_file
+Size of file
+
+#### _type_file
+MIME type of file
+
+#### _name_file
+Name of file
+
+#### _description_file
+Description of file
+
+## _file
+
+Messages with a _file method contain a file,
+which is saved to disk upon reception at the following location:
+$GNUNET_DATA_HOME/social/files/<H(place_pub)>/<H(message_id)>
+
+### Environment
+
+#### _size_file
+Size of file
+
+#### _type_file
+MIME type of file
+
+#### _name_file
+Name of file
+
+#### _description_file
+Description of file
+
address@hidden
+*/
+
+
+#ifndef GNUNET_SOCIAL_SERVICE_H
+#define GNUNET_SOCIAL_SERVICE_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#if 0                           /* keep Emacsens' auto-indent happy */
+}
+#endif
+#endif
+
+#include <stdint.h>
+#include "gnunet_util_lib.h"
+#include "gnunet_psyc_util_lib.h"
+#include "gnunet_identity_service.h"
+#include "gnunet_namestore_service.h"
+#include "gnunet_psyc_service.h"
+
+
+/**
+ * Version number of GNUnet Social API.
+ */
+#define GNUNET_SOCIAL_VERSION 0x00000000
+
+/**
+ * Maximum size of client ID including '\0' terminator.
+ */
+#define GNUNET_SOCIAL_APP_MAX_ID_SIZE 256
+
+enum GNUNET_SOCIAL_MsgProcFlags {
+  GNUNET_SOCIAL_MSG_PROC_NONE = 0,
+  GNUNET_SOCIAL_MSG_PROC_RELAY = 1,
+  GNUNET_SOCIAL_MSG_PROC_SAVE= 2,
+};
+
+/**
+ * Handle for an application.
+ */
+struct GNUNET_SOCIAL_App;
+
+/**
+ * Handle for an ego (own identity)
+ */
+struct GNUNET_SOCIAL_Ego;
+
+/**
+ * Handle for a pseudonym of another user in the network.
+ */
+struct GNUNET_SOCIAL_Nym;
+
+/**
+ * Handle for a place where social interactions happen.
+ */
+struct GNUNET_SOCIAL_Place;
+
+/**
+ * Host handle for a place that we entered.
+ */
+struct GNUNET_SOCIAL_Host;
+
+/**
+ * Guest handle for place that we entered.
+ */
+struct GNUNET_SOCIAL_Guest;
+
+/**
+ * Handle that can be used to reconnect to a place as host.
+ */
+struct GNUNET_SOCIAL_HostConnection;
+
+/**
+ * Handle that can be used to reconnect to a place as guest.
+ */
+struct GNUNET_SOCIAL_GuestConnection;
+
+/**
+ * Notification about an available identity.
+ *
+ * @param cls
+ *        Closure.
+ * @param pub_key
+ *        Public key of ego.
+ * @param name
+ *        Name of ego.
+ */
+typedef void
+(*GNUNET_SOCIAL_AppEgoCallback) (void *cls,
+                                 struct GNUNET_SOCIAL_Ego *ego,
+                                 const struct GNUNET_CRYPTO_EcdsaPublicKey 
*ego_pub_key,
+                                 const char *name);
+
+
+/**
+ * Entry status of a place per application.
+ */
+enum GNUNET_SOCIAL_AppPlaceState
+{
+  /**
+   * The place was once entered by the ego, but left since.
+   * It's possible to establish a local connection to the place
+   * without re-entering to fetch history from the PSYCstore.
+   * @see enum GNUNET_PSYC_SlaveJoinFlags and GNUNET_SOCIAL_guest_enter()
+   */
+  GNUNET_SOCIAL_PLACE_STATE_ARCHIVED = 0,
+
+  /**
+   * The place is entered by the ego,
+   * but this application is not subscribed to it.
+   */
+  GNUNET_SOCIAL_PLACE_STATE_ENTERED = 1,
+
+  /**
+   * The place is entered by the ego and
+   * and this application is subscribed to it.
+   */
+  GNUNET_SOCIAL_PLACE_STATE_SUBSCRIBED = 2,
+};
+
+
+/**
+ * Called after receiving initial list of egos and places.
+ */
+typedef void
+(*GNUNET_SOCIAL_AppConnectedCallback) (void *cls);
+
+
+/**
+ * Notification about a home.
+ *
+ * @param cls
+ *        Closure.
+ * @param hconn
+ *        Host connection, to be used with GNUNET_SOCIAL_host_enter_reconnect()
+ * @param ego
+ *        Ego used to enter the place.
+ * @param place_pub_key
+ *        Public key of the place.
+ * @param place_state
+ *        @see enum GNUNET_SOCIAL_AppPlaceState
+ */
+typedef void
+(*GNUNET_SOCIAL_AppHostPlaceCallback) (void *cls,
+                                       struct GNUNET_SOCIAL_HostConnection 
*hconn,
+                                       struct GNUNET_SOCIAL_Ego *ego,
+                                       const struct 
GNUNET_CRYPTO_EddsaPublicKey *place_pub_key,
+                                       enum GNUNET_SOCIAL_AppPlaceState 
place_state);
+
+/**
+ * Notification about a place.
+ *
+ * @param cls
+ *        Closure.
+ * @param gconn
+ *        Guest connection, to be used with 
GNUNET_SOCIAL_guest_enter_reconnect()
+ * @param ego
+ *        Ego used to enter the place.
+ * @param place_pub_key
+ *        Public key of the place.
+ * @param place_state
+ *        @see enum GNUNET_SOCIAL_AppPlaceState
+ */
+typedef void
+(*GNUNET_SOCIAL_AppGuestPlaceCallback) (void *cls,
+                                        struct GNUNET_SOCIAL_GuestConnection 
*gconn,
+                                        struct GNUNET_SOCIAL_Ego *ego,
+                                        const struct 
GNUNET_CRYPTO_EddsaPublicKey *place_pub_key,
+                                        enum GNUNET_SOCIAL_AppPlaceState 
place_state);
+
+
+/**
+ * Establish application connection to the social service.
+ *
+ * The @host_cb and @guest_cb functions are
+ * initially called for each entered places,
+ * then later each time a new place is entered with the current app ID.
+ *
+ * @param cfg
+ *        Configuration.
+ * @param ego_cb
+ *        Function to notify about an available ego.
+ * @param host_cb
+ *        Function to notify about a place entered as host.
+ * @param guest_cb
+ *        Function to notify about a place entered as guest.
+ * @param cls
+ *        Closure for the callbacks.
+ *
+ * @return Handle that can be used to stop listening.
+ */
+struct GNUNET_SOCIAL_App *
+GNUNET_SOCIAL_app_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
+                           const char *id,
+                           GNUNET_SOCIAL_AppEgoCallback ego_cb,
+                           GNUNET_SOCIAL_AppHostPlaceCallback host_cb,
+                           GNUNET_SOCIAL_AppGuestPlaceCallback guest_cb,
+                           GNUNET_SOCIAL_AppConnectedCallback connected_cb,
+                           void *cls);
+
+
+/**
+ * Disconnect app.
+ *
+ * @param app
+ *        Application handle.
+ * @param disconnect_cb
+ *        Disconnect callback.
+ * @param disconnect_cls
+ *        Disconnect closure.
+ */
+void
+GNUNET_SOCIAL_app_disconnect (struct GNUNET_SOCIAL_App *app,
+                              GNUNET_ContinuationCallback disconnect_cb,
+                              void *disconnect_cls);
+
+
+/**
+ * Get the public key of @a ego.
+ *
+ * @param ego
+ *        Ego.
+ *
+ * @return Public key of ego.
+ */
+const struct GNUNET_CRYPTO_EcdsaPublicKey *
+GNUNET_SOCIAL_ego_get_pub_key (const struct GNUNET_SOCIAL_Ego *ego);
+
+
+/**
+ * Get the name of @a ego.
+ *
+ * @param ego
+ *        Ego.
+ *
+ * @return Public key of @a ego.
+ */
+const char *
+GNUNET_SOCIAL_ego_get_name (const struct GNUNET_SOCIAL_Ego *ego);
+
+
+/**
+ * Get the public key of a @a nym.
+ *
+ * Suitable, for example, to be used with GNUNET_SOCIAL_zone_add_nym().
+ *
+ * @param nym
+ *        Pseudonym to map to a cryptographic identifier.
+ *
+ * @return Public key of nym.
+ */
+const struct GNUNET_CRYPTO_EcdsaPublicKey *
+GNUNET_SOCIAL_nym_get_pub_key (const struct GNUNET_SOCIAL_Nym *nym);
+
+
+/**
+ * Get the hash of the public key of a @a nym.
+ *
+ * @param nym
+ *        Pseudonym to map to a cryptographic identifier.
+ *
+ * @return Hash of the public key of nym.
+ */
+const struct GNUNET_HashCode *
+GNUNET_SOCIAL_nym_get_pub_key_hash (const struct GNUNET_SOCIAL_Nym *nym);
+
+
+/**
+ * Function called asking for nym to be admitted to the place.
+ *
+ * Should call either GNUNET_SOCIAL_host_admit() or
+ * GNUNET_SOCIAL_host_reject_entry() (possibly asynchronously).  If this host
+ * cannot decide, it is fine to call neither function, in which case hopefully
+ * some other host of the place exists that will make the decision.  The @a nym
+ * reference remains valid until the #GNUNET_SOCIAL_FarewellCallback is invoked
+ * for it.
+ *
+ * @param cls
+ *        Closure.
+ * @param nym
+ *        Handle for the user who wants to enter.
+ * @param method_name
+ *        Method name in the entry request.
+ * @param variable_count
+ *        Number of elements in the @a variables array.
+ * @param variables
+ *        Variables present in the message.
+ * @param data
+ *        Payload given on enter (e.g. a password).
+ * @param data_size
+ *        Number of bytes in @a data.
+ */
+typedef void
+(*GNUNET_SOCIAL_AnswerDoorCallback) (void *cls,
+                                     struct GNUNET_SOCIAL_Nym *nym,
+                                     const char *method_name,
+                                     struct GNUNET_PSYC_Environment *env,
+                                     const void *data,
+                                     size_t data_size);
+
+
+/**
+ * Function called when a @a nym leaves the place.
+ *
+ * This is also called if the @a nym was never given permission to enter
+ * (i.e. the @a nym stopped asking to get in).
+ *
+ * @param cls
+ *        Closure.
+ * @param nym
+ *        Handle for the user who left.
+ */
+typedef void
+(*GNUNET_SOCIAL_FarewellCallback) (void *cls,
+                                   const struct GNUNET_SOCIAL_Nym *nym,
+                                   struct GNUNET_PSYC_Environment *env);
+
+
+/**
+ * Function called after the host entered a home.
+ *
+ * @param cls
+ *        Closure.
+ * @param result
+ *        #GNUNET_OK on success, or
+ *        #GNUNET_SYSERR on error.
+ * @param place_pub_key
+ *        Public key of home.
+ * @param max_message_id
+ *        Last message ID sent to the channel.
+ *        Or 0 if no messages have been sent to the place yet.
+ */
+typedef void
+(*GNUNET_SOCIAL_HostEnterCallback) (void *cls, int result,
+                                    const struct GNUNET_CRYPTO_EddsaPublicKey 
*place_pub_key,
+                                    uint64_t max_message_id);
+
+
+/**
+ * Enter a place as host.
+ *
+ * A place is created upon first entering, and it is active until permanently
+ * left using GNUNET_SOCIAL_host_leave().
+ *
+ * @param cfg
+ *        Configuration to contact the social service.
+ * @param ego
+ *        Identity of the host.
+ * @param place_key
+ *        Private-public key pair of the place.
+ *        NULL for ephemeral places.
+ * @param policy
+ *        Policy specifying entry and history restrictions for the place.
+ * @param slicer
+ *        Slicer to handle incoming messages.
+ * @param enter_cb
+ *        Function called when the place is entered and ready to use.
+ * @param answer_door_cb
+ *        Function to handle new nyms that want to enter.
+ * @param farewell_cb
+ *        Function to handle departing nyms.
+ * @param cls
+ *        Closure for the callbacks.
+ *
+ * @return Handle for the host.
+ */
+struct GNUNET_SOCIAL_Host *
+GNUNET_SOCIAL_host_enter (const struct GNUNET_SOCIAL_App *app,
+                          const struct GNUNET_SOCIAL_Ego *ego,
+                          enum GNUNET_PSYC_Policy policy,
+                          struct GNUNET_PSYC_Slicer *slicer,
+                          GNUNET_SOCIAL_HostEnterCallback enter_cb,
+                          GNUNET_SOCIAL_AnswerDoorCallback answer_door_cb,
+                          GNUNET_SOCIAL_FarewellCallback farewell_cb,
+                          void *cls);
+
+
+/**
+ * Reconnect to an already entered place as host.
+ *
+ * @param hconn
+ *        Host connection handle.
+ *        @see GNUNET_SOCIAL_app_connect() & 
GNUNET_SOCIAL_AppHostPlaceCallback()
+ * @param slicer
+ *        Slicer to handle incoming messages.
+ * @param enter_cb
+ *        Function called when the place is entered and ready to use.
+ * @param answer_door_cb
+ *        Function to handle new nyms that want to enter.
+ * @param farewell_cb
+ *        Function to handle departing nyms.
+ * @param cls
+ *        Closure for the callbacks.
+ *
+ * @return Handle for the host.
+ */
+struct GNUNET_SOCIAL_Host *
+GNUNET_SOCIAL_host_enter_reconnect (struct GNUNET_SOCIAL_HostConnection *hconn,
+                                    struct GNUNET_PSYC_Slicer *slicer,
+                                    GNUNET_SOCIAL_HostEnterCallback enter_cb,
+                                    GNUNET_SOCIAL_AnswerDoorCallback 
answer_door_cb,
+                                    GNUNET_SOCIAL_FarewellCallback farewell_cb,
+                                    void *cls);
+
+
+/**
+ * Decision whether to admit @a nym into the place or refuse entry.
+ *
+ * @param hst
+ *        Host of the place.
+ * @param nym
+ *        Handle for the entity that wanted to enter.
+ * @param is_admitted
+ *        #GNUNET_YES    if @a nym is admitted,
+ *        #GNUNET_NO     if @a nym is refused entry,
+ *        #GNUNET_SYSERR if we cannot answer the request.
+ * @param entry_resp
+ *        Entry response message, or NULL.
+ * @return #GNUNET_OK on success,
+ *         #GNUNET_SYSERR if the message is too large.
+ */
+int
+GNUNET_SOCIAL_host_entry_decision (struct GNUNET_SOCIAL_Host *hst,
+                                   struct GNUNET_SOCIAL_Nym *nym,
+                                   int is_admitted,
+                                   const struct GNUNET_PSYC_Message 
*entry_resp);
+
+
+/**
+ * Throw @a nym out of the place.
+ *
+ * Sends a _notice_place_leave announcement to the home.
+ *
+ * The @a nym reference will remain valid until the
+ * #GNUNET_SOCIAL_FarewellCallback is invoked,
+ * which should be very soon after this call.
+ *
+ * @param host
+ *        Host of the place.
+ * @param nym
+ *        Handle for the entity to be ejected.
+ * @param env
+ *        Environment for the message or NULL.
+ *        _nym is set to @e nym regardless whether an @e env is provided.
+ */
+void
+GNUNET_SOCIAL_host_eject (struct GNUNET_SOCIAL_Host *host,
+                          const struct GNUNET_SOCIAL_Nym *nym,
+                          struct GNUNET_PSYC_Environment *env);
+
+
+/**
+ * Flags for announcements by a host.
+ */
+enum GNUNET_SOCIAL_AnnounceFlags
+{
+  GNUNET_SOCIAL_ANNOUNCE_NONE = 0,
+
+  /**
+   * Whether this announcement removes all objects from the place.
+   *
+   * New objects can be still added to the now empty place using the @e env
+   * parameter of the same announcement.
+   */
+  GNUNET_SOCIAL_ANNOUNCE_CLEAR_OBJECTS = 1 << 0
+};
+
+
+/**
+ * Handle for an announcement request.
+ */
+struct GNUNET_SOCIAL_Announcement;
+
+
+/**
+ * Send a message to all nyms that are present in the place.
+ *
+ * This function is restricted to the host.  Nyms can only send requests
+ * to the host who can decide to relay it to everyone in the place.
+ *
+ * @param host
+ *        Host of the place.
+ * @param method_name
+ *        Method to use for the announcement.
+ * @param env
+ *        Environment containing variables for the message and operations
+ *        on objects of the place.
+ *        Has to remain available until the first call to @a notify_data.
+ *        Can be NULL.
+ * @param notify_data
+ *        Function to call to get the payload of the announcement.
+ * @param notify_data_cls
+ *        Closure for @a notify.
+ * @param flags
+ *        Flags for this announcement.
+ *
+ * @return NULL on error (another announcement already in progress?).
+ */
+struct GNUNET_SOCIAL_Announcement *
+GNUNET_SOCIAL_host_announce (struct GNUNET_SOCIAL_Host *host,
+                             const char *method_name,
+                             const struct GNUNET_PSYC_Environment *env,
+                             GNUNET_PSYC_TransmitNotifyData notify_data,
+                             void *notify_data_cls,
+                             enum GNUNET_SOCIAL_AnnounceFlags flags);
+
+
+/**
+ * Resume transmitting announcement.
+ *
+ * @param a
+ *        The announcement to resume.
+ */
+void
+GNUNET_SOCIAL_host_announce_resume (struct GNUNET_SOCIAL_Announcement *a);
+
+
+/**
+ * Cancel announcement.
+ *
+ * @param a
+ *        The announcement to cancel.
+ */
+void
+GNUNET_SOCIAL_host_announce_cancel (struct GNUNET_SOCIAL_Announcement *a);
+
+
+/**
+ * Allow relaying messages from guests matching a given @a method_prefix.
+ *
+ * @param host
+ *        The host.
+ * @param method_prefix
+ *        Method prefix to allow.
+ */
+void
+GNUNET_SOCIAL_host_relay_allow_method (struct GNUNET_SOCIAL_Host *host,
+                                       const char *method_prefix);
+
+
+/**
+ * Allow relaying changes to objects of the place.
+ *
+ * Only applies to messages with an allowed method name.
+ * @see GNUNET_SCOIAL_host_relay_allow_method()
+ *
+ * @param host
+ *        The host.
+ * @param object_prefix
+ *        Object prefix to allow modifying.
+ */
+void
+GNUNET_SOCIAL_host_relay_allow_method (struct GNUNET_SOCIAL_Host *host,
+                                       const char *object_prefix);
+
+
+/**
+ * Stop relaying messages from guests.
+ *
+ * Remove all allowed relay rules.
+ *
+ *
+ *
+ */
+void
+GNUNET_SOCIAL_host_relay_stop (struct GNUNET_SOCIAL_Host *host);
+
+
+/**
+ * Obtain handle for a hosted place.
+ *
+ * The returned handle can be used to access the place API.
+ *
+ * @param host
+ *        Handle for the host.
+ *
+ * @return Handle for the hosted place, valid as long as @a host is valid.
+ */
+struct GNUNET_SOCIAL_Place *
+GNUNET_SOCIAL_host_get_place (struct GNUNET_SOCIAL_Host *host);
+
+
+/**
+ * Disconnect from a home.
+ *
+ * Invalidates host handle.
+ *
+ * @param hst
+ *        The host to disconnect.
+ * @param disconnect_cb
+ *        Function called after disconnected from the service.
+ * @param cls
+ *        Closure for @a disconnect_cb.
+ */
+void
+GNUNET_SOCIAL_host_disconnect (struct GNUNET_SOCIAL_Host *hst,
+                               GNUNET_ContinuationCallback disconnect_cb,
+                               void *cls);
+
+
+/**
+ * Stop hosting a home.
+ *
+ * Sends a _notice_place_closing announcement to the home.
+ * Invalidates host handle.
+ *
+ * @param hst
+ *        Host leaving.
+ * @param env
+ *        Environment for the message or NULL.
+ * @param disconnect_cb
+ *        Function called after the host left the place
+ *        and disconnected from the service.
+ * @param cls
+ *        Closure for @a disconnect_cb.
+ */
+void
+GNUNET_SOCIAL_host_leave (struct GNUNET_SOCIAL_Host *hst,
+                          const struct GNUNET_PSYC_Environment *env,
+                          GNUNET_ContinuationCallback disconnect_cb,
+                          void *cls);
+
+
+/**
+ * Function called after the guest entered the local copy of the place.
+ *
+ * History and object query functions can be used after this call,
+ * but new messages can't be sent or received.
+ *
+ * @param cls
+ *        Closure.
+ * @param result
+ *        #GNUNET_OK on success, or
+ *        #GNUNET_SYSERR on error, e.g. could not connect to the service, or
+ *        could not resolve GNS name.
+ * @param place_pub_key
+ *        Public key of place.
+ * @param max_message_id
+ *        Last message ID sent to the place.
+ *        Or 0 if no messages have been sent to the place yet.
+ */
+typedef void
+(*GNUNET_SOCIAL_GuestEnterCallback) (void *cls, int result,
+                                     const struct GNUNET_CRYPTO_EddsaPublicKey 
*place_pub_key,
+                                     uint64_t max_message_id);
+
+
+/**
+ * Function called upon a guest receives a decision about entry to the place.
+ *
+ * @param is_admitted
+ *        Is the guest admitted to the place?
+ *        #GNUNET_YES    if admitted,
+ *        #GNUNET_NO     if refused entry,
+ *        #GNUNET_SYSERR if the request could not be answered.
+ * @param data
+ *        Entry response message.
+ */
+typedef void
+(*GNUNET_SOCIAL_EntryDecisionCallback) (void *cls,
+                                        int is_admitted,
+                                        const struct GNUNET_PSYC_Message 
*entry_resp);
+
+
+/**
+ * Request entry to a place as a guest.
+ *
+ * @param app
+ *        Application handle.
+ * @param ego
+ *        Identity of the guest.
+ * @param place_pub_key
+ *        Public key of the place to enter.
+ * @param flags
+ *        Flags for the entry.
+ * @param origin
+ *        Peer identity of the origin of the underlying multicast group.
+ * @param relay_count
+ *        Number of elements in the @a relays array.
+ * @param relays
+ *        Relays for the underlying multicast group.
+ * @param entry_msg
+ *        Entry message.
+ * @param slicer
+ *        Slicer to use for processing incoming requests from guests.
+ *
+ * @return NULL on errors, otherwise handle for the guest.
+ */
+struct GNUNET_SOCIAL_Guest *
+GNUNET_SOCIAL_guest_enter (const struct GNUNET_SOCIAL_App *app,
+                           const struct GNUNET_SOCIAL_Ego *ego,
+                           const struct GNUNET_CRYPTO_EddsaPublicKey 
*place_pub_key,
+                           enum GNUNET_PSYC_SlaveJoinFlags flags,
+                           const struct GNUNET_PeerIdentity *origin,
+                           uint32_t relay_count,
+                           const struct GNUNET_PeerIdentity *relays,
+                           const struct GNUNET_PSYC_Message *entry_msg,
+                           struct GNUNET_PSYC_Slicer *slicer,
+                           GNUNET_SOCIAL_GuestEnterCallback local_enter_cb,
+                           GNUNET_SOCIAL_EntryDecisionCallback entry_dcsn_cb,
+                           void *cls);
+
+
+/**
+ * Request entry to a place by name as a guest.
+ *
+ * @param app
+ *        Application handle.
+ * @param ego
+ *        Identity of the guest.
+ * @param gns_name
+ *        GNS name of the place to enter.  Either in the form of
+ *        'room.friend.gnu', or 'NYMPUBKEY.zkey'.  This latter case refers to
+ *        the 'PLACE' record of the empty label ("+") in the GNS zone with the
+ *        nym's public key 'NYMPUBKEY', and can be used to request entry to a
+ *        pseudonym's place directly.
+ * @param password
+ *        Password to decrypt the record, or NULL for cleartext records.
+ * @param join_msg
+ *        Entry request message.
+ * @param slicer
+ *        Slicer to use for processing incoming requests from guests.
+ * @param local_enter_cb
+ *        Called upon connection established to the social service.
+ * @param entry_decision_cb
+ *        Called upon receiving entry decision.
+ *
+ * @return NULL on errors, otherwise handle for the guest.
+ */
+struct GNUNET_SOCIAL_Guest *
+GNUNET_SOCIAL_guest_enter_by_name (const struct GNUNET_SOCIAL_App *app,
+                                   const struct GNUNET_SOCIAL_Ego *ego,
+                                   const char *gns_name,
+                                   const char *password,
+                                   const struct GNUNET_PSYC_Message *join_msg,
+                                   struct GNUNET_PSYC_Slicer *slicer,
+                                   GNUNET_SOCIAL_GuestEnterCallback 
local_enter_cb,
+                                   GNUNET_SOCIAL_EntryDecisionCallback 
entry_decision_cb,
+                                   void *cls);
+
+
+/**
+ * Reconnect to an already entered place as guest.
+ *
+ * @param gconn
+ *        Guest connection handle.
+ *        @see GNUNET_SOCIAL_app_connect() & 
GNUNET_SOCIAL_AppGuestPlaceCallback()
+ * @param flags
+ *        Flags for the entry.
+ * @param slicer
+ *        Slicer to use for processing incoming requests from guests.
+ * @param local_enter_cb
+ *        Called upon connection established to the social service.
+ * @param entry_decision_cb
+ *        Called upon receiving entry decision.
+ *
+ * @return NULL on errors, otherwise handle for the guest.
+ */
+struct GNUNET_SOCIAL_Guest *
+GNUNET_SOCIAL_guest_enter_reconnect (struct GNUNET_SOCIAL_GuestConnection 
*gconn,
+                                     enum GNUNET_PSYC_SlaveJoinFlags flags,
+                                     struct GNUNET_PSYC_Slicer *slicer,
+                                     GNUNET_SOCIAL_GuestEnterCallback 
local_enter_cb,
+                                     void *cls);
+
+
+/**
+ * Flags for talking to the host of a place.
+ */
+enum GNUNET_SOCIAL_TalkFlags
+{
+  GNUNET_SOCIAL_TALK_NONE = 0
+};
+
+
+/**
+ * A talk request.
+ */
+struct GNUNET_SOCIAL_TalkRequest;
+
+
+/**
+ * Talk to the host of the place.
+ *
+ * @param place
+ *        Place where we want to talk to the host.
+ * @param method_name
+ *        Method to invoke on the host.
+ * @param env
+ *        Environment containing variables for the message, or NULL.
+ * @param notify_data
+ *        Function to use to get the payload for the method.
+ * @param notify_data_cls
+ *        Closure for @a notify_data.
+ * @param flags
+ *        Flags for the message being sent.
+ *
+ * @return NULL if we are already trying to talk to the host,
+ *         otherwise handle to cancel the request.
+ */
+struct GNUNET_SOCIAL_TalkRequest *
+GNUNET_SOCIAL_guest_talk (struct GNUNET_SOCIAL_Guest *guest,
+                          const char *method_name,
+                          const struct GNUNET_PSYC_Environment *env,
+                          GNUNET_PSYC_TransmitNotifyData notify_data,
+                          void *notify_data_cls,
+                          enum GNUNET_SOCIAL_TalkFlags flags);
+
+
+/**
+ * Resume talking to the host of the place.
+ *
+ * @param tr
+ *        Talk request to resume.
+ */
+void
+GNUNET_SOCIAL_guest_talk_resume (struct GNUNET_SOCIAL_TalkRequest *tr);
+
+
+/**
+ * Cancel talking to the host of the place.
+ *
+ * @param tr
+ *        Talk request to cancel.
+ */
+void
+GNUNET_SOCIAL_guest_talk_cancel (struct GNUNET_SOCIAL_TalkRequest *tr);
+
+
+/**
+ * Disconnect from a place.
+ *
+ * Invalidates guest handle.
+ *
+ * @param gst
+ *        The guest to disconnect.
+ * @param disconnect_cb
+ *        Function called after disconnected from the service.
+ * @param cls
+ *        Closure for @a disconnect_cb.
+ */
+void
+GNUNET_SOCIAL_guest_disconnect (struct GNUNET_SOCIAL_Guest *gst,
+                                GNUNET_ContinuationCallback disconnect_cb,
+                                void *cls);
+
+
+/**
+ * Leave a place temporarily or permanently.
+ *
+ * Notifies the owner of the place about leaving, and destroys the place 
handle.
+ *
+ * @param place
+ *        Place to leave.
+ * @param env
+ *        Optional environment for the leave message if @a keep_active
+ *        is #GNUNET_NO.  NULL if not needed.
+ * @param disconnect_cb
+ *        Called upon disconnecting from the social service.
+ */
+void
+GNUNET_SOCIAL_guest_leave (struct GNUNET_SOCIAL_Guest *gst,
+                           struct GNUNET_PSYC_Environment *env,
+                           GNUNET_ContinuationCallback disconnect_cb,
+                           void *leave_cls);
+
+
+/**
+ * Obtain handle for a place entered as guest.
+ *
+ * The returned handle can be used to access the place API.
+ *
+ * @param guest  Handle for the guest.
+ *
+ * @return Handle for the place, valid as long as @a guest is valid.
+ */
+struct GNUNET_SOCIAL_Place *
+GNUNET_SOCIAL_guest_get_place (struct GNUNET_SOCIAL_Guest *guest);
+
+
+/**
+ * A history request.
+ */
+struct GNUNET_SOCIAL_HistoryRequest;
+
+
+/**
+ * Get the public key of a place.
+ *
+ * @param plc
+ *        Place.
+ *
+ * @return Public key of the place.
+ */
+const struct GNUNET_CRYPTO_EddsaPublicKey *
+GNUNET_SOCIAL_place_get_pub_key (const struct GNUNET_SOCIAL_Place *plc);
+
+
+/**
+ * Set message processing @a flags for a @a method_prefix.
+ *
+ * @param plc
+ *        Place.
+ * @param method_prefix
+ *        Method prefix @a flags apply to.
+ * @param flags
+ *        The flags that apply to a matching @a method_prefix.
+ */
+void
+GNUNET_SOCIAL_place_msg_proc_set (struct GNUNET_SOCIAL_Place *plc,
+                                  const char *method_prefix,
+                                  enum GNUNET_SOCIAL_MsgProcFlags flags);
+
+/**
+ * Clear all message processing flags previously set for this place.
+ */
+void
+GNUNET_SOCIAL_place_msg_proc_clear (struct GNUNET_SOCIAL_Place *plc);
+
+
+/**
+ * Learn about the history of a place.
+ *
+ * Messages are returned through the @a slicer function
+ * and have the #GNUNET_PSYC_MESSAGE_HISTORIC flag set.
+ *
+ * @param place
+ *        Place we want to learn more about.
+ * @param start_message_id
+ *        First historic message we are interested in.
+ * @param end_message_id
+ *        Last historic message we are interested in (inclusive).
+ * @param method_prefix
+ *        Only retrieve messages with this method prefix.
+ * @param flags
+ *        OR'ed GNUNET_PSYC_HistoryReplayFlags
+ * @param slicer
+ *        Slicer to use for retrieved messages.
+ *        Can be the same as the slicer of the place.
+ * @param result_cb
+ *        Function called after all messages retrieved.
+ *        NULL if not needed.
+ * @param cls Closure for @a result_cb.
+ */
+struct GNUNET_SOCIAL_HistoryRequest *
+GNUNET_SOCIAL_place_history_replay (struct GNUNET_SOCIAL_Place *plc,
+                                    uint64_t start_message_id,
+                                    uint64_t end_message_id,
+                                    const char *method_prefix,
+                                    uint32_t flags,
+                                    struct GNUNET_PSYC_Slicer *slicer,
+                                    GNUNET_ResultCallback result_cb,
+                                    void *cls);
+
+
+/**
+ * Learn about the history of a place.
+ *
+ * Sends messages through the slicer function of the place where
+ * start_message_id <= message_id <= end_message_id.
+ * The messages will have the #GNUNET_PSYC_MESSAGE_HISTORIC flag set.
+ *
+ * To get the latest message, use 0 for both the start and end message ID.
+ *
+ * @param place
+ *        Place we want to learn more about.
+ * @param message_limit
+ *        Maximum number of historic messages we are interested in.
+ * @param result_cb
+ *        Function called after all messages retrieved.
+ *        NULL if not needed.
+ * @param cls Closure for @a result_cb.
+ */
+struct GNUNET_SOCIAL_HistoryRequest *
+GNUNET_SOCIAL_place_history_replay_latest (struct GNUNET_SOCIAL_Place *plc,
+                                           uint64_t message_limit,
+                                           const char *method_prefix,
+                                           uint32_t flags,
+                                           struct GNUNET_PSYC_Slicer *slicer,
+                                           GNUNET_ResultCallback result_cb,
+                                           void *cls);
+
+/**
+ * Cancel learning about the history of a place.
+ *
+ * @param hist
+ *        History lesson to cancel.
+ */
+void
+GNUNET_SOCIAL_place_history_replay_cancel (struct GNUNET_SOCIAL_HistoryRequest 
*hist);
+
+
+struct GNUNET_SOCIAL_LookHandle;
+
+
+/**
+ * Look at a particular object in the place.
+ *
+ * The best matching object is returned (its name might be less specific than
+ * what was requested).
+ *
+ * @param place
+ *        The place to look the object at.
+ * @param full_name
+ *        Full name of the object.
+ *
+ * @return NULL if there is no such object at this place.
+ */
+struct GNUNET_SOCIAL_LookHandle *
+GNUNET_SOCIAL_place_look_at (struct GNUNET_SOCIAL_Place *plc,
+                             const char *full_name,
+                             GNUNET_PSYC_StateVarCallback var_cb,
+                             GNUNET_ResultCallback result_cb,
+                             void *cls);
+
+/**
+ * Look for objects in the place with a matching name prefix.
+ *
+ * @param place
+ *        The place to look its objects at.
+ * @param name_prefix
+ *        Look at objects with names beginning with this value.
+ * @param var_cb
+ *        Function to call for each object found.
+ * @param cls
+ *        Closure for callback function.
+ *
+ * @return Handle that can be used to stop looking at objects.
+ */
+struct GNUNET_SOCIAL_LookHandle *
+GNUNET_SOCIAL_place_look_for (struct GNUNET_SOCIAL_Place *plc,
+                              const char *name_prefix,
+                              GNUNET_PSYC_StateVarCallback var_cb,
+                              GNUNET_ResultCallback result_cb,
+                              void *cls);
+
+
+/**
+ * Stop looking at objects.
+ *
+ * @param lh Look handle to stop.
+ */
+void
+GNUNET_SOCIAL_place_look_cancel (struct GNUNET_SOCIAL_LookHandle *lh);
+
+
+/**
+ * Advertise a @e place in the GNS zone of @a ego.
+ *
+ * @param app
+ *        Application handle.
+ * @param ego
+ *        Ego.
+ * @param place_pub_key
+ *        Public key of place to add.
+ * @param name
+ *        The name for the PLACE record to put in the zone.
+ * @param password
+ *        Password used to encrypt the record or NULL to keep it cleartext.
+ * @param relay_count
+ *        Number of elements in the @a relays array.
+ * @param relays
+ *        List of relays to put in the PLACE record to advertise
+ *        as entry points to the place in addition to the origin.
+ * @param expiration_time
+ *        Expiration time of the record, use 0 to remove the record.
+ * @param result_cb
+ *        Function called with the result of the operation.
+ * @param result_cls
+ *        Closure for @a result_cb
+ *
+ * @return #GNUNET_OK if the request was sent,
+ *         #GNUNET_SYSERR on error, e.g. the name/password is too long.
+ */
+int
+GNUNET_SOCIAL_zone_add_place (const struct GNUNET_SOCIAL_App *app,
+                              const struct GNUNET_SOCIAL_Ego *ego,
+                              const char *name,
+                              const char *password,
+                              const struct GNUNET_CRYPTO_EddsaPublicKey 
*place_pub_key,
+                              const struct GNUNET_PeerIdentity *origin,
+                              uint32_t relay_count,
+                              const struct GNUNET_PeerIdentity *relays,
+                              struct GNUNET_TIME_Absolute expiration_time,
+                              GNUNET_ResultCallback result_cb,
+                              void *result_cls);
+
+
+/**
+ * Add public key to the GNS zone of the @e ego.
+ *
+ * @param cfg
+ *        Configuration.
+ * @param ego
+ *        Ego.
+ * @param name
+ *        The name for the PKEY record to put in the zone.
+ * @param nym_pub_key
+ *        Public key of nym to add.
+ * @param expiration_time
+ *        Expiration time of the record, use 0 to remove the record.
+ * @param result_cb
+ *        Function called with the result of the operation.
+ * @param result_cls
+ *        Closure for @a result_cb
+ *
+ * @return #GNUNET_OK if the request was sent,
+ *         #GNUNET_SYSERR on error, e.g. the name is too long.
+ */
+int
+GNUNET_SOCIAL_zone_add_nym (const struct GNUNET_SOCIAL_App *app,
+                            const struct GNUNET_SOCIAL_Ego *ego,
+                            const char *name,
+                            const struct GNUNET_CRYPTO_EcdsaPublicKey 
*nym_pub_key,
+                            struct GNUNET_TIME_Absolute expiration_time,
+                            GNUNET_ResultCallback result_cb,
+                            void *result_cls);
+
+
+#if 0                           /* keep Emacsens' auto-indent happy */
+{
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+/* ifndef GNUNET_SOCIAL_SERVICE_H */
+#endif
+
+/** @} */  /* end of group */
diff --git a/src/multicast/.gitignore b/src/multicast/.gitignore
new file mode 100644
index 0000000..a97844e
--- /dev/null
+++ b/src/multicast/.gitignore
@@ -0,0 +1,7 @@
+gnunet-service-multicast
+gnunet-multicast
+test_multicast 
+test_multicast_multipeer
+test_multicast_2peers
+test_multicast_multipeer_line
+test_multicast_multipeer_star
diff --git a/src/multicast/Makefile.am b/src/multicast/Makefile.am
new file mode 100644
index 0000000..61a9f8b
--- /dev/null
+++ b/src/multicast/Makefile.am
@@ -0,0 +1,79 @@
+# This Makefile.am is in the public domain
+AM_CPPFLAGS = -I$(top_srcdir)/src/include
+
+pkgcfgdir= $(pkgdatadir)/config.d/
+
+libexecdir= $(pkglibdir)/libexec/
+
+pkgcfg_DATA = \
+  multicast.conf
+
+if MINGW
+ WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols
+endif
+
+if USE_COVERAGE
+  AM_CFLAGS = -fprofile-arcs -ftest-coverage
+endif
+
+lib_LTLIBRARIES = libgnunetmulticast.la
+
+libgnunetmulticast_la_SOURCES = \
+  multicast_api.c multicast.h
+libgnunetmulticast_la_LIBADD = \
+  $(top_builddir)/src/util/libgnunetutil.la \
+  $(GN_LIBINTL) $(XLIB)
+libgnunetmulticast_la_LDFLAGS = \
+  $(GN_LIB_LDFLAGS)  $(WINFLAGS) \
+  -version-info 0:0:0
+
+
+bin_PROGRAMS = \
+ gnunet-multicast
+
+libexec_PROGRAMS = \
+ gnunet-service-multicast \
+ $(EXP_LIBEXEC)
+
+gnunet_multicast_SOURCES = \
+ gnunet-multicast.c
+gnunet_multicast_LDADD = \
+  $(top_builddir)/src/util/libgnunetutil.la \
+  $(GN_LIBINTL)
+
+gnunet_service_multicast_SOURCES = \
+ gnunet-service-multicast.c
+gnunet_service_multicast_LDADD = \
+  $(top_builddir)/src/util/libgnunetutil.la \
+  $(top_builddir)/src/cadet/libgnunetcadet.la \
+  $(top_builddir)/src/statistics/libgnunetstatistics.la \
+  $(GN_LIBINTL)
+
+check_PROGRAMS = \
+  test_multicast \
+  test_multicast_multipeer_star \
+  test_multicast_multipeer_line
+
+if ENABLE_TEST_RUN
+AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:address@hidden@}; 
export PATH=$${GNUNET_PREFIX:address@hidden@}/bin:$$PATH; unset XDG_DATA_HOME; 
unset XDG_CONFIG_HOME;
+TESTS = $(check_PROGRAMS)
+endif
+
+test_multicast_SOURCES = \
+ test_multicast.c
+test_multicast_LDADD = \
+  libgnunetmulticast.la \
+  $(top_builddir)/src/testing/libgnunettesting.la \
+  $(top_builddir)/src/util/libgnunetutil.la
+test_multicast_multipeer_star_SOURCES = \
+  test_multicast_multipeer.c
+test_multicast_multipeer_star_LDADD = \
+  libgnunetmulticast.la \
+  $(top_builddir)/src/testbed/libgnunettestbed.la \
+  $(top_builddir)/src/util/libgnunetutil.la
+test_multicast_multipeer_line_SOURCES = \
+  test_multicast_multipeer.c
+test_multicast_multipeer_line_LDADD = \
+  libgnunetmulticast.la \
+  $(top_builddir)/src/testbed/libgnunettestbed.la \
+  $(top_builddir)/src/util/libgnunetutil.la
diff --git a/src/multicast/gnunet-multicast.c b/src/multicast/gnunet-multicast.c
new file mode 100644
index 0000000..63e1d52
--- /dev/null
+++ b/src/multicast/gnunet-multicast.c
@@ -0,0 +1,79 @@
+/*
+     This file is part of GNUnet.
+     Copyright (C) 2013 GNUnet e.V.
+
+     GNUnet is free software: you can redistribute it and/or modify it
+     under the terms of the GNU Affero General Public License as published
+     by the Free Software Foundation, either version 3 of the License,
+     or (at your option) any later version.
+
+     GNUnet 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
+     Affero General Public License for more details.
+    
+     You should have received a copy of the GNU Affero General Public License
+     along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+     SPDX-License-Identifier: AGPL3.0-or-later
+*/
+
+/**
+ * @file multicast/gnunet-multicast.c
+ * @brief multicast for writing a tool
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "gnunet_util_lib.h"
+/* #include "gnunet_multicast_service.h" */
+
+/**
+ * Final status code.
+ */
+static int ret;
+
+/**
+ * Main function that will be run by the scheduler.
+ *
+ * @param cls closure
+ * @param args remaining command-line arguments
+ * @param cfgfile name of the configuration file used (for saving, can be 
NULL!)
+ * @param cfg configuration
+ */
+static void
+run (void *cls, char *const *args, const char *cfgfile,
+     const struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+  /* main code here */
+  puts( gettext_noop ("This command doesn't do anything yet.") );
+  ret = -1;
+}
+
+
+/**
+ * The main function.
+ *
+ * @param argc number of arguments from the command line
+ * @param argv command line arguments
+ * @return 0 ok, 1 on error
+ */
+int
+main (int argc, char *const *argv)
+{
+  static const struct GNUNET_GETOPT_CommandLineOption options[] = {
+    /* FIMXE: add options here */
+    GNUNET_GETOPT_OPTION_END
+  };
+  if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
+    return 2;
+
+  ret = (GNUNET_OK ==
+        GNUNET_PROGRAM_run (argc, argv, "gnunet-multicast",
+                            gettext_noop ("This command doesn't do anything 
yet."),
+                            options, &run,
+                            NULL)) ? ret : 1;
+  GNUNET_free ((void*) argv);
+  return ret;
+}
+
+/* end of gnunet-multicast.c */
diff --git a/src/multicast/gnunet-service-multicast.c 
b/src/multicast/gnunet-service-multicast.c
new file mode 100644
index 0000000..18c3661
--- /dev/null
+++ b/src/multicast/gnunet-service-multicast.c
@@ -0,0 +1,2234 @@
+/*
+     This file is part of GNUnet.
+     Copyright (C) 2009 GNUnet e.V.
+
+     GNUnet is free software: you can redistribute it and/or modify it
+     under the terms of the GNU Affero General Public License as published
+     by the Free Software Foundation, either version 3 of the License,
+     or (at your option) any later version.
+
+     GNUnet 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
+     Affero General Public License for more details.
+    
+     You should have received a copy of the GNU Affero General Public License
+     along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+     SPDX-License-Identifier: AGPL3.0-or-later
+*/
+
+/**
+ * @file multicast/gnunet-service-multicast.c
+ * @brief program that does multicast
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_signatures.h"
+#include "gnunet_applications.h"
+#include "gnunet_statistics_service.h"
+#include "gnunet_cadet_service.h"
+#include "gnunet_multicast_service.h"
+#include "multicast.h"
+
+/**
+ * Handle to our current configuration.
+ */
+static const struct GNUNET_CONFIGURATION_Handle *cfg;
+
+/**
+ * Service handle.
+ */
+static struct GNUNET_SERVICE_Handle *service;
+
+/**
+ * CADET handle.
+ */
+static struct GNUNET_CADET_Handle *cadet;
+
+/**
+ * Identity of this peer.
+ */
+static struct GNUNET_PeerIdentity this_peer;
+
+/**
+ * Handle to the statistics service.
+ */
+static struct GNUNET_STATISTICS_Handle *stats;
+
+/**
+ * All connected origin clients.
+ * Group's pub_key_hash -> struct Origin * (uniq)
+ */
+static struct GNUNET_CONTAINER_MultiHashMap *origins;
+
+/**
+ * All connected member clients.
+ * Group's pub_key_hash -> struct Member * (multi)
+ */
+static struct GNUNET_CONTAINER_MultiHashMap *members;
+
+/**
+ * Connected member clients per group.
+ * Group's pub_key_hash -> Member's pub_key_hash (uniq) -> struct Member * 
(uniq)
+ */
+static struct GNUNET_CONTAINER_MultiHashMap *group_members;
+
+/**
+ * Incoming CADET channels with connected children in the tree.
+ * Group's pub_key_hash -> struct Channel * (multi)
+ */
+static struct GNUNET_CONTAINER_MultiHashMap *channels_in;
+
+/**
+ * Outgoing CADET channels connecting to parents in the tree.
+ * Group's pub_key_hash -> struct Channel * (multi)
+ */
+static struct GNUNET_CONTAINER_MultiHashMap *channels_out;
+
+/**
+ * Incoming replay requests from CADET.
+ * Group's pub_key_hash ->
+ *   H(fragment_id, message_id, fragment_offset, flags) -> struct Channel *
+ */
+static struct GNUNET_CONTAINER_MultiHashMap *replay_req_cadet;
+
+/**
+ * Incoming replay requests from clients.
+ * Group's pub_key_hash ->
+ *   H(fragment_id, message_id, fragment_offset, flags) -> struct 
GNUNET_SERVICE_Client *
+ */
+static struct GNUNET_CONTAINER_MultiHashMap *replay_req_client;
+
+
+/**
+ * Join status of a remote peer.
+ */
+enum JoinStatus
+{
+  JOIN_REFUSED  = -1,
+  JOIN_NOT_ASKED = 0,
+  JOIN_WAITING   = 1,
+  JOIN_ADMITTED  = 2,
+};
+
+enum ChannelDirection
+{
+  DIR_INCOMING = 0,
+  DIR_OUTGOING = 1,
+};
+
+
+/**
+ * Context for a CADET channel.
+ */
+struct Channel
+{
+  /**
+   * Group the channel belongs to.
+   *
+   * Only set for outgoing channels.
+   */
+  struct Group *group;
+
+  /**
+   * CADET channel.
+   */
+  struct GNUNET_CADET_Channel *channel;
+
+  // FIXME: not used
+  /**
+   * CADET transmission handle.
+   */
+  struct GNUNET_CADET_TransmitHandle *tmit_handle;
+
+  /**
+   * Public key of the target group.
+   */
+  struct GNUNET_CRYPTO_EddsaPublicKey group_pub_key;
+
+  /**
+   * Hash of @a group_pub_key.
+   */
+  struct GNUNET_HashCode group_pub_hash;
+
+  /**
+   * Public key of the joining member.
+   */
+  struct GNUNET_CRYPTO_EcdsaPublicKey member_pub_key;
+
+  /**
+   * Remote peer identity.
+   */
+  struct GNUNET_PeerIdentity peer;
+
+  /**
+   * Current window size, set by cadet_notify_window_change()
+   */
+  int32_t window_size;
+
+  /**
+   * Is the connection established?
+   */
+  int8_t is_connected;
+
+  /**
+   * Is the remote peer admitted to the group?
+   * @see enum JoinStatus
+   */
+  int8_t join_status;
+
+  /**
+   * Number of messages waiting to be sent to CADET.
+   */
+  uint8_t msgs_pending;
+
+  /**
+   * Channel direction.
+   * @see enum ChannelDirection
+   */
+  uint8_t direction;
+};
+
+
+/**
+ * List of connected clients.
+ */
+struct ClientList
+{
+  struct ClientList *prev;
+  struct ClientList *next;
+  struct GNUNET_SERVICE_Client *client;
+};
+
+
+/**
+ * Client context for an origin or member.
+ */
+struct Group
+{
+  struct ClientList *clients_head;
+  struct ClientList *clients_tail;
+
+  /**
+   * Public key of the group.
+   */
+  struct GNUNET_CRYPTO_EddsaPublicKey pub_key;
+
+  /**
+   * Hash of @a pub_key.
+   */
+  struct GNUNET_HashCode pub_key_hash;
+
+  /**
+   * CADET port hash.
+   */
+  struct GNUNET_HashCode cadet_port_hash;
+
+  /**
+   * Is the client disconnected? #GNUNET_YES or #GNUNET_NO
+   */
+  uint8_t is_disconnected;
+
+  /**
+   * Is this an origin (#GNUNET_YES), or member (#GNUNET_NO)?
+   */
+  uint8_t is_origin;
+
+  union {
+    struct Origin *origin;
+    struct Member *member;
+  };
+};
+
+
+/**
+* Client context for a group's origin.
+ */
+struct Origin
+{
+  struct Group group;
+
+  /**
+   * Private key of the group.
+   */
+  struct GNUNET_CRYPTO_EddsaPrivateKey priv_key;
+
+  /**
+   * CADET port.
+   */
+  struct GNUNET_CADET_Port *cadet_port;
+
+  /**
+   * Last message fragment ID sent to the group.
+   */
+  uint64_t max_fragment_id;
+};
+
+
+/**
+ * Client context for a group member.
+ */
+struct Member
+{
+  struct Group group;
+
+  /**
+   * Private key of the member.
+   */
+  struct GNUNET_CRYPTO_EcdsaPrivateKey priv_key;
+
+  /**
+   * Public key of the member.
+   */
+  struct GNUNET_CRYPTO_EcdsaPublicKey pub_key;
+
+  /**
+   * Hash of @a pub_key.
+   */
+  struct GNUNET_HashCode pub_key_hash;
+
+  /**
+   * Join request sent to the origin / members.
+   */
+  struct MulticastJoinRequestMessage *join_req;
+
+  /**
+   * Join decision sent in reply to our request.
+   *
+   * Only a positive decision is stored here, in case of a negative decision 
the
+   * client is disconnected.
+   */
+  struct MulticastJoinDecisionMessageHeader *join_dcsn;
+
+  /**
+   * CADET channel to the origin.
+   */
+  struct Channel *origin_channel;
+
+  /**
+   * Peer identity of origin.
+   */
+  struct GNUNET_PeerIdentity origin;
+
+  /**
+   * Peer identity of relays (other members to connect).
+   */
+  struct GNUNET_PeerIdentity *relays;
+
+  /**
+   * Last request fragment ID sent to the origin.
+   */
+  uint64_t max_fragment_id;
+
+  /**
+   * Number of @a relays.
+   */
+  uint32_t relay_count;
+};
+
+
+/**
+ * Client context.
+ */
+struct Client {
+  struct GNUNET_SERVICE_Client *client;
+  struct Group *group;
+};
+
+
+struct ReplayRequestKey
+{
+  uint64_t fragment_id;
+  uint64_t message_id;
+  uint64_t fragment_offset;
+  uint64_t flags;
+};
+
+
+static struct Channel *
+cadet_channel_create (struct Group *grp, struct GNUNET_PeerIdentity *peer);
+
+static void
+cadet_channel_destroy (struct Channel *chn);
+
+static void
+client_send_join_decision (struct Member *mem,
+                           const struct MulticastJoinDecisionMessageHeader 
*hdcsn);
+
+
+/**
+ * Task run during shutdown.
+ *
+ * @param cls unused
+ */
+static void
+shutdown_task (void *cls)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "shutting down\n");
+  if (NULL != cadet)
+  {
+    GNUNET_CADET_disconnect (cadet);
+    cadet = NULL;
+  }
+  if (NULL != stats)
+  {
+    GNUNET_STATISTICS_destroy (stats, GNUNET_YES);
+    stats = NULL;
+  }
+  /* FIXME: do more clean up here */
+}
+
+
+/**
+ * Clean up origin data structures after a client disconnected.
+ */
+static void
+cleanup_origin (struct Origin *orig)
+{
+  struct Group *grp = &orig->group;
+  GNUNET_CONTAINER_multihashmap_remove (origins, &grp->pub_key_hash, orig);
+  if (NULL != orig->cadet_port)
+  {
+    GNUNET_CADET_close_port (orig->cadet_port);
+    orig->cadet_port = NULL;
+  }
+  GNUNET_free (orig);
+}
+
+
+/**
+ * Clean up member data structures after a client disconnected.
+ */
+static void
+cleanup_member (struct Member *mem)
+{
+  struct Group *grp = &mem->group;
+  struct GNUNET_CONTAINER_MultiHashMap *
+    grp_mem = GNUNET_CONTAINER_multihashmap_get (group_members,
+                                                 &grp->pub_key_hash);
+  GNUNET_assert (NULL != grp_mem);
+  GNUNET_CONTAINER_multihashmap_remove (grp_mem, &mem->pub_key_hash, mem);
+
+  if (0 == GNUNET_CONTAINER_multihashmap_size (grp_mem))
+  {
+    GNUNET_CONTAINER_multihashmap_remove (group_members, &grp->pub_key_hash,
+                                          grp_mem);
+    GNUNET_CONTAINER_multihashmap_destroy (grp_mem);
+  }
+  if (NULL != mem->join_dcsn)
+  {
+    GNUNET_free (mem->join_dcsn);
+    mem->join_dcsn = NULL;
+  }
+  if (NULL != mem->origin_channel)
+  {
+    GNUNET_CADET_channel_destroy (mem->origin_channel->channel);
+    mem->origin_channel = NULL;
+  }
+  GNUNET_CONTAINER_multihashmap_remove (members, &grp->pub_key_hash, mem);
+  GNUNET_free (mem);
+}
+
+
+/**
+ * Clean up group data structures after a client disconnected.
+ */
+static void
+cleanup_group (struct Group *grp)
+{
+  (GNUNET_YES == grp->is_origin)
+    ? cleanup_origin (grp->origin)
+    : cleanup_member (grp->member);
+}
+
+
+void
+replay_key_hash (uint64_t fragment_id, uint64_t message_id,
+                 uint64_t fragment_offset, uint64_t flags,
+                 struct GNUNET_HashCode *key_hash)
+{
+  struct ReplayRequestKey key = {
+    .fragment_id = fragment_id,
+    .message_id = message_id,
+    .fragment_offset = fragment_offset,
+    .flags = flags,
+  };
+  GNUNET_CRYPTO_hash (&key, sizeof (key), key_hash);
+}
+
+
+/**
+ * Remove channel from replay request hashmap.
+ *
+ * @param chn
+ *        Channel to remove.
+ *
+ * @return #GNUNET_YES if there are more entries to process,
+ *         #GNUNET_NO when reached end of hashmap.
+ */
+static int
+replay_req_remove_cadet (struct Channel *chn)
+{
+  if (NULL == chn || NULL == chn->group)
+    return GNUNET_SYSERR;
+
+  struct GNUNET_CONTAINER_MultiHashMap *
+    grp_replay_req = GNUNET_CONTAINER_multihashmap_get (replay_req_cadet,
+                                                        
&chn->group->pub_key_hash);
+  if (NULL == grp_replay_req)
+    return GNUNET_NO;
+
+  struct GNUNET_CONTAINER_MultiHashMapIterator *
+    it = GNUNET_CONTAINER_multihashmap_iterator_create (grp_replay_req);
+  struct GNUNET_HashCode key;
+  const struct Channel *c;
+  while (GNUNET_YES
+         == GNUNET_CONTAINER_multihashmap_iterator_next (it, &key,
+                                                         (const void **) &c))
+  {
+    if (c == chn)
+    {
+      GNUNET_CONTAINER_multihashmap_remove (grp_replay_req, &key, chn);
+      GNUNET_CONTAINER_multihashmap_iterator_destroy (it);
+      return GNUNET_YES;
+    }
+  }
+  GNUNET_CONTAINER_multihashmap_iterator_destroy (it);
+  return GNUNET_NO;
+}
+
+
+/**
+ * Remove client from replay request hashmap.
+ *
+ * @param client
+ *        Client to remove.
+ *
+ * @return #GNUNET_YES if there are more entries to process,
+ *         #GNUNET_NO when reached end of hashmap.
+ */
+static int
+replay_req_remove_client (struct Group *grp, struct GNUNET_SERVICE_Client 
*client)
+{
+  struct GNUNET_CONTAINER_MultiHashMap *
+    grp_replay_req = GNUNET_CONTAINER_multihashmap_get (replay_req_client,
+                                                        &grp->pub_key_hash);
+  if (NULL == grp_replay_req)
+    return GNUNET_NO;
+
+  struct GNUNET_CONTAINER_MultiHashMapIterator *
+    it = GNUNET_CONTAINER_multihashmap_iterator_create (grp_replay_req);
+  struct GNUNET_HashCode key;
+  const struct GNUNET_SERVICE_Client *c;
+  while (GNUNET_YES
+         == GNUNET_CONTAINER_multihashmap_iterator_next (it, &key,
+                                                         (const void **) &c))
+  {
+    if (c == client)
+    {
+      GNUNET_CONTAINER_multihashmap_remove (grp_replay_req, &key, client);
+      GNUNET_CONTAINER_multihashmap_iterator_destroy (it);
+      return GNUNET_YES;
+    }
+  }
+  GNUNET_CONTAINER_multihashmap_iterator_destroy (it);
+  return GNUNET_NO;
+}
+
+
+/**
+ * Send message to a client.
+ */
+static void
+client_send (struct GNUNET_SERVICE_Client *client,
+             const struct GNUNET_MessageHeader *msg)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "%p Sending message to client.\n", client);
+
+  struct GNUNET_MQ_Envelope *
+    env = GNUNET_MQ_msg_copy (msg);
+
+  GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client),
+                  env);
+}
+
+
+/**
+ * Send message to all clients connected to the group.
+ */
+static void
+client_send_group_keep_envelope (const struct Group *grp,
+                                 struct GNUNET_MQ_Envelope *env)
+{
+  struct ClientList *cli = grp->clients_head;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "%p Sending message to all clients of the group.\n",
+              grp);
+  while (NULL != cli)
+  {
+    GNUNET_MQ_send_copy (GNUNET_SERVICE_client_get_mq (cli->client),
+                         env);
+    cli = cli->next;
+  }
+}
+
+
+/**
+ * Send message to all clients connected to the group and
+ * takes care of freeing @env.
+ */
+static void
+client_send_group (const struct Group *grp,
+                   struct GNUNET_MQ_Envelope *env)
+{
+  client_send_group_keep_envelope (grp, env);
+  GNUNET_MQ_discard (env);
+}
+
+
+/**
+ * Iterator callback for sending a message to origin clients.
+ */
+static int
+client_send_origin_cb (void *cls, const struct GNUNET_HashCode *pub_key_hash,
+                       void *origin)
+{
+  struct GNUNET_MQ_Envelope *env = cls;
+  struct Member *orig = origin;
+
+  client_send_group_keep_envelope (&orig->group, env);
+  return GNUNET_YES;
+}
+
+
+/**
+ * Iterator callback for sending a message to member clients.
+ */
+static int
+client_send_member_cb (void *cls, const struct GNUNET_HashCode *pub_key_hash,
+                       void *member)
+{
+  struct GNUNET_MQ_Envelope *env = cls;
+  struct Member *mem = member;
+
+  if (NULL != mem->join_dcsn)
+  { /* Only send message to admitted members */
+    client_send_group_keep_envelope (&mem->group, env);
+  }
+  return GNUNET_YES;
+}
+
+
+/**
+ * Send message to all origin and member clients connected to the group.
+ *
+ * @param pub_key_hash
+ *        H(key_pub) of the group.
+ * @param msg
+ *        Message to send.
+ */
+static int
+client_send_all (struct GNUNET_HashCode *pub_key_hash,
+                 struct GNUNET_MQ_Envelope *env)
+{
+  int n = 0;
+  n += GNUNET_CONTAINER_multihashmap_get_multiple (origins, pub_key_hash,
+                                                   client_send_origin_cb,
+                                                   (void *) env);
+  n += GNUNET_CONTAINER_multihashmap_get_multiple (members, pub_key_hash,
+                                                   client_send_member_cb,
+                                                   (void *) env);
+  GNUNET_MQ_discard (env);
+  return n;
+}
+
+
+/**
+ * Send message to a random origin client or a random member client.
+ *
+ * @param grp  The group to send @a msg to.
+ * @param msg  Message to send.
+ */
+static int
+client_send_random (struct GNUNET_HashCode *pub_key_hash,
+                    struct GNUNET_MQ_Envelope *env)
+{
+  int n = 0;
+  n = GNUNET_CONTAINER_multihashmap_get_random (origins, client_send_origin_cb,
+                                                 (void *) env);
+  if (n <= 0)
+    n = GNUNET_CONTAINER_multihashmap_get_random (members, 
client_send_member_cb,
+                                                   (void *) env);
+  GNUNET_MQ_discard (env);
+  return n;
+}
+
+
+/**
+ * Send message to all origin clients connected to the group.
+ *
+ * @param pub_key_hash
+ *        H(key_pub) of the group.
+ * @param msg
+ *        Message to send.
+ */
+static int
+client_send_origin (struct GNUNET_HashCode *pub_key_hash,
+                    struct GNUNET_MQ_Envelope *env)
+{
+  int n = 0;
+  n += GNUNET_CONTAINER_multihashmap_get_multiple (origins, pub_key_hash,
+                                                   client_send_origin_cb,
+                                                   (void *) env);
+  return n;
+}
+
+
+/**
+ * Send fragment acknowledgement to all clients of the channel.
+ *
+ * @param pub_key_hash
+ *        H(key_pub) of the group.
+ */
+static void
+client_send_ack (struct GNUNET_HashCode *pub_key_hash)
+{
+  struct GNUNET_MQ_Envelope *env;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Sending message ACK to client.\n");
+  env = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_MULTICAST_FRAGMENT_ACK);
+  client_send_all (pub_key_hash, env);
+}
+
+
+struct CadetTransmitClosure
+{
+  struct Channel *chn;
+  const struct GNUNET_MessageHeader *msg;
+};
+
+
+/**
+ * Send a message to a CADET channel.
+ *
+ * @param chn  Channel.
+ * @param msg  Message.
+ */
+static void
+cadet_send_channel (struct Channel *chn, const struct GNUNET_MessageHeader 
*msg)
+{
+  struct GNUNET_MQ_Envelope *
+    env = GNUNET_MQ_msg_copy (msg);
+
+  GNUNET_MQ_send (GNUNET_CADET_get_mq (chn->channel), env);
+
+  if (0 < chn->window_size)
+  {
+    client_send_ack (&chn->group_pub_hash);
+  }
+  else
+  {
+    chn->msgs_pending++;
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "%p Queuing message. Pending messages: %u\n",
+                chn, chn->msgs_pending);
+  }
+}
+
+
+/**
+ * Create CADET channel and send a join request.
+ */
+static void
+cadet_send_join_request (struct Member *mem)
+{
+  mem->origin_channel = cadet_channel_create (&mem->group, &mem->origin);
+  cadet_send_channel (mem->origin_channel, &mem->join_req->header);
+
+  uint32_t i;
+  for (i = 0; i < mem->relay_count; i++)
+  {
+    struct Channel *
+      chn = cadet_channel_create (&mem->group, &mem->relays[i]);
+    cadet_send_channel (chn, &mem->join_req->header);
+  }
+}
+
+
+static int
+cadet_send_join_decision_cb (void *cls,
+                             const struct GNUNET_HashCode *group_pub_hash,
+                             void *channel)
+{
+  const struct MulticastJoinDecisionMessageHeader *hdcsn = cls;
+  struct Channel *chn = channel;
+
+  const struct MulticastJoinDecisionMessage *dcsn =
+    (struct MulticastJoinDecisionMessage *) &hdcsn[1];
+
+  if (0 == memcmp (&hdcsn->member_pub_key, &chn->member_pub_key, sizeof 
(chn->member_pub_key))
+      && 0 == memcmp (&hdcsn->peer, &chn->peer, sizeof (chn->peer)))
+  {
+    if (GNUNET_YES == ntohl (dcsn->is_admitted))
+    {
+      chn->join_status = JOIN_ADMITTED;
+    }
+    else
+    {
+      chn->join_status = JOIN_REFUSED;
+    }
+    cadet_send_channel (chn, &hdcsn->header);
+    return GNUNET_YES;
+  }
+
+  // return GNUNET_YES to continue the multihashmap_get iteration
+  return GNUNET_YES;
+}
+
+
+/**
+ * Send join decision to a remote peer.
+ */
+static void
+cadet_send_join_decision (struct Group *grp,
+                          const struct MulticastJoinDecisionMessageHeader 
*hdcsn)
+{
+  GNUNET_CONTAINER_multihashmap_get_multiple (channels_in, &grp->pub_key_hash,
+                                              &cadet_send_join_decision_cb,
+                                              (void *) hdcsn);
+}
+
+
+/**
+ * Iterator callback for sending a message to origin clients.
+ */
+static int
+cadet_send_cb (void *cls, const struct GNUNET_HashCode *pub_key_hash,
+               void *channel)
+{
+  const struct GNUNET_MessageHeader *msg = cls;
+  struct Channel *chn = channel;
+  if (JOIN_ADMITTED == chn->join_status)
+    cadet_send_channel (chn, msg);
+  return GNUNET_YES;
+}
+
+
+/**
+ * Send message to all connected children.
+ */
+static int
+cadet_send_children (struct GNUNET_HashCode *pub_key_hash,
+                     const struct GNUNET_MessageHeader *msg)
+{
+  int n = 0;
+  if (channels_in != NULL)
+    n += GNUNET_CONTAINER_multihashmap_get_multiple (channels_in, pub_key_hash,
+                                                     cadet_send_cb, (void *) 
msg);
+  return n;
+}
+
+
+#if 0      // unused as yet
+/**
+ * Send message to all connected parents.
+ */
+static int
+cadet_send_parents (struct GNUNET_HashCode *pub_key_hash,
+                    const struct GNUNET_MessageHeader *msg)
+{
+  int n = 0;
+  if (channels_in != NULL)
+    n += GNUNET_CONTAINER_multihashmap_get_multiple (channels_out, 
pub_key_hash,
+                                                     cadet_send_cb, (void *) 
msg);
+  return n;
+}
+#endif
+
+
+/**
+ * CADET channel connect handler.
+ *
+ * @see GNUNET_CADET_ConnectEventHandler()
+ */
+static void *
+cadet_notify_connect (void *cls,
+                      struct GNUNET_CADET_Channel *channel,
+                      const struct GNUNET_PeerIdentity *source)
+{
+  struct Channel *chn = GNUNET_malloc (sizeof (struct Channel));
+  chn->group = cls;
+  chn->channel = channel;
+  chn->direction = DIR_INCOMING;
+  chn->join_status = JOIN_NOT_ASKED;
+      
+  GNUNET_CONTAINER_multihashmap_put (channels_in, &chn->group->pub_key_hash, 
chn,
+                                     
GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
+  return chn;
+}
+
+
+/**
+ * CADET window size change handler.
+ *
+ * @see GNUNET_CADET_WindowSizeEventHandler()
+ */
+static void
+cadet_notify_window_change (void *cls,
+                            const struct GNUNET_CADET_Channel *channel,
+                            int window_size)
+{
+  struct Channel *chn = cls;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "%p Window size changed to %d.  Pending messages: %u\n",
+              chn, window_size, chn->msgs_pending);
+
+  chn->is_connected = GNUNET_YES;
+  chn->window_size = (int32_t) window_size;
+
+  for (int i = 0; i < window_size; i++)
+  {
+    if (0 < chn->msgs_pending)
+    {
+      client_send_ack (&chn->group_pub_hash);
+      chn->msgs_pending--;
+    }
+    else
+    {
+      break;
+    }
+  }
+}
+
+
+/**
+ * CADET channel disconnect handler.
+ *
+ * @see GNUNET_CADET_DisconnectEventHandler()
+ */
+static void
+cadet_notify_disconnect (void *cls,
+                         const struct GNUNET_CADET_Channel *channel)
+{
+  if (NULL == cls)
+    return;
+
+  struct Channel *chn = cls;
+  if (NULL != chn->group)
+  {
+    if (GNUNET_NO == chn->group->is_origin)
+    {
+      struct Member *mem = (struct Member *) chn->group;
+      if (chn == mem->origin_channel)
+        mem->origin_channel = NULL;
+    }
+  }
+
+  int ret;
+  do
+  {
+    ret = replay_req_remove_cadet (chn);
+  }
+  while (GNUNET_YES == ret);
+
+  GNUNET_free (chn);
+}
+
+
+static int
+check_cadet_join_request (void *cls,
+                          const struct MulticastJoinRequestMessage *req)
+{
+  struct Channel *chn = cls;
+
+  if (NULL == chn
+      || JOIN_NOT_ASKED != chn->join_status)
+  {
+    return GNUNET_SYSERR;
+  }
+
+  uint16_t size = ntohs (req->header.size);
+  if (size < sizeof (*req))
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  if (ntohl (req->purpose.size) != (size
+                                    - sizeof (req->header)
+                                    - sizeof (req->reserved)
+                                    - sizeof (req->signature)))
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  if (GNUNET_OK !=
+      GNUNET_CRYPTO_ecdsa_verify (GNUNET_SIGNATURE_PURPOSE_MULTICAST_REQUEST,
+                                  &req->purpose, &req->signature,
+                                  &req->member_pub_key))
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+
+  return GNUNET_OK;
+}
+
+
+/**
+ * Incoming join request message from CADET.
+ */
+static void
+handle_cadet_join_request (void *cls,
+                           const struct MulticastJoinRequestMessage *req)
+{
+  struct Channel *chn = cls;
+  GNUNET_CADET_receive_done (chn->channel);
+
+  struct GNUNET_HashCode group_pub_hash;
+  GNUNET_CRYPTO_hash (&req->group_pub_key, sizeof (req->group_pub_key), 
&group_pub_hash);
+  chn->group_pub_key = req->group_pub_key;
+  chn->group_pub_hash = group_pub_hash;
+  chn->member_pub_key = req->member_pub_key;
+  chn->peer = req->peer;
+  chn->join_status = JOIN_WAITING;
+
+  client_send_all (&group_pub_hash,
+                   GNUNET_MQ_msg_copy (&req->header));
+}
+
+
+static int
+check_cadet_join_decision (void *cls,
+                           const struct MulticastJoinDecisionMessageHeader 
*hdcsn)
+{
+  uint16_t size = ntohs (hdcsn->header.size);
+  if (size < sizeof (struct MulticastJoinDecisionMessageHeader) +
+             sizeof (struct MulticastJoinDecisionMessage))
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+
+  struct Channel *chn = cls;
+  if (NULL == chn)
+  {
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+  if (NULL == chn->group || GNUNET_NO != chn->group->is_origin)
+  {
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+  switch (chn->join_status)
+  {
+  case JOIN_REFUSED:
+    return GNUNET_SYSERR;
+
+  case JOIN_ADMITTED:
+    return GNUNET_OK;
+
+  case JOIN_NOT_ASKED:
+  case JOIN_WAITING:
+    break;
+  }
+
+  return GNUNET_OK;
+}
+
+
+/**
+ * Incoming join decision message from CADET.
+ */
+static void
+handle_cadet_join_decision (void *cls,
+                            const struct MulticastJoinDecisionMessageHeader 
*hdcsn)
+{
+  const struct MulticastJoinDecisionMessage *
+    dcsn = (const struct MulticastJoinDecisionMessage *) &hdcsn[1];
+
+  struct Channel *chn = cls;
+  GNUNET_CADET_receive_done (chn->channel);
+
+  // FIXME: do we need to copy chn->peer or compare it with hdcsn->peer?
+  struct Member *mem = (struct Member *) chn->group;
+  client_send_join_decision (mem, hdcsn);
+  if (GNUNET_YES == ntohl (dcsn->is_admitted))
+  {
+    chn->join_status = JOIN_ADMITTED;
+  }
+  else
+  {
+    chn->join_status = JOIN_REFUSED;
+    cadet_channel_destroy (chn);
+  }
+}
+
+
+static int
+check_cadet_message (void *cls,
+                     const struct GNUNET_MULTICAST_MessageHeader *msg)
+{
+  uint16_t size = ntohs (msg->header.size);
+  if (size < sizeof (*msg))
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+
+  struct Channel *chn = cls;
+  if (NULL == chn)
+  {
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+  if (ntohl (msg->purpose.size) != (size
+                                    - sizeof (msg->header)
+                                    - sizeof (msg->hop_counter)
+                                    - sizeof (msg->signature)))
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  if (GNUNET_OK !=
+      GNUNET_CRYPTO_eddsa_verify (GNUNET_SIGNATURE_PURPOSE_MULTICAST_MESSAGE,
+                                  &msg->purpose, &msg->signature,
+                                  &chn->group_pub_key))
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+
+  return GNUNET_OK;
+}
+
+
+/**
+ * Incoming multicast message from CADET.
+ */
+static void
+handle_cadet_message (void *cls,
+                      const struct GNUNET_MULTICAST_MessageHeader *msg)
+{
+  struct Channel *chn = cls;
+  GNUNET_CADET_receive_done (chn->channel);
+  client_send_all (&chn->group_pub_hash,
+                   GNUNET_MQ_msg_copy (&msg->header));
+}
+
+
+static int
+check_cadet_request (void *cls,
+                     const struct GNUNET_MULTICAST_RequestHeader *req)
+{
+  uint16_t size = ntohs (req->header.size);
+  if (size < sizeof (*req))
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+
+  struct Channel *chn = cls;
+  if (NULL == chn)
+  {
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+  if (ntohl (req->purpose.size) != (size
+                                    - sizeof (req->header)
+                                    - sizeof (req->member_pub_key)
+                                    - sizeof (req->signature)))
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  if (GNUNET_OK !=
+      GNUNET_CRYPTO_ecdsa_verify (GNUNET_SIGNATURE_PURPOSE_MULTICAST_REQUEST,
+                                  &req->purpose, &req->signature,
+                                  &req->member_pub_key))
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+
+  return GNUNET_OK;
+}
+
+
+/**
+ * Incoming multicast request message from CADET.
+ */
+static void
+handle_cadet_request (void *cls,
+                      const struct GNUNET_MULTICAST_RequestHeader *req)
+{
+  struct Channel *chn = cls;
+  GNUNET_CADET_receive_done (chn->channel);
+  client_send_origin (&chn->group_pub_hash,
+                      GNUNET_MQ_msg_copy (&req->header));
+}
+
+
+// FIXME: do checks in handle_cadet_replay_request
+//static int
+//check_cadet_replay_request (void *cls,
+//                            const struct MulticastReplayRequestMessage *req)
+//{
+//  uint16_t size = ntohs (req->header.size);
+//  if (size < sizeof (*req))
+//  {
+//    GNUNET_break_op (0);
+//    return GNUNET_SYSERR;
+//  }
+//
+//  struct Channel *chn = cls;
+//  if (NULL == chn)
+//  {
+//    GNUNET_break_op (0);
+//    return GNUNET_SYSERR;
+//  }
+//
+//  return GNUNET_OK;
+//}
+
+
+/**
+ * Incoming multicast replay request from CADET.
+ */
+static void
+handle_cadet_replay_request (void *cls,
+                             const struct MulticastReplayRequestMessage *req)
+{
+  struct Channel *chn = cls;
+
+  GNUNET_CADET_receive_done (chn->channel);
+
+  struct MulticastReplayRequestMessage rep = *req;
+  GNUNET_memcpy (&rep.member_pub_key, &chn->member_pub_key, sizeof 
(chn->member_pub_key));
+
+  struct GNUNET_CONTAINER_MultiHashMap *
+    grp_replay_req = GNUNET_CONTAINER_multihashmap_get (replay_req_cadet,
+                                                        
&chn->group->pub_key_hash);
+  if (NULL == grp_replay_req)
+  {
+    grp_replay_req = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO);
+    GNUNET_CONTAINER_multihashmap_put (replay_req_cadet,
+                                       &chn->group->pub_key_hash, 
grp_replay_req,
+                                       
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
+  }
+  struct GNUNET_HashCode key_hash;
+  replay_key_hash (rep.fragment_id,
+                   rep.message_id,
+                   rep.fragment_offset,
+                   rep.flags,
+                   &key_hash);
+  GNUNET_CONTAINER_multihashmap_put (grp_replay_req, &key_hash, chn,
+                                     
GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
+
+  client_send_random (&chn->group_pub_hash,
+                      GNUNET_MQ_msg_copy (&rep.header));
+}
+
+
+static int
+check_cadet_replay_response (void *cls,
+                             const struct MulticastReplayResponseMessage *res)
+{
+  struct Channel *chn = cls;
+  if (NULL == chn)
+  {
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+  return GNUNET_OK;
+}
+
+
+/**
+ * Incoming multicast replay response from CADET.
+ */
+static void
+handle_cadet_replay_response (void *cls,
+                              const struct MulticastReplayResponseMessage *res)
+{
+  struct Channel *chn = cls;
+  GNUNET_CADET_receive_done (chn->channel);
+
+  /* @todo FIXME: got replay error response, send request to other members */
+}
+
+
+static void
+group_set_cadet_port_hash (struct Group *grp)
+{
+  struct CadetPort {
+    struct GNUNET_CRYPTO_EddsaPublicKey pub_key;
+    uint32_t app_type;
+  } port = {
+    grp->pub_key,
+    GNUNET_APPLICATION_TYPE_MULTICAST,
+  };
+  GNUNET_CRYPTO_hash (&port, sizeof (port), &grp->cadet_port_hash);
+}
+
+
+
+/**
+ * Create new outgoing CADET channel.
+ *
+ * @param peer
+ *        Peer to connect to.
+ * @param group_pub_key
+ *        Public key of group the channel belongs to.
+ * @param group_pub_hash
+ *        Hash of @a group_pub_key.
+ *
+ * @return Channel.
+ */
+static struct Channel *
+cadet_channel_create (struct Group *grp, struct GNUNET_PeerIdentity *peer)
+{
+  struct Channel *chn = GNUNET_malloc (sizeof (*chn));
+  chn->group = grp;
+  chn->group_pub_key = grp->pub_key;
+  chn->group_pub_hash = grp->pub_key_hash;
+  chn->peer = *peer;
+  chn->direction = DIR_OUTGOING;
+  chn->is_connected = GNUNET_NO;
+  chn->join_status = JOIN_WAITING;
+
+  struct GNUNET_MQ_MessageHandler cadet_handlers[] = {
+    GNUNET_MQ_hd_var_size (cadet_message,
+                           GNUNET_MESSAGE_TYPE_MULTICAST_MESSAGE,
+                           struct GNUNET_MULTICAST_MessageHeader,
+                           chn),
+
+    GNUNET_MQ_hd_var_size (cadet_join_decision,
+                           GNUNET_MESSAGE_TYPE_MULTICAST_JOIN_DECISION,
+                           struct MulticastJoinDecisionMessageHeader,
+                           chn),
+
+    GNUNET_MQ_hd_fixed_size (cadet_replay_request,
+                             GNUNET_MESSAGE_TYPE_MULTICAST_REPLAY_REQUEST,
+                             struct MulticastReplayRequestMessage,
+                             chn),
+
+    GNUNET_MQ_hd_var_size (cadet_replay_response,
+                           GNUNET_MESSAGE_TYPE_MULTICAST_REPLAY_RESPONSE,
+                           struct MulticastReplayResponseMessage,
+                           chn),
+
+    GNUNET_MQ_handler_end ()
+  };
+
+  chn->channel = GNUNET_CADET_channel_create (cadet, chn, &chn->peer,
+                                              &grp->cadet_port_hash,
+                                              GNUNET_CADET_OPTION_RELIABLE,
+                                              cadet_notify_window_change,
+                                              cadet_notify_disconnect,
+                                              cadet_handlers);
+  GNUNET_CONTAINER_multihashmap_put (channels_out, &chn->group_pub_hash, chn,
+                                     
GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
+  return chn;
+}
+
+
+/**
+ * Destroy outgoing CADET channel.
+ */
+static void
+cadet_channel_destroy (struct Channel *chn)
+{
+  GNUNET_CADET_channel_destroy (chn->channel);
+  GNUNET_CONTAINER_multihashmap_remove_all (channels_out, 
&chn->group_pub_hash);
+  GNUNET_free (chn);
+}
+
+/**
+ * Handle a connecting client starting an origin.
+ */
+static void
+handle_client_origin_start (void *cls,
+                            const struct MulticastOriginStartMessage *msg)
+{
+  struct Client *c = cls;
+  struct GNUNET_SERVICE_Client *client = c->client;
+
+  struct GNUNET_CRYPTO_EddsaPublicKey pub_key;
+  struct GNUNET_HashCode pub_key_hash;
+
+  GNUNET_CRYPTO_eddsa_key_get_public (&msg->group_key, &pub_key);
+  GNUNET_CRYPTO_hash (&pub_key, sizeof (pub_key), &pub_key_hash);
+
+  struct Origin *
+    orig = GNUNET_CONTAINER_multihashmap_get (origins, &pub_key_hash);
+  struct Group *grp;
+
+  if (NULL == orig)
+  {
+    orig = GNUNET_new (struct Origin);
+    orig->priv_key = msg->group_key;
+    orig->max_fragment_id = GNUNET_ntohll (msg->max_fragment_id);
+
+    grp = c->group = &orig->group;
+    grp->origin = orig;
+    grp->is_origin = GNUNET_YES;
+    grp->pub_key = pub_key;
+    grp->pub_key_hash = pub_key_hash;
+    grp->is_disconnected = GNUNET_NO;
+
+    GNUNET_CONTAINER_multihashmap_put (origins, &grp->pub_key_hash, orig,
+                                       
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
+
+    group_set_cadet_port_hash (grp);
+
+    struct GNUNET_MQ_MessageHandler cadet_handlers[] = {
+      GNUNET_MQ_hd_var_size (cadet_message,
+                             GNUNET_MESSAGE_TYPE_MULTICAST_MESSAGE,
+                             struct GNUNET_MULTICAST_MessageHeader,
+                             grp),
+
+      GNUNET_MQ_hd_var_size (cadet_request,
+                             GNUNET_MESSAGE_TYPE_MULTICAST_REQUEST,
+                             struct GNUNET_MULTICAST_RequestHeader,
+                             grp),
+
+      GNUNET_MQ_hd_var_size (cadet_join_request,
+                             GNUNET_MESSAGE_TYPE_MULTICAST_JOIN_REQUEST,
+                             struct MulticastJoinRequestMessage,
+                             grp),
+
+      GNUNET_MQ_hd_fixed_size (cadet_replay_request,
+                               GNUNET_MESSAGE_TYPE_MULTICAST_REPLAY_REQUEST,
+                               struct MulticastReplayRequestMessage,
+                               grp),
+
+      GNUNET_MQ_hd_var_size (cadet_replay_response,
+                             GNUNET_MESSAGE_TYPE_MULTICAST_REPLAY_RESPONSE,
+                             struct MulticastReplayResponseMessage,
+                             grp),
+
+      GNUNET_MQ_handler_end ()
+    };
+
+
+    orig->cadet_port = GNUNET_CADET_open_port (cadet,
+                                               &grp->cadet_port_hash,
+                                               cadet_notify_connect,
+                                               grp,
+                                               cadet_notify_window_change,
+                                               cadet_notify_disconnect,
+                                               cadet_handlers);
+  }
+  else
+  {
+    grp = &orig->group;
+  }
+
+  struct ClientList *cl = GNUNET_new (struct ClientList);
+  cl->client = client;
+  GNUNET_CONTAINER_DLL_insert (grp->clients_head, grp->clients_tail, cl);
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%p Client connected as origin to group %s.\n",
+              orig, GNUNET_h2s (&grp->pub_key_hash));
+  GNUNET_SERVICE_client_continue (client);
+}
+
+
+static int
+check_client_member_join (void *cls,
+                          const struct MulticastMemberJoinMessage *msg)
+{
+  uint16_t msg_size = ntohs (msg->header.size);
+  struct GNUNET_PeerIdentity *relays = (struct GNUNET_PeerIdentity *) &msg[1];
+  uint32_t relay_count = ntohl (msg->relay_count);
+
+  if (0 != relay_count)
+  {
+    if (UINT32_MAX / relay_count < sizeof (*relays)){
+        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                      "relay_count (%lu) * sizeof (*relays)  (%lu) exceeds 
UINT32_MAX!\n",
+                (unsigned long)relay_count,
+                sizeof (*relays));
+        return GNUNET_SYSERR;
+    }
+  }
+  uint32_t relay_size = relay_count * sizeof (*relays);
+  struct GNUNET_MessageHeader *join_msg = NULL;
+  uint16_t join_msg_size = 0;
+  if (sizeof (*msg) + relay_size + sizeof (struct GNUNET_MessageHeader)
+      <= msg_size)
+  {
+    join_msg = (struct GNUNET_MessageHeader *)
+      (((char *) &msg[1]) + relay_size);
+    join_msg_size = ntohs (join_msg->size);
+    if (UINT16_MAX - join_msg_size < sizeof (struct 
MulticastJoinRequestMessage)){
+        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                    "join_msg_size (%u) + sizeof (struct 
MulticastJoinRequestMessage) (%lu) exceeds UINT16_MAX!\n",
+                (unsigned)join_msg_size,
+                (unsigned long)sizeof (struct MulticastJoinRequestMessage));
+        return GNUNET_SYSERR;
+    } 
+  }
+  if (msg_size != (sizeof (*msg) + relay_size + join_msg_size)){
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                    "msg_size does not match real size of message!\n");
+      return GNUNET_SYSERR;
+  }else{
+      return GNUNET_OK;
+  }
+}
+
+
+/**
+ * Handle a connecting client joining a group.
+ */
+static void
+handle_client_member_join (void *cls,
+                           const struct MulticastMemberJoinMessage *msg)
+{
+  struct Client *c = cls;
+  struct GNUNET_SERVICE_Client *client = c->client;
+
+  uint16_t msg_size = ntohs (msg->header.size);
+
+  struct GNUNET_CRYPTO_EcdsaPublicKey mem_pub_key;
+  struct GNUNET_HashCode pub_key_hash, mem_pub_key_hash;
+
+  GNUNET_CRYPTO_ecdsa_key_get_public (&msg->member_key, &mem_pub_key);
+  GNUNET_CRYPTO_hash (&mem_pub_key, sizeof (mem_pub_key), &mem_pub_key_hash);
+  GNUNET_CRYPTO_hash (&msg->group_pub_key, sizeof (msg->group_pub_key), 
&pub_key_hash);
+  
+  struct GNUNET_CONTAINER_MultiHashMap *
+    grp_mem = GNUNET_CONTAINER_multihashmap_get (group_members, &pub_key_hash);
+  struct Member *mem = NULL;
+  struct Group *grp;
+
+  if (NULL != grp_mem)
+  {
+    mem = GNUNET_CONTAINER_multihashmap_get (grp_mem, &mem_pub_key_hash);
+  }
+  
+  if (NULL == mem)
+  {
+    mem = GNUNET_new (struct Member);
+    mem->origin = msg->origin;
+    mem->priv_key = msg->member_key;
+    mem->pub_key = mem_pub_key;
+    mem->pub_key_hash = mem_pub_key_hash;
+    mem->max_fragment_id = 0; // FIXME
+
+    grp = c->group = &mem->group;
+    grp->member = mem;
+    grp->is_origin = GNUNET_NO;
+    grp->pub_key = msg->group_pub_key;
+    grp->pub_key_hash = pub_key_hash;
+    grp->is_disconnected = GNUNET_NO;
+    group_set_cadet_port_hash (grp);
+  
+    if (NULL == grp_mem)
+    {
+      grp_mem = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES);
+      GNUNET_CONTAINER_multihashmap_put (group_members, &grp->pub_key_hash, 
grp_mem,
+                                         
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
+    }
+    GNUNET_CONTAINER_multihashmap_put (grp_mem, &mem->pub_key_hash, mem,
+                                       
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
+   
+    // FIXME: should the members hash map have option UNIQUE_FAST?
+    GNUNET_CONTAINER_multihashmap_put (members, &grp->pub_key_hash, mem,
+                                       
GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
+  }
+  else
+  {
+    grp = &mem->group;
+  }
+
+  struct ClientList *cl = GNUNET_new (struct ClientList);
+  cl->client = client;
+  GNUNET_CONTAINER_DLL_insert (grp->clients_head, grp->clients_tail, cl);
+
+  char *str = GNUNET_CRYPTO_ecdsa_public_key_to_string (&mem->pub_key);
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Client connected to group %s as member %s (%s). size = %d\n",
+              GNUNET_h2s (&grp->pub_key_hash),
+              GNUNET_h2s2 (&mem->pub_key_hash),
+              str,
+              GNUNET_CONTAINER_multihashmap_size (members));
+  GNUNET_free (str);
+
+  if (NULL != mem->join_dcsn)
+  { /* Already got a join decision, send it to client. */
+    struct GNUNET_MQ_Envelope *
+      env = GNUNET_MQ_msg_copy (&mem->join_dcsn->header);
+
+    GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client),
+                    env);
+  }
+  else
+  { /* First client of the group, send join request. */
+    struct GNUNET_PeerIdentity *relays = (struct GNUNET_PeerIdentity *) 
&msg[1];
+    uint32_t relay_count = ntohl (msg->relay_count);
+    uint16_t relay_size = relay_count * sizeof (*relays);
+    struct GNUNET_MessageHeader *join_msg = NULL;
+    uint16_t join_msg_size = 0;
+    if (sizeof (*msg) + relay_size + sizeof (struct GNUNET_MessageHeader)
+        <= msg_size)
+    {
+      join_msg = (struct GNUNET_MessageHeader *)
+        (((char *) &msg[1]) + relay_size);
+      join_msg_size = ntohs (join_msg->size);
+    }
+
+    uint16_t req_msg_size = sizeof (struct MulticastJoinRequestMessage) + 
join_msg_size;
+    struct MulticastJoinRequestMessage *
+      req = GNUNET_malloc (req_msg_size);
+    req->header.size = htons (req_msg_size);
+    req->header.type = htons (GNUNET_MESSAGE_TYPE_MULTICAST_JOIN_REQUEST);
+    req->group_pub_key = grp->pub_key;
+    req->peer = this_peer;
+    GNUNET_CRYPTO_ecdsa_key_get_public (&mem->priv_key, &req->member_pub_key);
+    if (0 < join_msg_size)
+      GNUNET_memcpy (&req[1], join_msg, join_msg_size);
+
+    req->member_pub_key = mem->pub_key;
+    req->purpose.size = htonl (req_msg_size
+                               - sizeof (req->header)
+                               - sizeof (req->reserved)
+                               - sizeof (req->signature));
+    req->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_MULTICAST_REQUEST);
+
+    if (GNUNET_OK != GNUNET_CRYPTO_ecdsa_sign (&mem->priv_key, &req->purpose,
+                                               &req->signature))
+    {
+      /* FIXME: handle error */
+      GNUNET_assert (0);
+    }
+
+    if (NULL != mem->join_req)
+      GNUNET_free (mem->join_req);
+    mem->join_req = req;
+
+    if (0 ==
+        client_send_origin (&grp->pub_key_hash,
+                            GNUNET_MQ_msg_copy (&mem->join_req->header)))
+    { /* No local origins, send to remote origin */
+      cadet_send_join_request (mem);
+    }
+  }
+  GNUNET_SERVICE_client_continue (client);
+}
+
+
+static void
+client_send_join_decision (struct Member *mem,
+                           const struct MulticastJoinDecisionMessageHeader 
*hdcsn)
+{
+  client_send_group (&mem->group, GNUNET_MQ_msg_copy (&hdcsn->header));
+
+  const struct MulticastJoinDecisionMessage *
+    dcsn = (const struct MulticastJoinDecisionMessage *) &hdcsn[1];
+  if (GNUNET_YES == ntohl (dcsn->is_admitted))
+  { /* Member admitted, store join_decision. */
+    uint16_t dcsn_size = ntohs (dcsn->header.size);
+    mem->join_dcsn = GNUNET_malloc (dcsn_size);
+    GNUNET_memcpy (mem->join_dcsn, dcsn, dcsn_size);
+  }
+  else
+  { /* Refused entry, but replay would be still possible for past members. */
+  }
+}
+
+
+static int
+check_client_join_decision (void *cls,
+                            const struct MulticastJoinDecisionMessageHeader 
*hdcsn)
+{
+  return GNUNET_OK;
+}
+
+
+/**
+ * Join decision from client.
+ */
+static void
+handle_client_join_decision (void *cls,
+                             const struct MulticastJoinDecisionMessageHeader 
*hdcsn)
+{
+  struct Client *c = cls;
+  struct GNUNET_SERVICE_Client *client = c->client;
+  struct Group *grp = c->group;
+
+  if (NULL == grp)
+  {
+    GNUNET_break (0);
+    GNUNET_SERVICE_client_drop (client);
+    return;
+  }
+  GNUNET_assert (GNUNET_NO == grp->is_disconnected);
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%p got join decision from client for group %s..\n",
+              grp, GNUNET_h2s (&grp->pub_key_hash));
+
+  struct GNUNET_CONTAINER_MultiHashMap *
+    grp_mem = GNUNET_CONTAINER_multihashmap_get (group_members,
+                                                 &grp->pub_key_hash);
+  struct Member *mem = NULL;
+  if (NULL != grp_mem)
+  {
+    struct GNUNET_HashCode member_key_hash;
+    GNUNET_CRYPTO_hash (&hdcsn->member_pub_key, sizeof (hdcsn->member_pub_key),
+                        &member_key_hash);
+    mem = GNUNET_CONTAINER_multihashmap_get (grp_mem, &member_key_hash);
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "%p ..and member %s: %p\n",
+                grp, GNUNET_h2s (&member_key_hash), mem);
+  }
+  
+  if (NULL != mem) 
+  { /* Found local member */
+    client_send_join_decision (mem, hdcsn);
+  }
+  else
+  { /* Look for remote member */
+    cadet_send_join_decision (grp, hdcsn);
+  }
+  GNUNET_SERVICE_client_continue (client);
+}
+
+
+static void
+handle_client_part_request (void *cls,
+                            const struct GNUNET_MessageHeader *msg)
+{
+  struct Client *c = cls;
+  struct GNUNET_SERVICE_Client *client = c->client;
+  struct Group *grp = c->group;
+  struct GNUNET_MQ_Envelope *env;
+
+  if (NULL == grp)
+  {
+    GNUNET_break (0);
+    GNUNET_SERVICE_client_drop (client);
+    return;
+  }
+  GNUNET_assert (GNUNET_NO == grp->is_disconnected);
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%p got part request from client for group %s.\n",
+              grp, GNUNET_h2s (&grp->pub_key_hash));
+  grp->is_disconnected = GNUNET_YES;
+  env = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_MULTICAST_PART_ACK);
+  client_send_group (grp, env);
+  GNUNET_SERVICE_client_continue (client);
+}
+
+
+static int
+check_client_multicast_message (void *cls,
+                                const struct GNUNET_MULTICAST_MessageHeader 
*msg)
+{
+  return GNUNET_OK;
+}
+
+
+/**
+ * Incoming message from a client.
+ */
+static void
+handle_client_multicast_message (void *cls,
+                                 const struct GNUNET_MULTICAST_MessageHeader 
*msg)
+{
+  // FIXME: what if GNUNET_YES == grp->is_disconnected? Do we allow sending 
messages?
+  struct Client *c = cls;
+  struct GNUNET_SERVICE_Client *client = c->client;
+  struct Group *grp = c->group;
+
+  if (NULL == grp)
+  {
+    GNUNET_break (0);
+    GNUNET_SERVICE_client_drop (client);
+    return;
+  }
+  GNUNET_assert (GNUNET_YES == grp->is_origin);
+  struct Origin *orig = grp->origin;
+
+  // FIXME: use GNUNET_MQ_msg_copy
+  /* FIXME: yucky, should use separate message structs for P2P and CS! */
+  struct GNUNET_MULTICAST_MessageHeader *
+    out = (struct GNUNET_MULTICAST_MessageHeader *) GNUNET_copy_message 
(&msg->header);
+  out->fragment_id = GNUNET_htonll (++orig->max_fragment_id);
+  out->purpose.size = htonl (ntohs (out->header.size)
+                             - sizeof (out->header)
+                             - sizeof (out->hop_counter)
+                             - sizeof (out->signature));
+  out->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_MULTICAST_MESSAGE);
+
+  if (GNUNET_OK != GNUNET_CRYPTO_eddsa_sign (&orig->priv_key, &out->purpose,
+                                             &out->signature))
+  {
+    GNUNET_assert (0);
+  }
+
+  client_send_all (&grp->pub_key_hash, GNUNET_MQ_msg_copy (&out->header));
+  cadet_send_children (&grp->pub_key_hash, &out->header);
+  client_send_ack (&grp->pub_key_hash);
+  GNUNET_free (out);
+
+  GNUNET_SERVICE_client_continue (client);
+}
+
+
+static int
+check_client_multicast_request (void *cls,
+                                const struct GNUNET_MULTICAST_RequestHeader 
*req)
+{
+  return GNUNET_OK;
+}
+
+
+/**
+ * Incoming request from a client.
+ */
+static void
+handle_client_multicast_request (void *cls,
+                                 const struct GNUNET_MULTICAST_RequestHeader 
*req)
+{
+  struct Client *c = cls;
+  struct GNUNET_SERVICE_Client *client = c->client;
+  struct Group *grp = c->group;
+
+  if (NULL == grp)
+  {
+    GNUNET_break (0);
+    GNUNET_SERVICE_client_drop (client);
+    return;
+  }
+  GNUNET_assert (GNUNET_NO == grp->is_disconnected);
+  GNUNET_assert (GNUNET_NO == grp->is_origin);
+  struct Member *mem = grp->member;
+
+  /* FIXME: yucky, should use separate message structs for P2P and CS! */
+  struct GNUNET_MULTICAST_RequestHeader *
+    out = (struct GNUNET_MULTICAST_RequestHeader *) GNUNET_copy_message 
(&req->header);
+  out->member_pub_key = mem->pub_key;
+  out->fragment_id = GNUNET_ntohll (++mem->max_fragment_id);
+  out->purpose.size = htonl (ntohs (out->header.size)
+                             - sizeof (out->header)
+                             - sizeof (out->member_pub_key)
+                             - sizeof (out->signature));
+  out->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_MULTICAST_REQUEST);
+
+  if (GNUNET_OK != GNUNET_CRYPTO_ecdsa_sign (&mem->priv_key, &out->purpose,
+                                             &out->signature))
+  {
+    GNUNET_assert (0);
+  }
+
+  uint8_t send_ack = GNUNET_YES;
+  if (0 ==
+      client_send_origin (&grp->pub_key_hash,
+                          GNUNET_MQ_msg_copy (&out->header)))
+  { /* No local origins, send to remote origin */
+    if (NULL != mem->origin_channel)
+    {
+      cadet_send_channel (mem->origin_channel, &out->header);
+      send_ack = GNUNET_NO;
+    }
+    else
+    {
+      /* FIXME: not yet connected to origin */
+      GNUNET_SERVICE_client_drop (client);
+      GNUNET_free (out);
+      return;
+    }
+  }
+  if (GNUNET_YES == send_ack)
+  {
+    client_send_ack (&grp->pub_key_hash);
+  }
+  GNUNET_free (out);
+  GNUNET_SERVICE_client_continue (client);
+}
+
+
+/**
+ * Incoming replay request from a client.
+ */
+static void
+handle_client_replay_request (void *cls,
+                              const struct MulticastReplayRequestMessage *rep)
+{
+  struct Client *c = cls;
+  struct GNUNET_SERVICE_Client *client = c->client;
+  struct Group *grp = c->group;
+
+  if (NULL == grp)
+  {
+    GNUNET_break (0);
+    GNUNET_SERVICE_client_drop (client);
+    return;
+  }
+  GNUNET_assert (GNUNET_NO == grp->is_disconnected);
+  GNUNET_assert (GNUNET_NO == grp->is_origin);
+  struct Member *mem = grp->member;
+
+  struct GNUNET_CONTAINER_MultiHashMap *
+    grp_replay_req = GNUNET_CONTAINER_multihashmap_get (replay_req_client,
+                                                        &grp->pub_key_hash);
+  if (NULL == grp_replay_req)
+  {
+    grp_replay_req = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO);
+    GNUNET_CONTAINER_multihashmap_put (replay_req_client,
+                                       &grp->pub_key_hash, grp_replay_req,
+                                       
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
+  }
+
+  struct GNUNET_HashCode key_hash;
+  replay_key_hash (rep->fragment_id, rep->message_id, rep->fragment_offset,
+                   rep->flags, &key_hash);
+  GNUNET_CONTAINER_multihashmap_put (grp_replay_req, &key_hash, client,
+                                     
GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
+
+  if (0 ==
+      client_send_origin (&grp->pub_key_hash,
+                          GNUNET_MQ_msg_copy (&rep->header)))
+  { /* No local origin, replay from remote members / origin. */
+    if (NULL != mem->origin_channel)
+    {
+      cadet_send_channel (mem->origin_channel, &rep->header);
+    }
+    else
+    {
+      /* FIXME: not yet connected to origin */
+      
+      GNUNET_assert (0);
+      GNUNET_SERVICE_client_drop (client);
+      return;
+    }
+  }
+  GNUNET_SERVICE_client_continue (client);
+}
+
+
+static int
+cadet_send_replay_response_cb (void *cls,
+                               const struct GNUNET_HashCode *key_hash,
+                               void *value)
+{
+  struct Channel *chn = value;
+  struct GNUNET_MessageHeader *msg = cls;
+
+  cadet_send_channel (chn, msg);
+  return GNUNET_OK;
+}
+
+
+static int
+client_send_replay_response_cb (void *cls,
+                                const struct GNUNET_HashCode *key_hash,
+                                void *value)
+{
+  struct GNUNET_SERVICE_Client *client = value;
+  struct GNUNET_MessageHeader *msg = cls;
+
+  client_send (client, msg);
+  return GNUNET_OK;
+}
+
+
+static int
+check_client_replay_response_end (void *cls,
+                                  const struct MulticastReplayResponseMessage 
*res)
+{
+  return GNUNET_OK;
+}
+
+
+/**
+ * End of replay response from a client.
+ */
+static void
+handle_client_replay_response_end (void *cls,
+                                   const struct MulticastReplayResponseMessage 
*res)
+{
+  struct Client *c = cls;
+  struct GNUNET_SERVICE_Client *client = c->client;
+  struct Group *grp = c->group;
+
+  if (NULL == grp)
+  {
+    GNUNET_break (0);
+    GNUNET_SERVICE_client_drop (client);
+    return;
+  }
+  GNUNET_assert (GNUNET_NO == grp->is_disconnected);
+
+  struct GNUNET_HashCode key_hash;
+  replay_key_hash (res->fragment_id, res->message_id, res->fragment_offset,
+                   res->flags, &key_hash);
+
+  struct GNUNET_CONTAINER_MultiHashMap *
+    grp_replay_req_cadet = GNUNET_CONTAINER_multihashmap_get (replay_req_cadet,
+                                                              
&grp->pub_key_hash);
+  if (NULL != grp_replay_req_cadet)
+  {
+    GNUNET_CONTAINER_multihashmap_remove_all (grp_replay_req_cadet, &key_hash);
+  }
+  struct GNUNET_CONTAINER_MultiHashMap *
+    grp_replay_req_client = GNUNET_CONTAINER_multihashmap_get 
(replay_req_client,
+                                                               
&grp->pub_key_hash);
+  if (NULL != grp_replay_req_client)
+  {
+    GNUNET_CONTAINER_multihashmap_remove_all (grp_replay_req_client, 
&key_hash);
+  }
+  GNUNET_SERVICE_client_continue (client);
+}
+
+
+static int
+check_client_replay_response (void *cls,
+                              const struct MulticastReplayResponseMessage *res)
+{
+  const struct GNUNET_MessageHeader *msg;
+  if (GNUNET_MULTICAST_REC_OK == res->error_code)
+  {
+    msg = GNUNET_MQ_extract_nested_mh (res);
+    if (NULL == msg)
+    {
+      return GNUNET_SYSERR;
+    }
+  }
+  return GNUNET_OK;
+}
+
+
+/**
+ * Incoming replay response from a client.
+ *
+ * Respond with a multicast message on success, or otherwise with an error 
code.
+ */
+static void
+handle_client_replay_response (void *cls,
+                               const struct MulticastReplayResponseMessage 
*res)
+{
+  struct Client *c = cls;
+  struct GNUNET_SERVICE_Client *client = c->client;
+  struct Group *grp = c->group;
+
+  if (NULL == grp)
+  {
+    GNUNET_break (0);
+    GNUNET_SERVICE_client_drop (client);
+    return;
+  }
+  GNUNET_assert (GNUNET_NO == grp->is_disconnected);
+
+  const struct GNUNET_MessageHeader *msg = &res->header;
+  if (GNUNET_MULTICAST_REC_OK == res->error_code)
+  {
+    msg = GNUNET_MQ_extract_nested_mh (res);
+  }
+
+  struct GNUNET_HashCode key_hash;
+  replay_key_hash (res->fragment_id, res->message_id, res->fragment_offset,
+                   res->flags, &key_hash);
+
+  struct GNUNET_CONTAINER_MultiHashMap *
+    grp_replay_req_cadet = GNUNET_CONTAINER_multihashmap_get (replay_req_cadet,
+                                                              
&grp->pub_key_hash);
+  if (NULL != grp_replay_req_cadet)
+  {
+    GNUNET_CONTAINER_multihashmap_get_multiple (grp_replay_req_cadet, 
&key_hash,
+                                                cadet_send_replay_response_cb,
+                                                (void *) msg);
+  }
+  if (GNUNET_MULTICAST_REC_OK == res->error_code)
+  {
+    struct GNUNET_CONTAINER_MultiHashMap *
+      grp_replay_req_client = GNUNET_CONTAINER_multihashmap_get 
(replay_req_client,
+                                                                 
&grp->pub_key_hash);
+    if (NULL != grp_replay_req_client)
+    {
+      GNUNET_CONTAINER_multihashmap_get_multiple (grp_replay_req_client, 
&key_hash,
+                                                  
client_send_replay_response_cb,
+                                                  (void *) msg);
+    }
+  }
+  else
+  {
+    handle_client_replay_response_end (c, res);
+    return;
+  }
+  GNUNET_SERVICE_client_continue (client);
+}
+
+
+/**
+ * A new client connected.
+ *
+ * @param cls NULL
+ * @param client client to add
+ * @param mq message queue for @a client
+ * @return @a client
+ */
+static void *
+client_notify_connect (void *cls,
+                       struct GNUNET_SERVICE_Client *client,
+                       struct GNUNET_MQ_Handle *mq)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Client connected: %p\n", client);
+  /* FIXME: send connect ACK */
+
+  struct Client *c = GNUNET_new (struct Client);
+  c->client = client;
+
+  return c;
+}
+
+
+/**
+ * Called whenever a client is disconnected.
+ * Frees our resources associated with that client.
+ *
+ * @param cls closure
+ * @param client identification of the client
+ * @param app_ctx must match @a client
+ */
+static void
+client_notify_disconnect (void *cls,
+                          struct GNUNET_SERVICE_Client *client,
+                          void *app_ctx)
+{
+  struct Client *c = app_ctx;
+  struct Group *grp = c->group;
+  GNUNET_free (c);
+
+  if (NULL == grp)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "%p User context is NULL in client_disconnect()\n", grp);
+    GNUNET_break (0);
+    return;
+  }
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%p Client (%s) disconnected from group %s\n",
+              grp, (GNUNET_YES == grp->is_origin) ? "origin" : "member",
+              GNUNET_h2s (&grp->pub_key_hash));
+
+  // FIXME (due to protocol change): here we must not remove all clients,
+  // only the one we were notified about!
+  struct ClientList *cl = grp->clients_head;
+  while (NULL != cl)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "iterating clients for group %p\n",
+                grp);
+    if (cl->client == client)
+    {
+      GNUNET_CONTAINER_DLL_remove (grp->clients_head, grp->clients_tail, cl);
+      GNUNET_free (cl);
+      break;
+    }
+    cl = cl->next;
+  }
+
+  while (GNUNET_YES == replay_req_remove_client (grp, client));
+
+  if (NULL == grp->clients_head)
+  { /* Last client disconnected. */
+    cleanup_group (grp);
+  }
+}
+
+
+/**
+ * Service started.
+ *
+ * @param cls closure
+ * @param server the initialized server
+ * @param cfg configuration to use
+ */
+static void
+run (void *cls,
+     const struct GNUNET_CONFIGURATION_Handle *c,
+     struct GNUNET_SERVICE_Handle *svc)
+{
+  cfg = c;
+  service = svc;
+  GNUNET_CRYPTO_get_peer_identity (cfg, &this_peer);
+
+  stats = GNUNET_STATISTICS_create ("multicast", cfg);
+  origins = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES);
+  members = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES);
+  group_members = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO);
+  channels_in = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES);
+  channels_out = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES);
+  replay_req_cadet = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO);
+  replay_req_client = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO);
+
+  cadet = GNUNET_CADET_connect (cfg);
+
+  GNUNET_assert (NULL != cadet);
+
+  GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
+                                NULL);
+}
+
+
+/**
+ * Define "main" method using service macro.
+ */
+GNUNET_SERVICE_MAIN
+("multicast",
+ GNUNET_SERVICE_OPTION_NONE,
+ &run,
+ &client_notify_connect,
+ &client_notify_disconnect,
+ NULL,
+ GNUNET_MQ_hd_fixed_size (client_origin_start,
+                          GNUNET_MESSAGE_TYPE_MULTICAST_ORIGIN_START,
+                          struct MulticastOriginStartMessage,
+                          NULL),
+ GNUNET_MQ_hd_var_size (client_member_join,
+                        GNUNET_MESSAGE_TYPE_MULTICAST_MEMBER_JOIN,
+                        struct MulticastMemberJoinMessage,
+                        NULL),
+ GNUNET_MQ_hd_var_size (client_join_decision,
+                        GNUNET_MESSAGE_TYPE_MULTICAST_JOIN_DECISION,
+                        struct MulticastJoinDecisionMessageHeader,
+                        NULL),
+ GNUNET_MQ_hd_fixed_size (client_part_request,
+                          GNUNET_MESSAGE_TYPE_MULTICAST_PART_REQUEST,
+                          struct GNUNET_MessageHeader,
+                          NULL),
+ GNUNET_MQ_hd_var_size (client_multicast_message,
+                        GNUNET_MESSAGE_TYPE_MULTICAST_MESSAGE,
+                        struct GNUNET_MULTICAST_MessageHeader,
+                        NULL),
+ GNUNET_MQ_hd_var_size (client_multicast_request,
+                        GNUNET_MESSAGE_TYPE_MULTICAST_REQUEST,
+                        struct GNUNET_MULTICAST_RequestHeader,
+                        NULL),
+ GNUNET_MQ_hd_fixed_size (client_replay_request,
+                          GNUNET_MESSAGE_TYPE_MULTICAST_REPLAY_REQUEST,
+                          struct MulticastReplayRequestMessage,
+                          NULL),
+ GNUNET_MQ_hd_var_size (client_replay_response,
+                        GNUNET_MESSAGE_TYPE_MULTICAST_REPLAY_RESPONSE,
+                        struct MulticastReplayResponseMessage,
+                        NULL),
+ GNUNET_MQ_hd_var_size (client_replay_response_end,
+                        GNUNET_MESSAGE_TYPE_MULTICAST_REPLAY_RESPONSE_END,
+                        struct MulticastReplayResponseMessage,
+                        NULL));
+
+/* end of gnunet-service-multicast.c */
diff --git a/src/multicast/multicast.conf.in b/src/multicast/multicast.conf.in
new file mode 100644
index 0000000..97a5413
--- /dev/null
+++ b/src/multicast/multicast.conf.in
@@ -0,0 +1,22 @@
+[multicast]
+START_ON_DEMAND = @START_ON_DEMAND@
+BINARY = gnunet-service-multicast
+
+UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-multicast.sock
+UNIX_MATCH_UID = YES
+UNIX_MATCH_GID = YES
+
address@hidden@PORT = 2109
+HOSTNAME = localhost
+ACCEPT_FROM = 127.0.0.1;
+ACCEPT_FROM6 = ::1;
+
+# DISABLE_SOCKET_FORWARDING = NO
+# USERNAME = 
+# MAXBUF =
+# TIMEOUT =
+# DISABLEV6 =
+# BINDTO =
+# REJECT_FROM =
+# REJECT_FROM6 =
+# PREFIX =
diff --git a/src/multicast/multicast.h b/src/multicast/multicast.h
new file mode 100644
index 0000000..8a3ca14
--- /dev/null
+++ b/src/multicast/multicast.h
@@ -0,0 +1,303 @@
+/*
+     This file is part of GNUnet.
+     Copyright (C) 2012, 2013 GNUnet e.V.
+
+     GNUnet is free software: you can redistribute it and/or modify it
+     under the terms of the GNU Affero General Public License as published
+     by the Free Software Foundation, either version 3 of the License,
+     or (at your option) any later version.
+
+     GNUnet 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
+     Affero General Public License for more details.
+    
+     You should have received a copy of the GNU Affero General Public License
+     along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+     SPDX-License-Identifier: AGPL3.0-or-later
+*/
+
+/**
+ * @file multicast/multicast.h
+ * @brief multicast IPC messages
+ * @author Christian Grothoff
+ * @author Gabor X Toth
+ */
+#ifndef MULTICAST_H
+#define MULTICAST_H
+
+#include "platform.h"
+#include "gnunet_multicast_service.h"
+
+GNUNET_NETWORK_STRUCT_BEGIN
+
+
+/**
+ * Header of a join request sent to the origin or another member.
+ */
+struct MulticastJoinRequestMessage
+{
+  /**
+   * Type: GNUNET_MESSAGE_TYPE_MULTICAST_JOIN_REQUEST
+   */
+  struct GNUNET_MessageHeader header;
+
+  /**
+   * Always zero.
+   */
+  uint32_t reserved;
+
+  /**
+   * ECC signature of the rest of the fields of the join request.
+   *
+   * Signature must match the public key of the joining member.
+   */
+  struct GNUNET_CRYPTO_EcdsaSignature signature;
+
+  /**
+   * Purpose for the signature and size of the signed data.
+   */
+  struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
+
+  /**
+   * Public key of the target group.
+   */
+  struct GNUNET_CRYPTO_EddsaPublicKey group_pub_key;
+
+  /**
+   * Public key of the joining member.
+   */
+  struct GNUNET_CRYPTO_EcdsaPublicKey member_pub_key;
+
+  /**
+   * Peer identity of the joining member.
+   */
+  struct GNUNET_PeerIdentity peer;
+
+  /* Followed by struct GNUNET_MessageHeader join_message */
+};
+
+
+/**
+ * Header of a join decision message sent to a peer requesting join.
+ */
+struct MulticastJoinDecisionMessage
+{
+  /**
+   * Type: GNUNET_MESSAGE_TYPE_MULTICAST_JOIN_DECISION
+   */
+  struct GNUNET_MessageHeader header;
+
+  /**
+   * #GNUNET_YES    if the peer was admitted
+   * #GNUNET_NO     if entry was refused,
+   * #GNUNET_SYSERR if the request could not be answered.
+   */
+  int32_t is_admitted;
+
+  /**
+   * Number of relays given.
+   */
+  uint32_t relay_count;
+
+  /* Followed by relay_count peer identities */
+
+  /* Followed by the join response message */
+};
+
+
+/**
+ * Header added to a struct MulticastJoinDecisionMessage
+ * when sent between the client and service.
+ */
+struct MulticastJoinDecisionMessageHeader
+{
+  /**
+   * Type: GNUNET_MESSAGE_TYPE_MULTICAST_JOIN_DECISION
+   */
+  struct GNUNET_MessageHeader header;
+
+  /**
+   * C->S: Peer to send the join decision to.
+   * S->C: Peer we received the join decision from.
+   */
+  struct GNUNET_PeerIdentity peer;
+
+  /**
+   * C->S: Public key of the member requesting join.
+   * S->C: Unused.
+   */
+  struct GNUNET_CRYPTO_EcdsaPublicKey member_pub_key;
+
+  /* Followed by struct MulticastJoinDecisionMessage */
+};
+
+
+/**
+ * Message sent from the client to the service to notify the service
+ * about the result of a membership test.
+ */
+struct MulticastMembershipTestResultMessage
+{
+  /**
+   * Type: GNUNET_MESSAGE_TYPE_MULTICAST_MEMBERSHIP_TEST_RESULT
+   */
+  struct GNUNET_MessageHeader header;
+
+  /**
+   * Unique ID that identifies the associated membership test.
+   */
+  uint32_t uid;
+
+  /**
+   * #GNUNET_YES    if the peer is a member
+   * #GNUNET_NO     if peer is not a member,
+   * #GNUNET_SYSERR if the test could not be answered.
+   */
+  int32_t is_admitted;
+};
+
+
+/**
+ * Message sent from the client to the service OR the service to the
+ * client asking for a message fragment to be replayed.
+ */
+struct MulticastReplayRequestMessage
+{
+
+  /**
+   * The message type should be
+   * #GNUNET_MESSAGE_TYPE_MULTICAST_REPLAY_REQUEST.
+   */
+  struct GNUNET_MessageHeader header;
+
+  /**
+   * S->C: Public key of the member requesting replay.
+   * C->S: Unused.
+   */
+  struct GNUNET_CRYPTO_EcdsaPublicKey member_pub_key;
+
+  /**
+   * ID of the message that is being requested.
+   */
+  uint64_t fragment_id;
+
+  /**
+   * ID of the message that is being requested.
+   */
+  uint64_t message_id;
+
+  /**
+   * Offset of the fragment that is being requested.
+   */
+  uint64_t fragment_offset;
+
+  /**
+   * Additional flags for the request.
+   */
+  uint64_t flags;
+
+  /**
+   * Replay request ID.
+   */
+  uint32_t uid;
+};
+
+
+/**
+ * Message sent from the client to the service to give the service
+ * a replayed message.
+ */
+struct MulticastReplayResponseMessage
+{
+
+  /**
+   * Type: GNUNET_MESSAGE_TYPE_MULTICAST_REPLAY_RESPONSE
+   *    or GNUNET_MESSAGE_TYPE_MULTICAST_REPLAY_RESPONSE_END
+   */
+  struct GNUNET_MessageHeader header;
+
+  /**
+   * ID of the message that is being requested.
+   */
+  uint64_t fragment_id;
+
+  /**
+   * ID of the message that is being requested.
+   */
+  uint64_t message_id;
+
+  /**
+   * Offset of the fragment that is being requested.
+   */
+  uint64_t fragment_offset;
+
+  /**
+   * Additional flags for the request.
+   */
+  uint64_t flags;
+
+  /**
+   * An `enum GNUNET_MULTICAST_ReplayErrorCode` identifying issues (in NBO).
+   */
+  int32_t error_code;
+
+  /* followed by replayed message */
+};
+
+
+/**
+ * Message sent from the client to the service to notify the service
+ * about the starting of a multicast group with this peers as its origin.
+ */
+struct MulticastOriginStartMessage
+{
+  /**
+   * Type: GNUNET_MESSAGE_TYPE_MULTICAST_ORIGIN_START
+   */
+  struct GNUNET_MessageHeader header;
+
+  /**
+   * Always zero.
+   */
+  uint32_t reserved;
+
+  /**
+   * Private, non-ephemeral key for the multicast group.
+   */
+  struct GNUNET_CRYPTO_EddsaPrivateKey group_key;
+
+  /**
+   * Last fragment ID sent to the group, used to continue counting fragments if
+   * we resume operating * a group.
+   */
+  uint64_t max_fragment_id;
+};
+
+
+struct MulticastMemberJoinMessage
+{
+  /**
+   * Type: GNUNET_MESSAGE_TYPE_MULTICAST_MEMBER_JOIN
+   */
+  struct GNUNET_MessageHeader header;
+
+  uint32_t relay_count GNUNET_PACKED;
+
+  struct GNUNET_CRYPTO_EddsaPublicKey group_pub_key;
+
+  struct GNUNET_CRYPTO_EcdsaPrivateKey member_key;
+
+  struct GNUNET_PeerIdentity origin;
+
+  /* Followed by struct GNUNET_PeerIdentity relays[relay_count] */
+
+  /* Followed by struct GNUNET_MessageHeader join_msg */
+};
+
+
+GNUNET_NETWORK_STRUCT_END
+
+#endif
+/* end of multicast.h */
diff --git a/src/multicast/multicast_api.c b/src/multicast/multicast_api.c
new file mode 100644
index 0000000..e5e8302
--- /dev/null
+++ b/src/multicast/multicast_api.c
@@ -0,0 +1,1399 @@
+/*
+     This file is part of GNUnet.
+     Copyright (C) 2012, 2013 GNUnet e.V.
+
+     GNUnet is free software: you can redistribute it and/or modify it
+     under the terms of the GNU Affero General Public License as published
+     by the Free Software Foundation, either version 3 of the License,
+     or (at your option) any later version.
+
+     GNUnet 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
+     Affero General Public License for more details.
+    
+     You should have received a copy of the GNU Affero General Public License
+     along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+     SPDX-License-Identifier: AGPL3.0-or-later
+*/
+
+/**
+ * @file multicast/multicast_api.c
+ * @brief Multicast service; implements multicast groups using CADET 
connections.
+ * @author Christian Grothoff
+ * @author Gabor X Toth
+ */
+
+#include "platform.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_multicast_service.h"
+#include "multicast.h"
+
+#define LOG(kind,...) GNUNET_log_from (kind, "multicast-api",__VA_ARGS__)
+
+
+/**
+ * Handle for a request to send a message to all multicast group members
+ * (from the origin).
+ */
+struct GNUNET_MULTICAST_OriginTransmitHandle
+{
+  GNUNET_MULTICAST_OriginTransmitNotify notify;
+  void *notify_cls;
+  struct GNUNET_MULTICAST_Origin *origin;
+
+  uint64_t message_id;
+  uint64_t group_generation;
+  uint64_t fragment_offset;
+};
+
+
+/**
+ * Handle for a message to be delivered from a member to the origin.
+ */
+struct GNUNET_MULTICAST_MemberTransmitHandle
+{
+  GNUNET_MULTICAST_MemberTransmitNotify notify;
+  void *notify_cls;
+  struct GNUNET_MULTICAST_Member *member;
+
+  uint64_t request_id;
+  uint64_t fragment_offset;
+};
+
+
+struct GNUNET_MULTICAST_Group
+{
+  /**
+   * Configuration to use.
+   */
+  const struct GNUNET_CONFIGURATION_Handle *cfg;
+
+  /**
+   * Client connection to the service.
+   */
+  struct GNUNET_MQ_Handle *mq;
+
+  /**
+   * Message to send on connect.
+   */
+  struct GNUNET_MQ_Envelope *connect_env;
+
+  /**
+   * Time to wait until we try to reconnect on failure.
+   */
+  struct GNUNET_TIME_Relative reconnect_delay;
+
+  /**
+   * Task for reconnecting when the listener fails.
+   */
+  struct GNUNET_SCHEDULER_Task *reconnect_task;
+
+  GNUNET_MULTICAST_JoinRequestCallback join_req_cb;
+  GNUNET_MULTICAST_ReplayFragmentCallback replay_frag_cb;
+  GNUNET_MULTICAST_ReplayMessageCallback replay_msg_cb;
+  GNUNET_MULTICAST_MessageCallback message_cb;
+  void *cb_cls;
+
+  /**
+   * Function called after disconnected from the service.
+   */
+  GNUNET_ContinuationCallback disconnect_cb;
+
+  /**
+   * Closure for @a disconnect_cb.
+   */
+  void *disconnect_cls;
+
+  /**
+   * Are we currently transmitting a message?
+   */
+  uint8_t in_transmit;
+
+  /**
+   * Number of MULTICAST_FRAGMENT_ACK messages we are still waiting for.
+   */
+  uint8_t acks_pending;
+
+  /**
+   * Is this the origin or a member?
+   */
+  uint8_t is_origin;
+
+  /**
+   * Is this channel in the process of disconnecting from the service?
+   * #GNUNET_YES or #GNUNET_NO
+   */
+  uint8_t is_disconnecting;
+};
+
+
+/**
+ * Handle for the origin of a multicast group.
+ */
+struct GNUNET_MULTICAST_Origin
+{
+  struct GNUNET_MULTICAST_Group grp;
+  struct GNUNET_MULTICAST_OriginTransmitHandle tmit;
+
+  GNUNET_MULTICAST_RequestCallback request_cb;
+};
+
+
+/**
+ * Handle for a multicast group member.
+ */
+struct GNUNET_MULTICAST_Member
+{
+  struct GNUNET_MULTICAST_Group grp;
+  struct GNUNET_MULTICAST_MemberTransmitHandle tmit;
+
+  GNUNET_MULTICAST_JoinDecisionCallback join_dcsn_cb;
+
+  /**
+   * Replay fragment -> struct GNUNET_MULTICAST_MemberReplayHandle *
+   */
+  struct GNUNET_CONTAINER_MultiHashMap *replay_reqs;
+
+  uint64_t next_fragment_id;
+};
+
+
+/**
+ * Handle that identifies a join request.
+ *
+ * Used to match calls to #GNUNET_MULTICAST_JoinRequestCallback to the
+ * corresponding calls to #GNUNET_MULTICAST_join_decision().
+ */
+struct GNUNET_MULTICAST_JoinHandle
+{
+  struct GNUNET_MULTICAST_Group *group;
+
+  /**
+   * Public key of the member requesting join.
+   */
+  struct GNUNET_CRYPTO_EcdsaPublicKey member_pub_key;
+
+  /**
+   * Peer identity of the member requesting join.
+   */
+  struct GNUNET_PeerIdentity peer;
+};
+
+
+/**
+ * Opaque handle to a replay request from the multicast service.
+ */
+struct GNUNET_MULTICAST_ReplayHandle
+{
+  struct GNUNET_MULTICAST_Group *grp;
+  struct MulticastReplayRequestMessage req;
+};
+
+
+/**
+ * Handle for a replay request.
+ */
+struct GNUNET_MULTICAST_MemberReplayHandle
+{
+};
+
+
+static void
+origin_to_all (struct GNUNET_MULTICAST_Origin *orig);
+
+static void
+member_to_origin (struct GNUNET_MULTICAST_Member *mem);
+
+
+/**
+ * Check join request message.
+ */
+static int
+check_group_join_request (void *cls,
+                          const struct MulticastJoinRequestMessage *jreq)
+{
+  uint16_t size = ntohs (jreq->header.size);
+
+  if (sizeof (*jreq) == size)
+    return GNUNET_OK;
+
+  if (sizeof (*jreq) + sizeof (struct GNUNET_MessageHeader) <= size)
+    return GNUNET_OK;
+
+  return GNUNET_SYSERR;
+}
+
+
+/**
+ * Receive join request from service.
+ */
+static void
+handle_group_join_request (void *cls,
+                           const struct MulticastJoinRequestMessage *jreq)
+{
+  struct GNUNET_MULTICAST_Group *grp = cls;
+  struct GNUNET_MULTICAST_JoinHandle *jh;
+  const struct GNUNET_MessageHeader *jmsg = NULL;
+
+  if (NULL == grp)
+  {
+    GNUNET_break (0);
+    return;
+  }
+  if (NULL == grp->join_req_cb)
+    return;
+
+  if (sizeof (*jreq) + sizeof (*jmsg) <= ntohs (jreq->header.size))
+    jmsg = (const struct GNUNET_MessageHeader *) &jreq[1];
+
+  jh = GNUNET_malloc (sizeof (*jh));
+  jh->group = grp;
+  jh->member_pub_key = jreq->member_pub_key;
+  jh->peer = jreq->peer;
+  grp->join_req_cb (grp->cb_cls, &jreq->member_pub_key, jmsg, jh);
+
+  grp->reconnect_delay = GNUNET_TIME_UNIT_MILLISECONDS;
+}
+
+
+/**
+ * Check multicast message.
+ */
+static int
+check_group_message (void *cls,
+                     const struct GNUNET_MULTICAST_MessageHeader *mmsg)
+{
+  return GNUNET_OK;
+}
+
+
+/**
+ * Receive multicast message from service.
+ */
+static void
+handle_group_message (void *cls,
+                      const struct GNUNET_MULTICAST_MessageHeader *mmsg)
+{
+  struct GNUNET_MULTICAST_Group *grp = cls;
+
+  if (GNUNET_YES == grp->is_disconnecting)
+    return;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Calling message callback with a message of size %u.\n",
+              ntohs (mmsg->header.size));
+
+  if (NULL != grp->message_cb)
+    grp->message_cb (grp->cb_cls, mmsg);
+
+  grp->reconnect_delay = GNUNET_TIME_UNIT_MILLISECONDS;
+}
+
+
+/**
+ * Receive message/request fragment acknowledgement from service.
+ */
+static void
+handle_group_fragment_ack (void *cls,
+                           const struct GNUNET_MessageHeader *msg)
+{
+  struct GNUNET_MULTICAST_Group *grp = cls;
+
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "%p Got fragment ACK. in_transmit=%u, acks_pending=%u\n",
+       grp, grp->in_transmit, grp->acks_pending);
+
+  if (0 == grp->acks_pending)
+  {
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "%p Ignoring extraneous fragment ACK.\n", grp);
+    return;
+  }
+  grp->acks_pending--;
+
+  if (GNUNET_YES != grp->in_transmit)
+    return;
+
+  if (GNUNET_YES == grp->is_origin)
+    origin_to_all ((struct GNUNET_MULTICAST_Origin *) grp);
+  else
+    member_to_origin ((struct GNUNET_MULTICAST_Member *) grp);
+
+  grp->reconnect_delay = GNUNET_TIME_UNIT_MILLISECONDS;
+}
+
+
+/**
+ * Check unicast request.
+ */
+static int
+check_origin_request (void *cls,
+                      const struct GNUNET_MULTICAST_RequestHeader *req)
+{
+  return GNUNET_OK;
+}
+
+
+/**
+ * Origin receives unicast request from a member.
+ */
+static void
+handle_origin_request (void *cls,
+                       const struct GNUNET_MULTICAST_RequestHeader *req)
+{
+  struct GNUNET_MULTICAST_Group *grp;
+  struct GNUNET_MULTICAST_Origin *orig = cls;
+  grp = &orig->grp;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Calling request callback with a request of size %u.\n",
+              ntohs (req->header.size));
+
+  if (NULL != orig->request_cb)
+    orig->request_cb (grp->cb_cls, req);
+
+  grp->reconnect_delay = GNUNET_TIME_UNIT_MILLISECONDS;
+}
+
+
+/**
+ * Receive multicast replay request from service.
+ */
+static void
+handle_group_replay_request (void *cls,
+                             const struct MulticastReplayRequestMessage *rep)
+
+{
+  struct GNUNET_MULTICAST_Group *grp = cls;
+
+  if (GNUNET_YES == grp->is_disconnecting)
+    return;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got replay request.\n");
+
+  if (0 != rep->fragment_id)
+  {
+    if (NULL != grp->replay_frag_cb)
+    {
+      struct GNUNET_MULTICAST_ReplayHandle * rh = GNUNET_malloc (sizeof (*rh));
+      rh->grp = grp;
+      rh->req = *rep;
+      grp->replay_frag_cb (grp->cb_cls, &rep->member_pub_key,
+                           GNUNET_ntohll (rep->fragment_id),
+                           GNUNET_ntohll (rep->flags), rh);
+    }
+  }
+  else if (0 != rep->message_id)
+  {
+    if (NULL != grp->replay_msg_cb)
+    {
+      struct GNUNET_MULTICAST_ReplayHandle * rh = GNUNET_malloc (sizeof (*rh));
+      rh->grp = grp;
+      rh->req = *rep;
+      grp->replay_msg_cb (grp->cb_cls, &rep->member_pub_key,
+                          GNUNET_ntohll (rep->message_id),
+                          GNUNET_ntohll (rep->fragment_offset),
+                          GNUNET_ntohll (rep->flags), rh);
+    }
+  }
+
+  grp->reconnect_delay = GNUNET_TIME_UNIT_MILLISECONDS;
+}
+
+
+/**
+ * Check replay response.
+ */
+static int
+check_member_replay_response (void *cls,
+                              const struct MulticastReplayResponseMessage *res)
+{
+  uint16_t size = ntohs (res->header.size);
+
+  if (sizeof (*res) == size)
+    return GNUNET_OK;
+
+  if (sizeof (*res) + sizeof (struct GNUNET_MULTICAST_MessageHeader) <= size)
+    return GNUNET_OK;
+
+  return GNUNET_SYSERR;
+}
+
+
+/**
+ * Receive replay response from service.
+ */
+static void
+handle_member_replay_response (void *cls,
+                               const struct MulticastReplayResponseMessage 
*res)
+{
+  struct GNUNET_MULTICAST_Group *grp;
+  struct GNUNET_MULTICAST_Member *mem = cls;
+  grp = &mem->grp;
+
+  if (GNUNET_YES == grp->is_disconnecting)
+    return;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got replay response.\n");
+
+  // FIXME: return result
+}
+
+
+/**
+ * Check join decision.
+ */
+static int
+check_member_join_decision (void *cls,
+                            const struct MulticastJoinDecisionMessageHeader 
*hdcsn)
+{
+  return GNUNET_OK; // checked in handle below
+}
+
+
+/**
+ * Member receives join decision.
+ */
+static void
+handle_member_join_decision (void *cls,
+                             const struct MulticastJoinDecisionMessageHeader 
*hdcsn)
+{
+  struct GNUNET_MULTICAST_Group *grp;
+  struct GNUNET_MULTICAST_Member *mem = cls;
+  grp = &mem->grp;
+
+  const struct MulticastJoinDecisionMessage *
+    dcsn = (const struct MulticastJoinDecisionMessage *) &hdcsn[1];
+
+  uint16_t dcsn_size = ntohs (dcsn->header.size);
+  int is_admitted = ntohl (dcsn->is_admitted);
+
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "%p Member got join decision from multicast: %d\n",
+       mem, is_admitted);
+
+  const struct GNUNET_MessageHeader *join_resp = NULL;
+  uint16_t join_resp_size = 0;
+
+  uint16_t relay_count = ntohl (dcsn->relay_count);
+  const struct GNUNET_PeerIdentity *relays = NULL;
+  uint16_t relay_size = relay_count * sizeof (*relays);
+  if (0 < relay_count)
+  {
+    if (dcsn_size < sizeof (*dcsn) + relay_size)
+    {
+      GNUNET_break_op (0);
+      is_admitted = GNUNET_SYSERR;
+    }
+    else
+    {
+      relays = (struct GNUNET_PeerIdentity *) &dcsn[1];
+    }
+  }
+
+  if (sizeof (*dcsn) + relay_size + sizeof (*join_resp) <= dcsn_size)
+  {
+    join_resp = (const struct GNUNET_MessageHeader *) ((char *) &dcsn[1] + 
relay_size);
+    join_resp_size = ntohs (join_resp->size);
+  }
+  if (dcsn_size < sizeof (*dcsn) + relay_size + join_resp_size)
+  {
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "Received invalid join decision message from multicast: %u < %u + %u 
+ %u\n",
+         dcsn_size , sizeof (*dcsn), relay_size, join_resp_size);
+    GNUNET_break_op (0);
+    is_admitted = GNUNET_SYSERR;
+  }
+
+  if (NULL != mem->join_dcsn_cb)
+    mem->join_dcsn_cb (grp->cb_cls, is_admitted, &hdcsn->peer,
+                       relay_count, relays, join_resp);
+
+  // FIXME:
+  //if (GNUNET_YES != is_admitted)
+  //  GNUNET_MULTICAST_member_part (mem);
+
+  grp->reconnect_delay = GNUNET_TIME_UNIT_MILLISECONDS;
+}
+
+
+static void
+group_cleanup (struct GNUNET_MULTICAST_Group *grp)
+{
+  if (NULL != grp->connect_env)
+  {
+    GNUNET_MQ_discard (grp->connect_env);
+    grp->connect_env = NULL;
+  }
+  if (NULL != grp->mq)
+  {
+    GNUNET_MQ_destroy (grp->mq);
+    grp->mq = NULL;
+  }
+  if (NULL != grp->disconnect_cb)
+  {
+    grp->disconnect_cb (grp->disconnect_cls);
+    grp->disconnect_cb = NULL;
+  }
+  GNUNET_free (grp);
+}
+
+
+static void
+handle_group_part_ack (void *cls,
+                       const struct GNUNET_MessageHeader *msg)
+{
+  struct GNUNET_MULTICAST_Group *grp = cls;
+
+  group_cleanup (grp);
+}
+
+
+/**
+ * Function to call with the decision made for a join request.
+ *
+ * Must be called once and only once in response to an invocation of the
+ * #GNUNET_MULTICAST_JoinRequestCallback.
+ *
+ * @param join
+ *        Join request handle.
+ * @param is_admitted
+ *        #GNUNET_YES    if the join is approved,
+ *        #GNUNET_NO     if it is disapproved,
+ *        #GNUNET_SYSERR if we cannot answer the request.
+ * @param relay_count
+ *        Number of relays given.
+ * @param relays
+ *        Array of suggested peers that might be useful relays to use
+ *        when joining the multicast group (essentially a list of peers that
+ *        are already part of the multicast group and might thus be willing
+ *        to help with routing).  If empty, only this local peer (which must
+ *        be the multicast origin) is a good candidate for building the
+ *        multicast tree.  Note that it is unnecessary to specify our own
+ *        peer identity in this array.
+ * @param join_resp
+ *        Message to send in response to the joining peer;
+ *        can also be used to redirect the peer to a different group at the
+ *        application layer; this response is to be transmitted to the
+ *        peer that issued the request even if admission is denied.
+ */
+struct GNUNET_MULTICAST_ReplayHandle *
+GNUNET_MULTICAST_join_decision (struct GNUNET_MULTICAST_JoinHandle *join,
+                                int is_admitted,
+                                uint16_t relay_count,
+                                const struct GNUNET_PeerIdentity *relays,
+                                const struct GNUNET_MessageHeader *join_resp)
+{
+  struct GNUNET_MULTICAST_Group *grp = join->group;
+  uint16_t join_resp_size = (NULL != join_resp) ? ntohs (join_resp->size) : 0;
+  uint16_t relay_size = relay_count * sizeof (*relays);
+
+  struct MulticastJoinDecisionMessageHeader *hdcsn;
+  struct MulticastJoinDecisionMessage *dcsn;
+  struct GNUNET_MQ_Envelope *
+    env = GNUNET_MQ_msg_extra (hdcsn, sizeof (*dcsn) + relay_size + 
join_resp_size,
+                               GNUNET_MESSAGE_TYPE_MULTICAST_JOIN_DECISION);
+  hdcsn->member_pub_key = join->member_pub_key;
+  hdcsn->peer = join->peer;
+
+  dcsn = (struct MulticastJoinDecisionMessage *) &hdcsn[1];
+  dcsn->header.type = htons (GNUNET_MESSAGE_TYPE_MULTICAST_JOIN_DECISION);
+  dcsn->header.size = htons (sizeof (*dcsn) + relay_size + join_resp_size);
+  dcsn->is_admitted = htonl (is_admitted);
+  dcsn->relay_count = htonl (relay_count);
+  if (0 < relay_size)
+    GNUNET_memcpy (&dcsn[1], relays, relay_size);
+  if (0 < join_resp_size)
+    GNUNET_memcpy (((char *) &dcsn[1]) + relay_size, join_resp, 
join_resp_size);
+
+  GNUNET_MQ_send (grp->mq, env);
+  GNUNET_free (join);
+  return NULL;
+}
+
+
+/**
+ * Replay a message fragment for the multicast group.
+ *
+ * @param rh
+ *        Replay handle identifying which replay operation was requested.
+ * @param msg
+ *        Replayed message fragment, NULL if not found / an error occurred.
+ * @param ec
+ *        Error code.  See enum GNUNET_MULTICAST_ReplayErrorCode
+ *        If not #GNUNET_MULTICAST_REC_OK, the replay handle is invalidated.
+ */
+void
+GNUNET_MULTICAST_replay_response (struct GNUNET_MULTICAST_ReplayHandle *rh,
+                                  const struct GNUNET_MessageHeader *msg,
+                                  enum GNUNET_MULTICAST_ReplayErrorCode ec)
+{
+  uint8_t msg_size = (NULL != msg) ? ntohs (msg->size) : 0;
+  struct MulticastReplayResponseMessage *res;
+  struct GNUNET_MQ_Envelope *
+    env = GNUNET_MQ_msg_extra (res, msg_size,
+                               GNUNET_MESSAGE_TYPE_MULTICAST_REPLAY_RESPONSE);
+  res->fragment_id = rh->req.fragment_id;
+  res->message_id = rh->req.message_id;
+  res->fragment_offset = rh->req.fragment_offset;
+  res->flags = rh->req.flags;
+  res->error_code = htonl (ec);
+
+  if (GNUNET_MULTICAST_REC_OK == ec)
+  {
+    GNUNET_assert (NULL != msg);
+    GNUNET_memcpy (&res[1], msg, msg_size);
+  }
+
+  GNUNET_MQ_send (rh->grp->mq, env);
+
+  if (GNUNET_MULTICAST_REC_OK != ec)
+    GNUNET_free (rh);
+}
+
+
+/**
+ * Indicate the end of the replay session.
+ *
+ * Invalidates the replay handle.
+ *
+ * @param rh
+ *        Replay session to end.
+ */
+void
+GNUNET_MULTICAST_replay_response_end (struct GNUNET_MULTICAST_ReplayHandle *rh)
+{
+  struct MulticastReplayResponseMessage *end;
+  struct GNUNET_MQ_Envelope *
+    env = GNUNET_MQ_msg (end, 
GNUNET_MESSAGE_TYPE_MULTICAST_REPLAY_RESPONSE_END);
+
+  end->fragment_id = rh->req.fragment_id;
+  end->message_id = rh->req.message_id;
+  end->fragment_offset = rh->req.fragment_offset;
+  end->flags = rh->req.flags;
+
+  GNUNET_MQ_send (rh->grp->mq, env);
+  GNUNET_free (rh);
+}
+
+
+/**
+ * Replay a message for the multicast group.
+ *
+ * @param rh
+ *        Replay handle identifying which replay operation was requested.
+ * @param notify
+ *        Function to call to get the message.
+ * @param notify_cls
+ *        Closure for @a notify.
+ */
+void
+GNUNET_MULTICAST_replay_response2 (struct GNUNET_MULTICAST_ReplayHandle *rh,
+                                   GNUNET_MULTICAST_ReplayTransmitNotify 
notify,
+                                   void *notify_cls)
+{
+}
+
+
+static void
+origin_connect (struct GNUNET_MULTICAST_Origin *orig);
+
+
+static void
+origin_reconnect (void *cls)
+{
+  origin_connect (cls);
+}
+
+
+/**
+ * Origin client disconnected from service.
+ *
+ * Reconnect after backoff period.
+ */
+static void
+origin_disconnected (void *cls, enum GNUNET_MQ_Error error)
+{
+  struct GNUNET_MULTICAST_Origin *orig = cls;
+  struct GNUNET_MULTICAST_Group *grp = &orig->grp;
+
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Origin client disconnected (%d), re-connecting\n",
+       (int) error);
+  if (NULL != grp->mq)
+  {
+    GNUNET_MQ_destroy (grp->mq);
+    grp->mq = NULL;
+  }
+
+  grp->reconnect_task = GNUNET_SCHEDULER_add_delayed (grp->reconnect_delay,
+                                                      origin_reconnect,
+                                                      orig);
+  grp->reconnect_delay = GNUNET_TIME_STD_BACKOFF (grp->reconnect_delay);
+}
+
+
+/**
+ * Connect to service as origin.
+ */
+static void
+origin_connect (struct GNUNET_MULTICAST_Origin *orig)
+{
+  struct GNUNET_MULTICAST_Group *grp = &orig->grp;
+
+  struct GNUNET_MQ_MessageHandler handlers[] = {
+    GNUNET_MQ_hd_var_size (group_message,
+                           GNUNET_MESSAGE_TYPE_MULTICAST_MESSAGE,
+                           struct GNUNET_MULTICAST_MessageHeader,
+                           grp),
+    GNUNET_MQ_hd_var_size (origin_request,
+                           GNUNET_MESSAGE_TYPE_MULTICAST_REQUEST,
+                           struct GNUNET_MULTICAST_RequestHeader,
+                           orig),
+    GNUNET_MQ_hd_fixed_size (group_fragment_ack,
+                             GNUNET_MESSAGE_TYPE_MULTICAST_FRAGMENT_ACK,
+                             struct GNUNET_MessageHeader,
+                             grp),
+    GNUNET_MQ_hd_var_size (group_join_request,
+                           GNUNET_MESSAGE_TYPE_MULTICAST_JOIN_REQUEST,
+                           struct MulticastJoinRequestMessage,
+                           grp),
+    GNUNET_MQ_hd_fixed_size (group_part_ack,
+                             GNUNET_MESSAGE_TYPE_MULTICAST_PART_ACK,
+                             struct GNUNET_MessageHeader,
+                             grp),
+    GNUNET_MQ_hd_fixed_size (group_replay_request,
+                             GNUNET_MESSAGE_TYPE_MULTICAST_REPLAY_REQUEST,
+                             struct MulticastReplayRequestMessage,
+                             grp),
+    GNUNET_MQ_handler_end ()
+  };
+
+  grp->mq = GNUNET_CLIENT_connect (grp->cfg, "multicast",
+                                   handlers, origin_disconnected, orig);
+  GNUNET_assert (NULL != grp->mq);
+  GNUNET_MQ_send_copy (grp->mq, grp->connect_env);
+}
+
+
+/**
+ * Start a multicast group.
+ *
+ * Will advertise the origin in the P2P overlay network under the respective
+ * public key so that other peer can find this peer to join it.  Peers that
+ * issue GNUNET_MULTICAST_member_join() can then transmit a join request to
+ * either an existing group member or to the origin.  If the joining is
+ * approved, the member is cleared for @e replay and will begin to receive
+ * messages transmitted to the group.  If joining is disapproved, the failed
+ * candidate will be given a response.  Members in the group can send messages
+ * to the origin (one at a time).
+ *
+ * @param cfg
+ *        Configuration to use.
+ * @param priv_key
+ *        ECC key that will be used to sign messages for this
+ *        multicast session; public key is used to identify the multicast 
group;
+ * @param max_fragment_id
+ *        Maximum fragment ID already sent to the group.
+ *        0 for a new group.
+ * @param join_request_cb
+ *        Function called to approve / disapprove joining of a peer.
+ * @param replay_frag_cb
+ *        Function that can be called to replay a message fragment.
+ * @param replay_msg_cb
+ *        Function that can be called to replay a message.
+ * @param request_cb
+ *        Function called with message fragments from group members.
+ * @param message_cb
+ *        Function called with the message fragments sent to the
+ *        network by GNUNET_MULTICAST_origin_to_all().  These message fragments
+ *        should be stored for answering replay requests later.
+ * @param cls
+ *        Closure for the various callbacks that follow.
+ *
+ * @return Handle for the origin, NULL on error.
+ */
+struct GNUNET_MULTICAST_Origin *
+GNUNET_MULTICAST_origin_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
+                               const struct GNUNET_CRYPTO_EddsaPrivateKey 
*priv_key,
+                               uint64_t max_fragment_id,
+                               GNUNET_MULTICAST_JoinRequestCallback 
join_request_cb,
+                               GNUNET_MULTICAST_ReplayFragmentCallback 
replay_frag_cb,
+                               GNUNET_MULTICAST_ReplayMessageCallback 
replay_msg_cb,
+                               GNUNET_MULTICAST_RequestCallback request_cb,
+                               GNUNET_MULTICAST_MessageCallback message_cb,
+                               void *cls)
+{
+  struct GNUNET_MULTICAST_Origin *orig = GNUNET_malloc (sizeof (*orig));
+  struct GNUNET_MULTICAST_Group *grp = &orig->grp;
+
+  struct MulticastOriginStartMessage *start;
+  grp->connect_env = GNUNET_MQ_msg (start,
+                                    
GNUNET_MESSAGE_TYPE_MULTICAST_ORIGIN_START);
+  start->max_fragment_id = max_fragment_id;
+  start->group_key = *priv_key;
+
+  grp->cfg = cfg;
+  grp->is_origin = GNUNET_YES;
+  grp->reconnect_delay = GNUNET_TIME_UNIT_MILLISECONDS;
+
+  grp->cb_cls = cls;
+  grp->join_req_cb = join_request_cb;
+  grp->replay_frag_cb = replay_frag_cb;
+  grp->replay_msg_cb = replay_msg_cb;
+  grp->message_cb = message_cb;
+
+  orig->request_cb = request_cb;
+
+  origin_connect (orig);
+  return orig;
+}
+
+
+/**
+ * Stop a multicast group.
+ *
+ * @param origin
+ *        Multicast group to stop.
+ */
+void
+GNUNET_MULTICAST_origin_stop (struct GNUNET_MULTICAST_Origin *orig,
+                              GNUNET_ContinuationCallback stop_cb,
+                              void *stop_cls)
+{
+  struct GNUNET_MULTICAST_Group *grp = &orig->grp;
+  struct GNUNET_MQ_Envelope *env;
+
+  grp->is_disconnecting = GNUNET_YES;
+  grp->disconnect_cb = stop_cb;
+  grp->disconnect_cls = stop_cls;
+  env = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_MULTICAST_PART_REQUEST);
+  GNUNET_MQ_send (grp->mq, env);
+}
+
+
+static void
+origin_to_all (struct GNUNET_MULTICAST_Origin *orig)
+{
+  LOG (GNUNET_ERROR_TYPE_DEBUG, "%p origin_to_all()\n", orig);
+  struct GNUNET_MULTICAST_Group *grp = &orig->grp;
+  struct GNUNET_MULTICAST_OriginTransmitHandle *tmit = &orig->tmit;
+  GNUNET_assert (GNUNET_YES == grp->in_transmit);
+
+  size_t buf_size = GNUNET_MULTICAST_FRAGMENT_MAX_SIZE;
+  struct GNUNET_MULTICAST_MessageHeader *msg;
+  struct GNUNET_MQ_Envelope *
+    env = GNUNET_MQ_msg_extra (msg, buf_size - sizeof(*msg),
+                               GNUNET_MESSAGE_TYPE_MULTICAST_MESSAGE);
+
+  int ret = tmit->notify (tmit->notify_cls, &buf_size, &msg[1]);
+
+  if (! (GNUNET_YES == ret || GNUNET_NO == ret)
+      || GNUNET_MULTICAST_FRAGMENT_MAX_SIZE < buf_size)
+  {
+    LOG (GNUNET_ERROR_TYPE_ERROR,
+         "%p OriginTransmitNotify() returned error or invalid message size.\n",
+         orig);
+    /* FIXME: handle error */
+    GNUNET_MQ_discard (env);
+    return;
+  }
+
+  if (GNUNET_NO == ret && 0 == buf_size)
+  {
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "%p OriginTransmitNotify() - transmission paused.\n", orig);
+    GNUNET_MQ_discard (env);
+    return; /* Transmission paused. */
+  }
+
+  msg->header.size = htons (sizeof (*msg) + buf_size);
+  msg->message_id = GNUNET_htonll (tmit->message_id);
+  msg->group_generation = tmit->group_generation;
+  msg->fragment_offset = GNUNET_htonll (tmit->fragment_offset);
+  tmit->fragment_offset += sizeof (*msg) + buf_size;
+
+  grp->acks_pending++;
+  GNUNET_MQ_send (grp->mq, env);
+
+  if (GNUNET_YES == ret)
+    grp->in_transmit = GNUNET_NO;
+}
+
+
+/**
+ * Send a message to the multicast group.
+ *
+ * @param orig
+ *        Handle to the multicast group.
+ * @param message_id
+ *        Application layer ID for the message.  Opaque to multicast.
+ * @param group_generation
+ *        Group generation of the message.
+ *        Documented in struct GNUNET_MULTICAST_MessageHeader.
+ * @param notify
+ *        Function to call to get the message.
+ * @param notify_cls
+ *        Closure for @a notify.
+ *
+ * @return Message handle on success,
+ *         NULL on error (i.e. another request is already pending).
+ */
+struct GNUNET_MULTICAST_OriginTransmitHandle *
+GNUNET_MULTICAST_origin_to_all (struct GNUNET_MULTICAST_Origin *orig,
+                                uint64_t message_id,
+                                uint64_t group_generation,
+                                GNUNET_MULTICAST_OriginTransmitNotify notify,
+                                void *notify_cls)
+{
+  struct GNUNET_MULTICAST_Group *grp = &orig->grp;
+  if (GNUNET_YES == grp->in_transmit)
+    return NULL;
+  grp->in_transmit = GNUNET_YES;
+
+  struct GNUNET_MULTICAST_OriginTransmitHandle *tmit = &orig->tmit;
+  tmit->origin = orig;
+  tmit->message_id = message_id;
+  tmit->fragment_offset = 0;
+  tmit->group_generation = group_generation;
+  tmit->notify = notify;
+  tmit->notify_cls = notify_cls;
+
+  origin_to_all (orig);
+  return tmit;
+}
+
+
+/**
+ * Resume message transmission to multicast group.
+ *
+ * @param th
+ *        Transmission to cancel.
+ */
+void
+GNUNET_MULTICAST_origin_to_all_resume (struct 
GNUNET_MULTICAST_OriginTransmitHandle *th)
+{
+  struct GNUNET_MULTICAST_Group *grp = &th->origin->grp;
+  if (0 != grp->acks_pending || GNUNET_YES != grp->in_transmit)
+    return;
+  origin_to_all (th->origin);
+}
+
+
+/**
+ * Cancel request for message transmission to multicast group.
+ *
+ * @param th
+ *        Transmission to cancel.
+ */
+void
+GNUNET_MULTICAST_origin_to_all_cancel (struct 
GNUNET_MULTICAST_OriginTransmitHandle *th)
+{
+  th->origin->grp.in_transmit = GNUNET_NO;
+}
+
+
+static void
+member_connect (struct GNUNET_MULTICAST_Member *mem);
+
+
+static void
+member_reconnect (void *cls)
+{
+  member_connect (cls);
+}
+
+
+/**
+ * Member client disconnected from service.
+ *
+ * Reconnect after backoff period.
+ */
+static void
+member_disconnected (void *cls, enum GNUNET_MQ_Error error)
+{
+  struct GNUNET_MULTICAST_Member *mem = cls;
+  struct GNUNET_MULTICAST_Group *grp = &mem->grp;
+
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Member client disconnected (%d), re-connecting\n",
+       (int) error);
+  GNUNET_MQ_destroy (grp->mq);
+  grp->mq = NULL;
+
+  grp->reconnect_task = GNUNET_SCHEDULER_add_delayed (grp->reconnect_delay,
+                                                      member_reconnect,
+                                                      mem);
+  grp->reconnect_delay = GNUNET_TIME_STD_BACKOFF (grp->reconnect_delay);
+}
+
+
+/**
+ * Connect to service as member.
+ */
+static void
+member_connect (struct GNUNET_MULTICAST_Member *mem)
+{
+  struct GNUNET_MULTICAST_Group *grp = &mem->grp;
+
+  struct GNUNET_MQ_MessageHandler handlers[] = {
+    GNUNET_MQ_hd_var_size (group_message,
+                           GNUNET_MESSAGE_TYPE_MULTICAST_MESSAGE,
+                           struct GNUNET_MULTICAST_MessageHeader,
+                           grp),
+    GNUNET_MQ_hd_fixed_size (group_fragment_ack,
+                             GNUNET_MESSAGE_TYPE_MULTICAST_FRAGMENT_ACK,
+                             struct GNUNET_MessageHeader,
+                             grp),
+    GNUNET_MQ_hd_var_size (group_join_request,
+                           GNUNET_MESSAGE_TYPE_MULTICAST_JOIN_REQUEST,
+                           struct MulticastJoinRequestMessage,
+                           grp),
+    GNUNET_MQ_hd_var_size (member_join_decision,
+                           GNUNET_MESSAGE_TYPE_MULTICAST_JOIN_DECISION,
+                           struct MulticastJoinDecisionMessageHeader,
+                           mem),
+    GNUNET_MQ_hd_fixed_size (group_part_ack,
+                             GNUNET_MESSAGE_TYPE_MULTICAST_PART_ACK,
+                             struct GNUNET_MessageHeader,
+                             grp),
+    GNUNET_MQ_hd_fixed_size (group_replay_request,
+                             GNUNET_MESSAGE_TYPE_MULTICAST_REPLAY_REQUEST,
+                             struct MulticastReplayRequestMessage,
+                             grp),
+    GNUNET_MQ_hd_var_size (member_replay_response,
+                           GNUNET_MESSAGE_TYPE_MULTICAST_REPLAY_RESPONSE,
+                           struct MulticastReplayResponseMessage,
+                           mem),
+    GNUNET_MQ_handler_end ()
+  };
+
+  grp->mq = GNUNET_CLIENT_connect (grp->cfg, "multicast",
+                                   handlers, member_disconnected, mem);
+  GNUNET_assert (NULL != grp->mq);
+  GNUNET_MQ_send_copy (grp->mq, grp->connect_env);
+}
+
+
+/**
+ * Join a multicast group.
+ *
+ * The entity joining is always the local peer.  Further information about the
+ * candidate can be provided in the @a join_request message.  If the join 
fails, the
+ * @a message_cb is invoked with a (failure) response and then with NULL.  If
+ * the join succeeds, outstanding (state) messages and ongoing multicast
+ * messages will be given to the @a message_cb until the member decides to part
+ * the group.  The @a replay_cb function may be called at any time by the
+ * multicast service to support relaying messages to other members of the 
group.
+ *
+ * @param cfg
+ *        Configuration to use.
+ * @param group_key
+ *        ECC public key that identifies the group to join.
+ * @param member_key
+ *        ECC key that identifies the member
+ *        and used to sign requests sent to the origin.
+ * @param origin
+ *        Peer ID of the origin to send unicast requsets to.  If NULL,
+ *        unicast requests are sent back via multiple hops on the reverse path
+ *        of multicast messages.
+ * @param relay_count
+ *        Number of peers in the @a relays array.
+ * @param relays
+ *        Peer identities of members of the group, which serve as relays
+ *        and can be used to join the group at. and send the @a join_request 
to.
+ *        If empty, the @a join_request is sent directly to the @a origin.
+ * @param join_msg
+ *        Application-dependent join message to be passed to the peer @a 
origin.
+ * @param join_request_cb
+ *        Function called to approve / disapprove joining of a peer.
+ * @param join_decision_cb
+ *        Function called to inform about the join decision.
+ * @param replay_frag_cb
+ *        Function that can be called to replay message fragments
+ *        this peer already knows from this group. NULL if this
+ *        client is unable to support replay.
+ * @param replay_msg_cb
+ *        Function that can be called to replay message fragments
+ *        this peer already knows from this group. NULL if this
+ *        client is unable to support replay.
+ * @param message_cb
+ *        Function to be called for all message fragments we
+ *        receive from the group, excluding those our @a replay_cb
+ *        already has.
+ * @param cls
+ *        Closure for callbacks.
+ *
+ * @return Handle for the member, NULL on error.
+ */
+struct GNUNET_MULTICAST_Member *
+GNUNET_MULTICAST_member_join (const struct GNUNET_CONFIGURATION_Handle *cfg,
+                              const struct GNUNET_CRYPTO_EddsaPublicKey 
*group_pub_key,
+                              const struct GNUNET_CRYPTO_EcdsaPrivateKey 
*member_key,
+                              const struct GNUNET_PeerIdentity *origin,
+                              uint16_t relay_count,
+                              const struct GNUNET_PeerIdentity *relays,
+                              const struct GNUNET_MessageHeader *join_msg,
+                              GNUNET_MULTICAST_JoinRequestCallback 
join_request_cb,
+                              GNUNET_MULTICAST_JoinDecisionCallback 
join_decision_cb,
+                              GNUNET_MULTICAST_ReplayFragmentCallback 
replay_frag_cb,
+                              GNUNET_MULTICAST_ReplayMessageCallback 
replay_msg_cb,
+                              GNUNET_MULTICAST_MessageCallback message_cb,
+                              void *cls)
+{
+  struct GNUNET_MULTICAST_Member *mem = GNUNET_malloc (sizeof (*mem));
+  struct GNUNET_MULTICAST_Group *grp = &mem->grp;
+
+  uint16_t relay_size = relay_count * sizeof (*relays);
+  uint16_t join_msg_size = (NULL != join_msg) ? ntohs (join_msg->size) : 0;
+  struct MulticastMemberJoinMessage *join;
+  grp->connect_env = GNUNET_MQ_msg_extra (join, relay_size + join_msg_size,
+                                          
GNUNET_MESSAGE_TYPE_MULTICAST_MEMBER_JOIN);
+  join->group_pub_key = *group_pub_key;
+  join->member_key = *member_key;
+  join->origin = *origin;
+  join->relay_count = ntohl (relay_count);
+  if (0 < relay_size)
+    GNUNET_memcpy (&join[1], relays, relay_size);
+  if (0 < join_msg_size)
+    GNUNET_memcpy (((char *) &join[1]) + relay_size, join_msg, join_msg_size);
+
+  grp->cfg = cfg;
+  grp->is_origin = GNUNET_NO;
+  grp->reconnect_delay = GNUNET_TIME_UNIT_MILLISECONDS;
+
+  mem->join_dcsn_cb = join_decision_cb;
+  grp->join_req_cb = join_request_cb;
+  grp->replay_frag_cb = replay_frag_cb;
+  grp->replay_msg_cb = replay_msg_cb;
+  grp->message_cb = message_cb;
+  grp->cb_cls = cls;
+
+  member_connect (mem);
+  return mem;
+}
+
+
+/**
+ * Part a multicast group.
+ *
+ * Disconnects from all group members and invalidates the @a member handle.
+ *
+ * An application-dependent part message can be transmitted beforehand using
+ * #GNUNET_MULTICAST_member_to_origin())
+ *
+ * @param member
+ *        Membership handle.
+ */
+void
+GNUNET_MULTICAST_member_part (struct GNUNET_MULTICAST_Member *mem,
+                              GNUNET_ContinuationCallback part_cb,
+                              void *part_cls)
+{
+  struct GNUNET_MULTICAST_Group *grp = &mem->grp;
+  struct GNUNET_MQ_Envelope *env;
+
+  mem->join_dcsn_cb = NULL;
+  grp->join_req_cb = NULL;
+  grp->message_cb = NULL;
+  grp->replay_msg_cb = NULL;
+  grp->replay_frag_cb = NULL;
+  grp->is_disconnecting = GNUNET_YES;
+  grp->disconnect_cb = part_cb;
+  grp->disconnect_cls = part_cls;
+  env = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_MULTICAST_PART_REQUEST);
+  GNUNET_MQ_send (grp->mq, env);
+}
+
+
+void
+member_replay_request (struct GNUNET_MULTICAST_Member *mem,
+                       uint64_t fragment_id,
+                       uint64_t message_id,
+                       uint64_t fragment_offset,
+                       uint64_t flags)
+{
+  struct MulticastReplayRequestMessage *rep;
+  struct GNUNET_MQ_Envelope *
+    env = GNUNET_MQ_msg (rep, GNUNET_MESSAGE_TYPE_MULTICAST_REPLAY_REQUEST);
+
+  rep->fragment_id = GNUNET_htonll (fragment_id);
+  rep->message_id = GNUNET_htonll (message_id);
+  rep->fragment_offset = GNUNET_htonll (fragment_offset);
+  rep->flags = GNUNET_htonll (flags);
+
+  GNUNET_MQ_send (mem->grp.mq, env);
+}
+
+
+/**
+ * Request a fragment to be replayed by fragment ID.
+ *
+ * Useful if messages below the @e max_known_fragment_id given when joining are
+ * needed and not known to the client.
+ *
+ * @param member
+ *        Membership handle.
+ * @param fragment_id
+ *        ID of a message fragment that this client would like to see replayed.
+ * @param flags
+ *        Additional flags for the replay request.
+ *        It is used and defined by GNUNET_MULTICAST_ReplayFragmentCallback
+ *
+ * @return Replay request handle.
+ */
+struct GNUNET_MULTICAST_MemberReplayHandle *
+GNUNET_MULTICAST_member_replay_fragment (struct GNUNET_MULTICAST_Member *mem,
+                                         uint64_t fragment_id,
+                                         uint64_t flags)
+{
+  member_replay_request (mem, fragment_id, 0, 0, flags);
+  // FIXME: return something useful
+  return NULL;
+}
+
+
+/**
+ * Request a message fragment to be replayed.
+ *
+ * Useful if messages below the @e max_known_fragment_id given when joining are
+ * needed and not known to the client.
+ *
+ * @param member
+ *        Membership handle.
+ * @param message_id
+ *        ID of the message this client would like to see replayed.
+ * @param fragment_offset
+ *        Offset of the fragment within the message to replay.
+ * @param flags
+ *        Additional flags for the replay request.
+ *        It is used & defined by GNUNET_MULTICAST_ReplayMessageCallback
+ *
+ * @return Replay request handle, NULL on error.
+ */
+struct GNUNET_MULTICAST_MemberReplayHandle *
+GNUNET_MULTICAST_member_replay_message (struct GNUNET_MULTICAST_Member *mem,
+                                        uint64_t message_id,
+                                        uint64_t fragment_offset,
+                                        uint64_t flags)
+{
+  member_replay_request (mem, 0, message_id, fragment_offset, flags);
+  // FIXME: return something useful
+  return NULL;
+}
+
+
+static void
+member_to_origin (struct GNUNET_MULTICAST_Member *mem)
+{
+  LOG (GNUNET_ERROR_TYPE_DEBUG, "member_to_origin()\n");
+  struct GNUNET_MULTICAST_Group *grp = &mem->grp;
+  struct GNUNET_MULTICAST_MemberTransmitHandle *tmit = &mem->tmit;
+  GNUNET_assert (GNUNET_YES == grp->in_transmit);
+
+  size_t buf_size = GNUNET_MULTICAST_FRAGMENT_MAX_SIZE;
+  struct GNUNET_MULTICAST_RequestHeader *req;
+  struct GNUNET_MQ_Envelope *
+    env = GNUNET_MQ_msg_extra (req, buf_size - sizeof(*req),
+                               GNUNET_MESSAGE_TYPE_MULTICAST_REQUEST);
+
+  int ret = tmit->notify (tmit->notify_cls, &buf_size, &req[1]);
+
+  if (! (GNUNET_YES == ret || GNUNET_NO == ret)
+      || GNUNET_MULTICAST_FRAGMENT_MAX_SIZE < buf_size)
+  {
+    LOG (GNUNET_ERROR_TYPE_ERROR,
+         "MemberTransmitNotify() returned error or invalid message size. "
+         "ret=%d, buf_size=%u\n", ret, buf_size);
+    /* FIXME: handle error */
+    GNUNET_MQ_discard (env);
+    return;
+  }
+
+  if (GNUNET_NO == ret && 0 == buf_size)
+  {
+    /* Transmission paused. */
+    GNUNET_MQ_discard (env);
+    return;
+  }
+
+  req->header.size = htons (sizeof (*req) + buf_size);
+  req->request_id = GNUNET_htonll (tmit->request_id);
+  req->fragment_offset = GNUNET_ntohll (tmit->fragment_offset);
+  tmit->fragment_offset += sizeof (*req) + buf_size;
+
+  GNUNET_MQ_send (grp->mq, env);
+
+  if (GNUNET_YES == ret)
+    grp->in_transmit = GNUNET_NO;
+}
+
+
+/**
+ * Send a message to the origin of the multicast group.
+ *
+ * @param mem
+ *        Membership handle.
+ * @param request_id
+ *        Application layer ID for the request.  Opaque to multicast.
+ * @param notify
+ *        Callback to call to get the message.
+ * @param notify_cls
+ *        Closure for @a notify.
+ *
+ * @return Handle to cancel request, NULL on error (i.e. request already 
pending).
+ */
+struct GNUNET_MULTICAST_MemberTransmitHandle *
+GNUNET_MULTICAST_member_to_origin (struct GNUNET_MULTICAST_Member *mem,
+                                   uint64_t request_id,
+                                   GNUNET_MULTICAST_MemberTransmitNotify 
notify,
+                                   void *notify_cls)
+{
+  if (GNUNET_YES == mem->grp.in_transmit)
+    return NULL;
+  mem->grp.in_transmit = GNUNET_YES;
+
+  struct GNUNET_MULTICAST_MemberTransmitHandle *tmit = &mem->tmit;
+  tmit->member = mem;
+  tmit->request_id = request_id;
+  tmit->fragment_offset = 0;
+  tmit->notify = notify;
+  tmit->notify_cls = notify_cls;
+
+  member_to_origin (mem);
+  return tmit;
+}
+
+
+/**
+ * Resume message transmission to origin.
+ *
+ * @param th
+ *        Transmission to cancel.
+ */
+void
+GNUNET_MULTICAST_member_to_origin_resume (struct 
GNUNET_MULTICAST_MemberTransmitHandle *th)
+{
+  struct GNUNET_MULTICAST_Group *grp = &th->member->grp;
+  if (0 != grp->acks_pending || GNUNET_YES != grp->in_transmit)
+    return;
+  member_to_origin (th->member);
+}
+
+
+/**
+ * Cancel request for message transmission to origin.
+ *
+ * @param th
+ *        Transmission to cancel.
+ */
+void
+GNUNET_MULTICAST_member_to_origin_cancel (struct 
GNUNET_MULTICAST_MemberTransmitHandle *th)
+{
+  th->member->grp.in_transmit = GNUNET_NO;
+}
+
+
+/* end of multicast_api.c */
diff --git a/src/multicast/test_multicast.c b/src/multicast/test_multicast.c
new file mode 100644
index 0000000..70efdcb
--- /dev/null
+++ b/src/multicast/test_multicast.c
@@ -0,0 +1,758 @@
+/*
+ * This file is part of GNUnet
+ * Copyright (C) 2013 GNUnet e.V.
+ *
+ * GNUnet is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * GNUnet 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
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+     SPDX-License-Identifier: AGPL3.0-or-later
+ */
+
+/**
+ * @file multicast/test_multicast.c
+ * @brief Tests for the Multicast API.
+ * @author Gabor X Toth
+ */
+
+#include <inttypes.h>
+
+#include "platform.h"
+#include "gnunet_crypto_lib.h"
+#include "gnunet_common.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_testing_lib.h"
+#include "gnunet_multicast_service.h"
+
+#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
+
+/**
+ * Return value from 'main'.
+ */
+static int res;
+
+/**
+ * Handle for task for timeout termination.
+ */
+static struct GNUNET_SCHEDULER_Task * end_badly_task;
+
+static const struct GNUNET_CONFIGURATION_Handle *cfg;
+
+struct GNUNET_PeerIdentity this_peer;
+
+struct GNUNET_MULTICAST_Origin *origin;
+struct GNUNET_MULTICAST_Member *member;
+
+struct GNUNET_CRYPTO_EddsaPrivateKey *group_key;
+struct GNUNET_CRYPTO_EddsaPublicKey group_pub_key;
+
+struct GNUNET_CRYPTO_EcdsaPrivateKey *member_key;
+struct GNUNET_CRYPTO_EcdsaPublicKey member_pub_key;
+
+struct TransmitClosure {
+  struct GNUNET_MULTICAST_OriginTransmitHandle *orig_tmit;
+  struct GNUNET_MULTICAST_MemberTransmitHandle *mem_tmit;
+  char * data[16];
+  uint8_t data_delay[16];
+  uint8_t data_count;
+  uint8_t paused;
+  uint8_t n;
+} tmit_cls;
+
+struct OriginClosure {
+  uint8_t msgs_expected;
+  uint8_t n;
+} origin_cls;
+
+struct MemberClosure {
+  uint8_t msgs_expected;
+  size_t n;
+} member_cls;
+
+struct GNUNET_MessageHeader *join_req, *join_resp;
+
+enum
+{
+  TEST_NONE                = 0,
+  TEST_ORIGIN_START        = 1,
+  TEST_MEMBER_JOIN_REFUSE  = 2,
+  TEST_MEMBER_JOIN_ADMIT   = 3,
+  TEST_ORIGIN_TO_ALL       = 4,
+  TEST_ORIGIN_TO_ALL_RECV  = 5,
+  TEST_MEMBER_TO_ORIGIN    = 6,
+  TEST_MEMBER_REPLAY_ERROR = 7,
+  TEST_MEMBER_REPLAY_OK    = 8,
+  TEST_MEMBER_PART         = 9,
+  TEST_ORIGIN_STOP        = 10,
+} test;
+
+uint64_t replay_fragment_id;
+uint64_t replay_flags;
+
+static void
+member_join (int t);
+
+
+/**
+ * Clean up all resources used.
+ */
+static void
+cleanup ()
+{
+  if (NULL != member)
+  {
+    GNUNET_MULTICAST_member_part (member, NULL, NULL);
+    member = NULL;
+  }
+  if (NULL != origin)
+  {
+    GNUNET_MULTICAST_origin_stop (origin, NULL, NULL);
+    origin = NULL;
+  }
+}
+
+
+/**
+ * Terminate the test case (failure).
+ *
+ * @param cls NULL
+ */
+static void
+end_badly (void *cls)
+{
+  res = 1;
+  cleanup ();
+  GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Test FAILED.\n");
+}
+
+
+/**
+ * Terminate the test case (success).
+ *
+ * @param cls NULL
+ */
+static void
+end_normally (void *cls)
+{
+  res = 0;
+  cleanup ();
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Test PASSED.\n");
+}
+
+
+/**
+ * Finish the test case (successfully).
+ */
+static void
+end ()
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Ending tests.\n");
+
+  if (end_badly_task != NULL)
+  {
+    GNUNET_SCHEDULER_cancel (end_badly_task);
+    end_badly_task = NULL;
+  }
+  GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS,
+                               &end_normally, NULL);
+}
+
+
+static void
+tmit_resume (void *cls)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Transmission resumed.\n");
+  struct TransmitClosure *tmit = cls;
+  if (NULL != tmit->orig_tmit)
+    GNUNET_MULTICAST_origin_to_all_resume (tmit->orig_tmit);
+  else if (NULL != tmit->mem_tmit)
+    GNUNET_MULTICAST_member_to_origin_resume (tmit->mem_tmit);
+}
+
+
+static int
+tmit_notify (void *cls, size_t *data_size, void *data)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Test #%u: origin_tmit_notify()\n", test);
+  struct TransmitClosure *tmit = cls;
+
+  if (0 == tmit->data_count)
+  {
+    *data_size = 0;
+    return GNUNET_YES;
+  }
+
+  uint16_t size = strlen (tmit->data[tmit->n]);
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Transmit notify data: %u bytes available, processing fragment 
%u/%u (size %u).\n",
+              (unsigned int) *data_size,
+              tmit->n + 1,
+              tmit->data_count,
+              size);
+  if (*data_size < size)
+  {
+    *data_size = 0;
+    GNUNET_assert (0);
+    return GNUNET_SYSERR;
+  }
+
+  if (GNUNET_YES != tmit->paused && 0 < tmit->data_delay[tmit->n])
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Transmission paused.\n");
+    tmit->paused = GNUNET_YES;
+    GNUNET_SCHEDULER_add_delayed (
+      GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
+                                     tmit->data_delay[tmit->n]),
+      tmit_resume, tmit);
+    *data_size = 0;
+    return GNUNET_NO;
+  }
+  tmit->paused = GNUNET_NO;
+
+  *data_size = size;
+  GNUNET_memcpy (data, tmit->data[tmit->n], size);
+
+  return ++tmit->n < tmit->data_count ? GNUNET_NO : GNUNET_YES;
+}
+
+
+static void
+member_recv_join_request (void *cls,
+                          const struct GNUNET_CRYPTO_EcdsaPublicKey 
*member_key,
+                          const struct GNUNET_MessageHeader *join_msg,
+                          struct GNUNET_MULTICAST_JoinHandle *jh)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Test #%u: member_recv_join_request()\n", test);
+}
+
+
+static void
+origin_stopped (void *cls)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Test #%u: origin_stopped()\n", test);
+  end ();
+}
+
+
+static void
+schedule_origin_stop (void *cls)
+{
+  test = TEST_ORIGIN_STOP;
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Test #%u: origin_stop()\n", test);
+  GNUNET_MULTICAST_origin_stop (origin, origin_stopped, NULL);
+  origin = NULL;
+}
+
+
+static void
+member_parted (void *cls)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Test #%u: member_parted()\n", test);
+  member = NULL;
+
+  switch (test)
+  {
+  case TEST_MEMBER_JOIN_REFUSE:
+    // Test 3 starts here
+    member_join (TEST_MEMBER_JOIN_ADMIT);
+    break;
+
+  case TEST_MEMBER_PART:
+    GNUNET_SCHEDULER_add_now (&schedule_origin_stop, NULL);
+    break;
+
+  default:
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Invalid test #%d in member_parted()\n", test);
+    GNUNET_assert (0);
+  }
+}
+
+
+static void
+schedule_member_part (void *cls)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Test #%u: schedule_member_part()\n", test);
+  GNUNET_MULTICAST_member_part (member, member_parted, NULL);
+}
+
+
+static void
+member_part ()
+{
+  test = TEST_MEMBER_PART;
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Test #%u: member_part()\n", test);
+  // Test 10 starts here
+  GNUNET_SCHEDULER_add_now (&schedule_member_part, NULL);
+}
+
+
+static void
+member_replay_ok ()
+{
+  // Execution of test 8 here
+  test = TEST_MEMBER_REPLAY_OK;
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Test #%u: member_replay_ok()\n", test);
+  replay_fragment_id = 1;
+  replay_flags = 1 | 1<<11;
+  GNUNET_MULTICAST_member_replay_fragment (member, replay_fragment_id,
+                                           replay_flags);
+}
+
+
+static void
+member_replay_error ()
+{
+  test = TEST_MEMBER_REPLAY_ERROR;
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Test #%u: member_replay_error()\n", test);
+  replay_fragment_id = 1234;
+  replay_flags = 11 | 1<<11;
+  GNUNET_MULTICAST_member_replay_fragment (member, replay_fragment_id,
+                                           replay_flags);
+}
+
+
+static void
+origin_recv_replay_msg (void *cls,
+                        const struct GNUNET_CRYPTO_EcdsaPublicKey *member_key,
+                        uint64_t message_id,
+                        uint64_t fragment_offset,
+                        uint64_t flags,
+                        struct GNUNET_MULTICAST_ReplayHandle *rh)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Test #%u: origin_recv_replay_msg()\n", test);
+  GNUNET_assert (0);
+}
+
+
+static void
+member_recv_replay_msg (void *cls,
+                        const struct GNUNET_CRYPTO_EcdsaPublicKey *member_key,
+                        uint64_t message_id,
+                        uint64_t fragment_offset,
+                        uint64_t flags,
+                        struct GNUNET_MULTICAST_ReplayHandle *rh)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Test #%u: member_recv_replay_msg()\n", test);
+  GNUNET_assert (0);
+}
+
+
+static void
+origin_recv_replay_frag (void *cls,
+                         const struct GNUNET_CRYPTO_EcdsaPublicKey *member_key,
+                         uint64_t fragment_id,
+                         uint64_t flags,
+                         struct GNUNET_MULTICAST_ReplayHandle *rh)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Test #%u: origin_recv_replay_frag()"
+              " - fragment_id=%" PRIu64 " flags=%" PRIu64 "\n",
+              test, fragment_id, flags);
+  GNUNET_assert (replay_fragment_id == fragment_id && replay_flags == flags);
+  switch (test)
+  {
+  case TEST_MEMBER_REPLAY_ERROR:
+    // Test 8 starts here
+    GNUNET_MULTICAST_replay_response (rh, NULL, GNUNET_SYSERR);
+    member_replay_ok ();
+    break;
+
+  case TEST_MEMBER_REPLAY_OK:
+  {
+    struct GNUNET_MULTICAST_MessageHeader mmsg = {
+      .header = {
+        .type = htons (GNUNET_MESSAGE_TYPE_MULTICAST_MESSAGE),
+        .size = htons (sizeof (mmsg)),
+      },
+      .fragment_id = GNUNET_htonll (1),
+      .message_id = GNUNET_htonll (1),
+      .fragment_offset = 0,
+      .group_generation = GNUNET_htonll (1),
+      .flags = 0,
+    };
+    member_cls.n = 0;
+    member_cls.msgs_expected = 1;
+    GNUNET_MULTICAST_replay_response (rh, &mmsg.header, 
GNUNET_MULTICAST_REC_OK);
+    GNUNET_MULTICAST_replay_response_end (rh);
+    break;
+  }
+
+  default:
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Invalid test #%d in origin_recv_replay_frag()\n", test);
+    GNUNET_assert (0);
+  }
+}
+
+
+static void
+member_recv_replay_frag (void *cls,
+                         const struct GNUNET_CRYPTO_EcdsaPublicKey *member_key,
+                         uint64_t fragment_id,
+                         uint64_t flags,
+                         struct GNUNET_MULTICAST_ReplayHandle *rh)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Test #%u: member_recv_replay_frag()\n", test);
+  GNUNET_assert (0);
+}
+
+
+static void
+origin_recv_request (void *cls,
+                     const struct GNUNET_MULTICAST_RequestHeader *req)
+{
+  struct OriginClosure *ocls = cls;
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Test #%u: origin_recv_request()\n", test);
+  if (++ocls->n != ocls->msgs_expected)
+    return;
+
+  GNUNET_assert (0 == memcmp (&req->member_pub_key,
+                              &member_pub_key, sizeof (member_pub_key)));
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Test #%u: verify message content, take first 3 bytes: %.3s\n",
+              test, (char *)&req[1]);
+  GNUNET_assert (0 == memcmp (&req[1], "abc", 3));
+
+  // Test 7 starts here
+  member_replay_error ();
+}
+
+
+static void
+member_to_origin ()
+{
+  test = TEST_MEMBER_TO_ORIGIN;
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Test #%u: member_to_origin()\n", test);
+
+  struct TransmitClosure *tmit = &tmit_cls;
+  *tmit = (struct TransmitClosure) {};
+  tmit->data[0] = "abc def";
+  tmit->data[1] = "ghi jkl mno";
+  tmit->data_delay[1] = 2;
+  tmit->data[2] = "pqr stuw xyz";
+  tmit->data_count = 3;
+
+  origin_cls.n = 0;
+  origin_cls.msgs_expected = 1;
+
+  tmit->mem_tmit = GNUNET_MULTICAST_member_to_origin (member, 1,
+                                                      tmit_notify, tmit);
+}
+
+
+static void
+member_recv_message (void *cls,
+                     const struct GNUNET_MULTICAST_MessageHeader *msg)
+{
+  struct MemberClosure *mcls = cls;
+
+  // Test 5 starts here after message has been received from origin
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Test #%u: member_recv_message() %u/%u\n",
+              test,
+              (unsigned int) (mcls->n + 1),
+              mcls->msgs_expected);
+  if (++mcls->n != mcls->msgs_expected)
+    return;
+
+  // FIXME: check message content
+
+  switch (test)
+  {
+  case TEST_ORIGIN_TO_ALL:
+    test = TEST_ORIGIN_TO_ALL_RECV;
+    break;
+
+  case TEST_ORIGIN_TO_ALL_RECV:
+    // Test 6 starts here
+    member_to_origin ();
+    break;
+
+  case TEST_MEMBER_REPLAY_OK:
+    // Test 9 starts here
+    GNUNET_assert (replay_fragment_id == GNUNET_ntohll (msg->fragment_id));
+    member_part ();
+    break;
+
+  default:
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Invalid test #%d in origin_recv_message()\n", test);
+    GNUNET_assert (0);
+  }
+}
+
+
+static void
+origin_recv_message (void *cls,
+                     const struct GNUNET_MULTICAST_MessageHeader *msg)
+{
+  struct OriginClosure *ocls = cls;
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Test #%u: origin_recv_message() %u/%u\n",
+              test, ocls->n + 1, ocls->msgs_expected);
+  if (++ocls->n != ocls->msgs_expected)
+    return;
+
+  // FIXME: check message content
+
+  switch (test)
+  {
+  case TEST_ORIGIN_TO_ALL:
+    // Prepare to execute test 5
+    test = TEST_ORIGIN_TO_ALL_RECV;
+    break;
+
+  case TEST_ORIGIN_TO_ALL_RECV:
+    // Test 6 starts here
+    member_to_origin ();
+    break;
+
+  default:
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Invalid test #%d in origin_recv_message()\n", test);
+    GNUNET_assert (0);
+  }
+}
+
+
+static void
+origin_to_all ()
+{
+  test = TEST_ORIGIN_TO_ALL;
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Test #%u: origin_to_all()\n", test);
+
+  struct TransmitClosure *tmit = &tmit_cls;
+  *tmit = (struct TransmitClosure) {};
+  tmit->data[0] = "ABC DEF";
+  tmit->data[1] =  GNUNET_malloc (GNUNET_MULTICAST_FRAGMENT_MAX_PAYLOAD + 1);
+  uint16_t i;
+  for (i = 0; i < GNUNET_MULTICAST_FRAGMENT_MAX_PAYLOAD; i++)
+    tmit->data[1][i] = (0 == i % 10000) ? '0' + i / 10000 : '_';
+  tmit->data[2] = "GHI JKL MNO";
+  tmit->data_delay[2] = 2;
+  tmit->data[3] = "PQR STUW XYZ";
+  tmit->data_count = 4;
+
+  origin_cls.n = member_cls.n = 0;
+  origin_cls.msgs_expected = member_cls.msgs_expected = tmit->data_count;
+
+  tmit->orig_tmit = GNUNET_MULTICAST_origin_to_all (origin, 1, 1,
+                                                    tmit_notify, tmit);
+}
+
+
+static void
+member_recv_join_decision (void *cls,
+                           int is_admitted,
+                           const struct GNUNET_PeerIdentity *peer,
+                           uint16_t relay_count,
+                           const struct GNUNET_PeerIdentity *relays,
+                           const struct GNUNET_MessageHeader *join_msg)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Test #%u: member_recv_join_decision() - is_admitted: %d\n",
+              test, is_admitted);
+
+  GNUNET_assert (join_msg->size == join_resp->size);
+  GNUNET_assert (join_msg->type == join_resp->type);
+  GNUNET_assert (0 == memcmp (join_msg, join_resp, ntohs (join_resp->size)));
+
+  switch (test)
+  {
+  case TEST_MEMBER_JOIN_REFUSE:
+    GNUNET_assert (0 == relay_count);
+    // Test 3 starts here
+    GNUNET_SCHEDULER_add_now (&schedule_member_part, NULL);
+    break;
+
+  case TEST_MEMBER_JOIN_ADMIT:
+    GNUNET_assert (1 == relay_count);
+    GNUNET_assert (0 == memcmp (relays, &this_peer, sizeof (this_peer)));
+    // Test 4 starts here
+    origin_to_all ();
+    break;
+
+  default:
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Invalid test #%d in member_recv_join_decision()\n", test);
+    GNUNET_assert (0);
+  }
+}
+
+/**
+ * Test: origin receives join request
+ */
+static void
+origin_recv_join_request (void *cls,
+                          const struct GNUNET_CRYPTO_EcdsaPublicKey *mem_key,
+                          const struct GNUNET_MessageHeader *join_msg,
+                          struct GNUNET_MULTICAST_JoinHandle *jh)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Test #%u: origin_recv_join_request()\n", test);
+
+  GNUNET_assert (0 == memcmp (mem_key, &member_pub_key, sizeof 
(member_pub_key)));
+  GNUNET_assert (join_msg->size == join_req->size);
+  GNUNET_assert (join_msg->type == join_req->type);
+  GNUNET_assert (0 == memcmp (join_msg, join_req, ntohs (join_req->size)));
+
+  char data[] = "here's the decision";
+  uint8_t data_size = strlen (data) + 1;
+  join_resp = GNUNET_malloc (sizeof (join_resp) + data_size);
+  join_resp->size = htons (sizeof (join_resp) + data_size);
+  join_resp->type = htons (456);
+  GNUNET_memcpy (&join_resp[1], data, data_size);
+
+  switch (test)
+  {
+  case TEST_MEMBER_JOIN_REFUSE:
+    // Test 3 starts here
+    GNUNET_MULTICAST_join_decision (jh, GNUNET_NO, 0, NULL, join_resp);
+    break;
+
+  case TEST_MEMBER_JOIN_ADMIT:
+    // Test 3 is running
+    GNUNET_MULTICAST_join_decision (jh, GNUNET_YES, 1, &this_peer, join_resp);
+    break;
+
+  default:
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Invalid test #%d in origin_recv_join_request()\n", test);
+    GNUNET_assert (0);
+    break;
+  }
+}
+
+/**
+ * Test: member joins multicast group
+ */
+static void
+member_join (int t)
+{
+  test = t;
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Test #%u: member_join()\n", test);
+
+  member_key = GNUNET_CRYPTO_ecdsa_key_create ();
+  GNUNET_CRYPTO_ecdsa_key_get_public (member_key, &member_pub_key);
+
+  if (NULL != join_req)
+    GNUNET_free (join_req);
+
+  char data[] = "let me in!";
+  uint8_t data_size = strlen (data) + 1;
+  join_req = GNUNET_malloc (sizeof (join_req) + data_size);
+  join_req->size = htons (sizeof (join_req) + data_size);
+  join_req->type = htons (123);
+  GNUNET_memcpy (&join_req[1], data, data_size);
+
+  member = GNUNET_MULTICAST_member_join (cfg, &group_pub_key, member_key,
+                                         &this_peer, 1, &this_peer, join_req,
+                                         member_recv_join_request,
+                                         member_recv_join_decision,
+                                         member_recv_replay_frag,
+                                         member_recv_replay_msg,
+                                         member_recv_message,
+                                         &member_cls);
+}
+
+/**
+ * Test: Start a multicast group as origin
+ */
+static void
+origin_start ()
+{
+  test = TEST_ORIGIN_START;
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Test #%u: origin_start()\n", test);
+
+  group_key = GNUNET_CRYPTO_eddsa_key_create ();
+  GNUNET_CRYPTO_eddsa_key_get_public (group_key, &group_pub_key);
+
+  origin = GNUNET_MULTICAST_origin_start (cfg, group_key, 0,
+                                          origin_recv_join_request,
+                                          origin_recv_replay_frag,
+                                          origin_recv_replay_msg,
+                                          origin_recv_request,
+                                          origin_recv_message,
+                                          &origin_cls);
+  // Test 2 starts here
+  member_join (TEST_MEMBER_JOIN_REFUSE);
+}
+
+
+/**
+ * Main function of the test, run from scheduler.
+ *
+ * @param cls NULL
+ * @param cfg configuration we use (also to connect to Multicast service)
+ * @param peer handle to access more of the peer (not used)
+ */
+static void
+#if DEBUG_TEST_MULTICAST
+run (void *cls,
+     char *const *args,
+     const char *cfgfile,
+     const struct GNUNET_CONFIGURATION_Handle *c)
+#else
+run (void *cls,
+     const struct GNUNET_CONFIGURATION_Handle *c,
+     struct GNUNET_TESTING_Peer *peer)
+#endif
+{
+  cfg = c;
+  end_badly_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT,
+                                                &end_badly, NULL);
+  GNUNET_CRYPTO_get_peer_identity (cfg, &this_peer);
+
+  // Test 1 starts here
+  origin_start ();
+}
+
+
+int
+main (int argc, char *argv[])
+{
+  res = 1;
+#if DEBUG_TEST_MULTICAST
+  const struct GNUNET_GETOPT_CommandLineOption opts[] = {
+    GNUNET_GETOPT_OPTION_END
+  };
+  if (GNUNET_OK != GNUNET_PROGRAM_run (argc, argv, "test-multicast",
+                                       "test-multicast [options]",
+                                       opts, &run, NULL))
+    return 1;
+#else
+  if (0 != GNUNET_TESTING_peer_run ("test-multicast", "test_multicast.conf", 
&run, NULL))
+    return 1;
+#endif
+  return res;
+}
+
+/* end of test_multicast.c */
diff --git a/src/multicast/test_multicast.conf 
b/src/multicast/test_multicast.conf
new file mode 100644
index 0000000..b2f1a76
--- /dev/null
+++ b/src/multicast/test_multicast.conf
@@ -0,0 +1,56 @@
+[testbed]
+HOSTNAME = localhost
+
+[arm]
+GLOBAL_POSTFIX=-L ERROR
+
+[multicast]
+#PREFIX = tmux new-window gdb -x ./cmd.gdb  --args
+#PREFIX = valgrind --leak-check=full
+UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-multicast.sock
+
+[vpn]
+START_ON_DEMAND = NO
+
+[peerinfo]
+# Do not use shipped gnunet HELLOs 
+USE_INCLUDED_HELLOS = NO
+
+# Option to disable all disk IO; only useful for testbed runs
+# (large-scale experiments); disables persistence of HELLOs!
+NO_IO = YES
+
+[hostlist]
+IMMEDIATE_START = NO
+START_ON_DEMAND = NO
+
+[nat]
+ENABLE_UPNP = NO
+
+[fs]
+IMMEDIATE_START = NO
+START_ON_DEMAND = NO
+
+[vpn]
+IMMEDIATE_START = NO
+START_ON_DEMAND = NO
+
+[revocation]
+IMMEDIATE_START = NO
+START_ON_DEMAND = NO
+
+[gns]
+IMMEDIATE_START = NO
+START_ON_DEMAND = NO
+
+[namestore]
+IMMEDIATE_START = NO
+START_ON_DEMAND = NO
+
+[namecache]
+IMMEDIATE_START = NO
+START_ON_DEMAND = NO
+
+[topology]
+IMMEDIATE_START = NO
+START_ON_DEMAND = NO
diff --git a/src/multicast/test_multicast_2peers.c 
b/src/multicast/test_multicast_2peers.c
new file mode 100644
index 0000000..ea99602
--- /dev/null
+++ b/src/multicast/test_multicast_2peers.c
@@ -0,0 +1,520 @@
+/*
+ * This file is part of GNUnet
+ * Copyright (C) 2013 GNUnet e.V.
+ *
+ * GNUnet is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * GNUnet 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
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+     SPDX-License-Identifier: AGPL3.0-or-later
+ */
+
+/**
+ * @file multicast/test_multicast_2peers.c
+ * @brief Tests for the Multicast API with two peers doing the ping
+ *        pong test.
+ * @author xrs
+ */
+
+#include <inttypes.h>
+
+#include "platform.h"
+#include "gnunet_crypto_lib.h"
+#include "gnunet_common.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_testbed_service.h"
+#include "gnunet_multicast_service.h"
+
+#define NUM_PEERS 2
+
+static struct GNUNET_TESTBED_Operation *op0;
+static struct GNUNET_TESTBED_Operation *op1;
+static struct GNUNET_TESTBED_Operation *pi_op0;
+static struct GNUNET_TESTBED_Operation *pi_op1;
+
+static struct GNUNET_TESTBED_Peer **peers;
+const struct GNUNET_PeerIdentity *peer_id[2];
+
+static struct GNUNET_SCHEDULER_Task *timeout_tid;
+
+static struct GNUNET_MULTICAST_Origin *origin;
+static struct GNUNET_MULTICAST_Member *member;
+
+struct GNUNET_CRYPTO_EddsaPrivateKey *group_key;
+struct GNUNET_CRYPTO_EddsaPublicKey group_pub_key;
+
+struct GNUNET_CRYPTO_EcdsaPrivateKey *member_key;
+struct GNUNET_CRYPTO_EcdsaPublicKey member_pub_key;
+
+/**
+ * Global result for testcase.
+ */
+static int result;
+
+
+/**
+ * Function run on CTRL-C or shutdown (i.e. success/timeout/etc.).
+ * Cleans up.
+ */
+static void
+shutdown_task (void *cls)
+{
+  if (NULL != op0)
+  {
+    GNUNET_TESTBED_operation_done (op0);
+    op0 = NULL;
+  }
+  if (NULL != op1)
+  {
+    GNUNET_TESTBED_operation_done (op1);
+    op1 = NULL;
+  }
+  if (NULL != pi_op0)
+  {
+    GNUNET_TESTBED_operation_done (pi_op0);
+    pi_op0 = NULL;
+  }
+  if (NULL != pi_op1)
+  {
+    GNUNET_TESTBED_operation_done (pi_op1);
+    pi_op1 = NULL;
+  }
+  if (NULL != timeout_tid)
+    {
+      GNUNET_SCHEDULER_cancel (timeout_tid);
+      timeout_tid = NULL;
+    }
+}
+
+
+static void
+timeout_task (void *cls)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+             "Timeout!\n");
+  result = GNUNET_SYSERR;
+  GNUNET_SCHEDULER_shutdown ();
+}
+
+
+static void
+member_join_request (void *cls,
+                     const struct GNUNET_CRYPTO_EcdsaPublicKey *member_pub_key,
+                     const struct GNUNET_MessageHeader *join_msg,
+                     struct GNUNET_MULTICAST_JoinHandle *jh)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Member sent a join request.\n");
+
+}
+
+
+static int
+notify (void *cls,
+        size_t *data_size,
+        void *data)
+{
+
+  char text[] = "ping";
+  *data_size = strlen(text)+1;
+  GNUNET_memcpy(data, text, *data_size);
+
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Member sents message to origin: %s\n", text);
+
+  return GNUNET_YES;
+}
+
+
+static void
+member_join_decision (void *cls,
+                      int is_admitted,
+                      const struct GNUNET_PeerIdentity *peer,
+                      uint16_t relay_count,
+                      const struct GNUNET_PeerIdentity *relays,
+                      const struct GNUNET_MessageHeader *join_msg)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Member received a decision from origin: %s\n",
+              (GNUNET_YES == is_admitted)
+              ? "accepted"
+              : "rejected");
+
+  if (GNUNET_YES == is_admitted)
+  {
+    struct GNUNET_MULTICAST_MemberTransmitHandle *req;
+
+    // FIXME: move to MQ-style API!
+    req = GNUNET_MULTICAST_member_to_origin (member,
+                                             0,
+                                             &notify,
+                                             NULL);
+  }
+}
+
+
+static void
+member_message (void *cls,
+                const struct GNUNET_MULTICAST_MessageHeader *msg)
+{
+  if (0 != strncmp ("pong", (char *)&msg[1], 4))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "member did not receive pong\n");
+    result = GNUNET_SYSERR;
+    GNUNET_SCHEDULER_shutdown ();
+  }
+
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "member receives: %s\n", (char *)&msg[1]);
+
+  // Testcase ends here.
+  result = GNUNET_YES;
+  GNUNET_SCHEDULER_shutdown ();
+}
+
+
+static void
+origin_join_request (void *cls,
+                 const struct GNUNET_CRYPTO_EcdsaPublicKey *member_pub_key,
+                 const struct GNUNET_MessageHeader *join_msg,
+                 struct GNUNET_MULTICAST_JoinHandle *jh)
+{
+  struct GNUNET_MessageHeader *join_resp;
+
+  uint8_t data_size = ntohs (join_msg->size);
+
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "origin got a join request...\n");
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "origin receives: '%s'\n", (char *)&join_msg[1]);
+
+  const char data[] = "Come in!";
+  data_size = strlen (data) + 1;
+  join_resp = GNUNET_malloc (sizeof (join_resp) + data_size);
+  join_resp->size = htons (sizeof (join_resp) + data_size);
+  join_resp->type = htons (123);
+  GNUNET_memcpy (&join_resp[1], data, data_size);
+
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "origin sends: '%s'\n", data);
+
+  GNUNET_MULTICAST_join_decision (jh,
+                                  GNUNET_YES,
+                                  0,
+                                  NULL,
+                                  join_resp);
+  GNUNET_free (join_resp);
+  result = GNUNET_OK;
+}
+
+
+int
+origin_notify (void *cls,
+               size_t *data_size,
+               void *data)
+{
+  char text[] = "pong";
+
+  *data_size = strlen(text)+1;
+  GNUNET_memcpy (data,
+                 text,
+                 *data_size);
+
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO, "origin sends (to all): %s\n", text);
+
+  return GNUNET_YES;
+}
+
+
+static void
+origin_request (void *cls,
+                const struct GNUNET_MULTICAST_RequestHeader *req)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO, "origin receives: %s\n", (char 
*)&req[1]);
+
+  if (0 != strncmp ("ping", (char *)&req[1], 4))
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "origin didn't reveice a correct 
request");
+
+  GNUNET_MULTICAST_origin_to_all (origin,
+                                  0,
+                                  0,
+                                  origin_notify,
+                                  NULL);
+}
+
+
+static void
+origin_message (void *cls,
+                const struct GNUNET_MULTICAST_MessageHeader *msg)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO, "origin message msg\n");
+}
+
+
+static void
+service_connect1 (void *cls,
+                  struct GNUNET_TESTBED_Operation *op,
+                  void *ca_result,
+                  const char *emsg)
+{
+  member = ca_result;
+
+  if (NULL == member)
+  {
+    result = GNUNET_SYSERR;
+    GNUNET_SCHEDULER_shutdown ();
+  }
+  else
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Connected to multicast service of 
member\n");
+  }
+}
+
+
+static void
+multicast_da1 (void *cls,
+               void * op_result)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Member parting from multicast group\n");
+
+  GNUNET_MULTICAST_member_part (member, NULL, NULL);
+}
+
+
+static void *
+multicast_ca1 (void *cls,
+               const struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+  struct GNUNET_MessageHeader *join_msg;
+  void *ret;
+
+  // Get members keys
+  member_key = GNUNET_CRYPTO_ecdsa_key_create ();
+  GNUNET_CRYPTO_ecdsa_key_get_public (member_key, &member_pub_key);
+
+  char data[] = "Hi, can I enter?";
+  uint8_t data_size = strlen (data) + 1;
+  join_msg = GNUNET_malloc (sizeof (join_msg) + data_size);
+  join_msg->size = htons (sizeof (join_msg) + data_size);
+  join_msg->type = htons (123);
+  GNUNET_memcpy (&join_msg[1], data, data_size);
+
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Members tries to join multicast group\n");
+
+  ret = GNUNET_MULTICAST_member_join (cfg,
+                                       &group_pub_key,
+                                       member_key,
+                                       peer_id[0],
+                                       0,
+                                       NULL,
+                                       join_msg, /* join message */
+                                       member_join_request,
+                                       member_join_decision,
+                                       NULL, /* no test for member_replay_frag 
*/
+                                       NULL, /* no test for member_replay_msg 
*/
+                                       member_message,
+                                       NULL);
+  GNUNET_free (join_msg);
+  return ret;
+}
+
+
+static void
+peer_information_cb (void *cls,
+                     struct GNUNET_TESTBED_Operation *op,
+                     const struct GNUNET_TESTBED_PeerInformation *pinfo,
+                     const char *emsg)
+{
+  int i = (int) (long) cls;
+
+  if (NULL == pinfo)
+  {
+    result = GNUNET_SYSERR;
+    GNUNET_SCHEDULER_shutdown ();
+  }
+
+  peer_id[i] = pinfo->result.id;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Got peer information of %s (%s)\n", (0==i)?"origin":"member" 
,GNUNET_i2s(pinfo->result.id));
+
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Create member peer\n");
+
+  if (0 == i)
+  {
+    /* connect to multicast service of member */
+    op1 = GNUNET_TESTBED_service_connect (NULL,                    /* Closure 
for operation */
+                                          peers[1],                /* The peer 
whose service to connect to */
+                                          "multicast",             /* The name 
of the service */
+                                          service_connect1,   /* callback to 
call after a handle to service
+                                                                 is opened */
+                                          NULL,                    /* closure 
for the above callback */
+                                          multicast_ca1,      /* callback to 
call with peer's configuration;
+                                                                 this should 
open the needed service connection */
+                                          multicast_da1,     /* callback to be 
called when closing the
+                                                                opened service 
connection */
+                                          NULL);                   /* closure 
for the above two callbacks */
+  }
+}
+
+
+/**
+ * Test logic of peer "0" being origin starts here.
+ *
+ * @param cls closure, for the example: NULL
+ * @param op should be equal to "dht_op"
+ * @param ca_result result of the connect operation, the
+ *        connection to the DHT service
+ * @param emsg error message, if testbed somehow failed to
+ *        connect to the DHT.
+ */
+static void
+service_connect0 (void *cls,
+                  struct GNUNET_TESTBED_Operation *op,
+                  void *ca_result,
+                  const char *emsg)
+{
+  origin = ca_result;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Connected to multicast service of origin\n");
+
+  // Get GNUnet identity of origin
+  pi_op0 = GNUNET_TESTBED_peer_get_information (peers[0],
+                                               GNUNET_TESTBED_PIT_IDENTITY,
+                                               peer_information_cb,
+                                               (void *) 0);
+  // Get GNUnet identity of member
+  pi_op1 = GNUNET_TESTBED_peer_get_information (peers[1],
+                                               GNUNET_TESTBED_PIT_IDENTITY,
+                                               peer_information_cb,
+                                               (void *) 1);
+
+  /* Connection to service successful. Here we'd usually do something with
+   * the service. */
+  result = GNUNET_OK;
+  //GNUNET_SCHEDULER_shutdown (); /* Also kills the testbed */
+}
+
+
+
+/**
+ * Function run when service multicast has started and is providing us
+ * with a configuration file.
+ */
+static void *
+multicast_ca0 (void *cls,
+               const struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+  group_key = GNUNET_CRYPTO_eddsa_key_create ();
+  GNUNET_CRYPTO_eddsa_key_get_public (group_key, &group_pub_key);
+
+  return GNUNET_MULTICAST_origin_start (cfg,
+                                        group_key,
+                                        0,
+                                        origin_join_request,
+                                        NULL, /* no test for 
origin_replay_frag */
+                                        NULL, /* no test for origin_replay_msg 
*/
+                                        origin_request,
+                                        origin_message,
+                                        NULL);
+}
+
+static void
+multicast_da0 (void *cls,
+               void *op_result)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Origin closes multicast group\n");
+
+  GNUNET_MULTICAST_origin_stop (origin, NULL, NULL);
+}
+
+
+/**
+ * Main function inovked from TESTBED once all of the
+ * peers are up and running.  This one then connects
+ * just to the multicast service of peer 0 and 1.
+ * Peer 0 is going to be origin.
+ * Peer 1 is going to be one member.
+ * Origin will start a multicast group and the member will try to join it.
+ * After that we execute some multicast test.
+ *
+ * @param cls closure
+ * @param h the run handle
+ * @param peers started peers for the test
+ * @param num_peers size of the 'peers' array
+ * @param links_succeeded number of links between peers that were created
+ * @param links_failed number of links testbed was unable to establish
+ */
+static void
+testbed_master (void *cls,
+     struct GNUNET_TESTBED_RunHandle *h,
+     unsigned int num_peers,
+     struct GNUNET_TESTBED_Peer **p,
+     unsigned int links_succeeded,
+     unsigned int links_failed)
+{
+  /* Testbed is ready with peers running and connected in a pre-defined overlay
+     topology (FIXME)  */
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Connected to testbed_master()\n");
+
+  peers = p;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Create origin peer\n");
+  op0 = GNUNET_TESTBED_service_connect (NULL,                    /* Closure 
for operation */
+                                        peers[0],                /* The peer 
whose service to connect to */
+                                        "multicast",             /* The name 
of the service */
+                                        service_connect0,   /* callback to 
call after a handle to service
+                                                               is opened */
+                                        NULL,                    /* closure 
for the above callback */
+                                        multicast_ca0,      /* callback to 
call with peer's configuration;
+                                                               this should 
open the needed service connection */
+                                        multicast_da0,     /* callback to be 
called when closing the
+                                                              opened service 
connection */
+                                        NULL);                   /* closure 
for the above two callbacks */
+
+  GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL); /* Schedule a new task 
on shutdown */
+
+  /* Schedule the shutdown task with a delay of a few Seconds */
+  timeout_tid = GNUNET_SCHEDULER_add_delayed 
(GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 50),
+                                             &timeout_task, NULL);
+}
+
+
+int
+main (int argc, char *argv[])
+{
+  int ret;
+
+  result = GNUNET_SYSERR;
+  ret = GNUNET_TESTBED_test_run
+      ("test-multicast-2peers",  /* test case name */
+       "test_multicast.conf", /* template configuration */
+       NUM_PEERS,       /* number of peers to start */
+       0LL, /* Event mask - set to 0 for no event notifications */
+       NULL, /* Controller event callback */
+       NULL, /* Closure for controller event callback */
+       testbed_master, /* continuation callback to be called when testbed 
setup is complete */
+       NULL); /* Closure for the test_master callback */
+  if ( (GNUNET_OK != ret) || (GNUNET_OK != result) )
+    return 1;
+  return 0;
+}
+
+
+/* end of test_multicast_2peers.c */
diff --git a/src/multicast/test_multicast_line.conf 
b/src/multicast/test_multicast_line.conf
new file mode 100644
index 0000000..c1ce7c6
--- /dev/null
+++ b/src/multicast/test_multicast_line.conf
@@ -0,0 +1,63 @@
+[testbed]
+HOSTNAME = localhost
+OVERLAY_TOPOLOGY = LINE
+
+[arm]
+GLOBAL_POSTFIX=-L ERROR
+
+[multicast]
+#PREFIX = tmux new-window gdb -x ./cmd.gdb  --args
+#PREFIX = valgrind --leak-check=full
+UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-multicast.sock
+
+[vpn]
+START_ON_DEMAND = NO
+
+[peerinfo]
+# Do not use shipped gnunet HELLOs 
+USE_INCLUDED_HELLOS = NO
+
+# Option to disable all disk IO; only useful for testbed runs
+# (large-scale experiments); disables persistence of HELLOs!
+NO_IO = YES
+
+[cadet]
+ID_ANNOUNCE_TIME = 5 s
+
+[hostlist]
+IMMEDIATE_START = NO
+START_ON_DEMAND = NO
+
+[nat]
+ENABLE_UPNP = NO
+
+[fs]
+IMMEDIATE_START = NO
+START_ON_DEMAND = NO
+
+[vpn]
+IMMEDIATE_START = NO
+START_ON_DEMAND = NO
+
+[revocation]
+IMMEDIATE_START = NO
+START_ON_DEMAND = NO
+
+[gns]
+IMMEDIATE_START = NO
+START_ON_DEMAND = NO
+
+[namestore]
+IMMEDIATE_START = NO
+START_ON_DEMAND = NO
+
+[namecache]
+IMMEDIATE_START = NO
+START_ON_DEMAND = NO
+
+[topology]
+IMMEDIATE_START = NO
+START_ON_DEMAND = NO
+
+[nse]
+WORKBITS = 0
diff --git a/src/multicast/test_multicast_multipeer.c 
b/src/multicast/test_multicast_multipeer.c
new file mode 100644
index 0000000..9b44e05
--- /dev/null
+++ b/src/multicast/test_multicast_multipeer.c
@@ -0,0 +1,643 @@
+/*
+ * This file is part of GNUnet
+ * Copyright (C) 2013 GNUnet e.V.
+ *
+ * GNUnet is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * GNUnet 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
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+     SPDX-License-Identifier: AGPL3.0-or-later
+ */
+
+/**
+ * @file multicast/test_multicast_multipeers.c
+ * @brief Tests for the Multicast API with multiple peers.
+ * @author xrs
+ */
+
+#include <inttypes.h>
+
+#include "platform.h"
+#include "gnunet_crypto_lib.h"
+#include "gnunet_common.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_testbed_service.h"
+#include "gnunet_multicast_service.h"
+
+#define PEERS_REQUESTED 12
+
+struct MulticastPeerContext
+{
+  int peer; /* peer number */
+  struct GNUNET_CRYPTO_EcdsaPrivateKey *key;
+  const struct GNUNET_PeerIdentity *id;
+  struct GNUNET_TESTBED_Operation *op; /* not yet in use */
+  struct GNUNET_TESTBED_Operation *pi_op; /* not yet in use */
+  int test_ok;
+};
+
+enum pingpong
+{
+  PING = 1,
+  PONG = 2
+};
+
+struct pingpong_msg
+{
+  int peer;
+  enum pingpong msg;
+};
+
+static void service_connect (void *cls,
+                             struct GNUNET_TESTBED_Operation *op,
+                             void *ca_result,
+                             const char *emsg);
+
+static struct MulticastPeerContext **multicast_peers;
+static struct GNUNET_TESTBED_Peer **peers;
+
+static struct GNUNET_TESTBED_Operation *op[PEERS_REQUESTED];
+static struct GNUNET_TESTBED_Operation *pi_op[PEERS_REQUESTED];
+
+static struct GNUNET_MULTICAST_Origin *origin;
+static struct GNUNET_MULTICAST_Member *members[PEERS_REQUESTED]; /* first 
element always empty */
+
+static struct GNUNET_SCHEDULER_Task *timeout_tid;
+
+static struct GNUNET_CRYPTO_EddsaPrivateKey *group_key;
+static struct GNUNET_CRYPTO_EddsaPublicKey group_pub_key;
+static struct GNUNET_HashCode group_pub_key_hash;
+
+/**
+ * Global result for testcase.
+ */
+static int result;
+
+/**
+ * Function run on CTRL-C or shutdown (i.e. success/timeout/etc.).
+ * Cleans up.
+ */
+static void
+shutdown_task (void *cls)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "shutdown_task!\n");
+  for (int i=0;i<PEERS_REQUESTED;i++)
+  {
+    if (NULL != op[i])
+    {
+      GNUNET_TESTBED_operation_done(op[i]);
+      op[i] = NULL;
+    }
+    if (NULL != pi_op[i])
+    {
+      GNUNET_TESTBED_operation_done (pi_op[i]);
+      pi_op[i] = NULL;
+    }
+  }
+
+  if (NULL != multicast_peers)
+  {
+    for (int i=0; i < PEERS_REQUESTED; i++)
+    {
+      GNUNET_free_non_null (multicast_peers[i]->key);
+      GNUNET_free (multicast_peers[i]);
+      multicast_peers[i] = NULL;
+    }
+    GNUNET_free (multicast_peers);
+    multicast_peers = NULL;
+  }
+
+  if (NULL != timeout_tid)
+  {
+    GNUNET_SCHEDULER_cancel (timeout_tid);
+    timeout_tid = NULL;
+  }
+}
+
+
+static void
+timeout_task (void *cls)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+             "Timeout!\n");
+  result = GNUNET_SYSERR;
+  GNUNET_SCHEDULER_shutdown ();
+}
+
+
+static void
+member_join_request (void *cls,
+                     const struct GNUNET_CRYPTO_EcdsaPublicKey *member_pub_key,
+                     const struct GNUNET_MessageHeader *join_msg,
+                     struct GNUNET_MULTICAST_JoinHandle *jh)
+{
+  struct MulticastPeerContext *mc_peer = (struct MulticastPeerContext*)cls;
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Peer #%u (%s) sent a join request.\n",
+              mc_peer->peer,
+              GNUNET_i2s (multicast_peers[mc_peer->peer]->id));
+}
+
+
+static int
+notify (void *cls,
+        size_t *data_size,
+        void *data)
+{
+  struct MulticastPeerContext *mc_peer = (struct MulticastPeerContext*)cls;
+
+  struct pingpong_msg *pp_msg = GNUNET_new (struct pingpong_msg);
+  pp_msg->peer = mc_peer->peer;
+  pp_msg->msg = PING;
+
+  *data_size = sizeof (struct pingpong_msg);
+  GNUNET_memcpy(data, pp_msg, *data_size);
+  GNUNET_free (pp_msg);
+
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Peer #%u sents ping to origin\n", mc_peer->peer);
+
+  return GNUNET_YES;
+}
+
+
+static void
+member_join_decision (void *cls,
+                      int is_admitted,
+                      const struct GNUNET_PeerIdentity *peer,
+                      uint16_t relay_count,
+                      const struct GNUNET_PeerIdentity *relays,
+                      const struct GNUNET_MessageHeader *join_msg)
+{
+  struct MulticastPeerContext *mc_peer = (struct MulticastPeerContext*)cls;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Peer #%u (%s) received a decision from origin: %s\n",
+              mc_peer->peer,
+              GNUNET_i2s (multicast_peers[mc_peer->peer]->id),
+              (GNUNET_YES == is_admitted)?"accepted":"rejected");
+
+  if (GNUNET_YES == is_admitted)
+  {
+    GNUNET_MULTICAST_member_to_origin (members[mc_peer->peer],
+                                       0,
+                                       notify,
+                                       cls);
+
+  }
+}
+
+
+static void
+member_replay_frag ()
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "member replay frag...\n");
+}
+
+
+static void
+member_replay_msg ()
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "member replay msg...\n");
+}
+
+
+static void
+origin_disconnected_cb (void *cls)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Origin disconnected. Shutting down.\n");
+  result = GNUNET_YES;
+  GNUNET_SCHEDULER_shutdown ();
+}
+
+
+static void
+member_disconnected_cb (void *cls)
+{
+  for (int i = 1; i < PEERS_REQUESTED; ++i)
+    if (GNUNET_NO == multicast_peers[i]->test_ok)
+      return;
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "All member disconnected. Stopping origin.\n");
+  GNUNET_MULTICAST_origin_stop (origin, origin_disconnected_cb, cls);
+}
+
+
+static void
+member_message (void *cls,
+                const struct GNUNET_MULTICAST_MessageHeader *msg)
+{
+  struct MulticastPeerContext *mc_peer = (struct MulticastPeerContext*)cls;
+  struct pingpong_msg *pp_msg = (struct pingpong_msg*) &(msg[1]);
+
+  if (PONG == pp_msg->msg && mc_peer->peer == pp_msg->peer)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "peer #%i (%s) receives a pong\n",
+                mc_peer->peer,
+                GNUNET_i2s (multicast_peers[mc_peer->peer]->id));
+    mc_peer->test_ok = GNUNET_OK;
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "peer #%u (%s) parting from multicast group\n",
+                mc_peer->peer,
+                GNUNET_i2s (multicast_peers[mc_peer->peer]->id));
+
+    GNUNET_MULTICAST_member_part (members[mc_peer->peer], 
member_disconnected_cb, cls);
+  }
+}
+
+
+static void
+origin_join_request (void *cls,
+                 const struct GNUNET_CRYPTO_EcdsaPublicKey *member_pub_key,
+                 const struct GNUNET_MessageHeader *join_msg,
+                 struct GNUNET_MULTICAST_JoinHandle *jh)
+{
+  struct GNUNET_MessageHeader *join_resp;
+
+  uint8_t data_size = ntohs (join_msg->size);
+
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "origin got a join request...\n");
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "origin receives: '%s'\n", (char *)&join_msg[1]);
+
+  char data[] = "Come in!";
+  data_size = strlen (data) + 1;
+  join_resp = GNUNET_malloc (sizeof (join_resp) + data_size);
+  join_resp->size = htons (sizeof (join_resp) + data_size);
+  join_resp->type = htons (123);
+  GNUNET_memcpy (&join_resp[1], data, data_size);
+
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "origin sends: '%s'\n", data);
+
+  GNUNET_MULTICAST_join_decision (jh,
+                                  GNUNET_YES,
+                                  0,
+                                  NULL,
+                                  join_resp);
+
+  result = GNUNET_OK;
+}
+
+
+static void
+origin_replay_frag (void *cls,
+                    const struct GNUNET_CRYPTO_EcdsaPublicKey *member_pub_key,
+                    uint64_t fragment_id,
+                    uint64_t flags,
+                    struct GNUNET_MULTICAST_ReplayHandle *rh)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO, "origin replay fraq msg\n");
+}
+
+
+static void
+origin_replay_msg (void *cls,
+                   const struct GNUNET_CRYPTO_EcdsaPublicKey *member_pub_key,
+                   uint64_t message_id,
+                   uint64_t fragment_offset,
+                   uint64_t flags,
+                   struct GNUNET_MULTICAST_ReplayHandle *rh)
+{
+
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO, "origin replay msg\n");
+}
+
+
+static int
+origin_notify (void *cls,
+               size_t *data_size,
+               void *data)
+{
+  struct pingpong_msg *rcv_pp_msg = (struct pingpong_msg*)cls;
+  struct pingpong_msg *pp_msg = GNUNET_new (struct pingpong_msg);
+
+  pp_msg->peer = rcv_pp_msg->peer;
+  pp_msg->msg = PONG;
+  *data_size = sizeof (struct pingpong_msg);
+  GNUNET_memcpy(data, pp_msg, *data_size);
+  GNUNET_free (pp_msg);
+
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO, "origin sends pong\n");
+
+  return GNUNET_YES;
+}
+
+
+static void
+origin_request (void *cls,
+                const struct GNUNET_MULTICAST_RequestHeader *req)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO, "origin receives a msg\n");
+
+  req++;
+  struct pingpong_msg *pp_msg = (struct pingpong_msg *) req;
+
+  if (1 != pp_msg->msg) {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "origin didn't reveice a correct 
request");
+  }
+
+  GNUNET_MULTICAST_origin_to_all (origin,
+                                  0,
+                                  0,
+                                  origin_notify,
+                                  pp_msg);
+}
+
+
+static void
+origin_message (void *cls,
+                const struct GNUNET_MULTICAST_MessageHeader *msg)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO, "origin message msg\n");
+}
+
+
+static void
+multicast_disconnect (void *cls,
+                      void *op_result)
+{
+
+}
+
+
+static void *
+multicast_connect (void *cls,
+                   const struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+  struct MulticastPeerContext *multicast_peer = cls;
+  struct GNUNET_MessageHeader *join_msg;
+  char data[64];
+
+  if (0 == multicast_peer->peer)
+  {
+    group_key = GNUNET_CRYPTO_eddsa_key_create ();
+    GNUNET_CRYPTO_eddsa_key_get_public (group_key, &group_pub_key);
+
+    GNUNET_CRYPTO_hash (&group_pub_key, sizeof (group_pub_key), 
&group_pub_key_hash);
+    origin = GNUNET_MULTICAST_origin_start (cfg,
+                                            group_key,
+                                            0,
+                                            origin_join_request,
+                                            origin_replay_frag,
+                                            origin_replay_msg,
+                                            origin_request,
+                                            origin_message,
+                                            cls);
+    if (NULL == origin)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                  "Peer #%u could not create a multicast group",
+                  multicast_peer->peer);
+      return NULL;
+    }
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "Peer #%u connected as origin to group %s\n",
+                multicast_peer->peer,
+                GNUNET_h2s (&group_pub_key_hash));
+    return origin;
+  }
+  else
+  {
+    multicast_peer->key = GNUNET_CRYPTO_ecdsa_key_create ();
+
+    sprintf(data, "Hi, I am peer #%u (%s). Can I enter?",
+            multicast_peer->peer,
+            GNUNET_i2s (multicast_peers[multicast_peer->peer]->id));
+    uint8_t data_size = strlen (data) + 1;
+    join_msg = GNUNET_malloc (sizeof (join_msg) + data_size);
+    join_msg->size = htons (sizeof (join_msg) + data_size);
+    join_msg->type = htons (123);
+    GNUNET_memcpy (&join_msg[1], data, data_size);
+
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "Peer #%u (%s) tries to join multicast group %s\n",
+                multicast_peer->peer,
+                GNUNET_i2s (multicast_peers[multicast_peer->peer]->id),
+                GNUNET_h2s (&group_pub_key_hash));
+
+    members[multicast_peer->peer] =
+      GNUNET_MULTICAST_member_join (cfg,
+                                    &group_pub_key,
+                                    multicast_peer->key,
+                                    multicast_peers[0]->id,
+                                    0,
+                                    NULL,
+                                    join_msg, /* join message */
+                                    member_join_request,
+                                    member_join_decision,
+                                    member_replay_frag,
+                                    member_replay_msg,
+                                    member_message,
+                                    cls);
+    return members[multicast_peer->peer];
+  }
+}
+
+
+static void
+peer_information_cb (void *cls,
+                     struct GNUNET_TESTBED_Operation *operation,
+                     const struct GNUNET_TESTBED_PeerInformation *pinfo,
+                     const char *emsg)
+{
+  struct MulticastPeerContext *mc_peer = (struct MulticastPeerContext*)cls;
+
+  if (NULL == pinfo) {
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO, "got no peer information\n");
+    result = GNUNET_SYSERR;
+    GNUNET_SCHEDULER_shutdown ();
+  }
+
+  multicast_peers[mc_peer->peer]->id = pinfo->result.id;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Got peer information of %s (%s)\n",
+              (0 == mc_peer->peer)? "origin" : "member",
+              GNUNET_i2s (pinfo->result.id));
+
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Create peer #%u (%s)\n",
+              mc_peer->peer,
+              GNUNET_i2s (multicast_peers[mc_peer->peer]->id));
+
+  if (0 != mc_peer->peer)
+  {
+    /* connect to multicast service of members */
+    op[mc_peer->peer] =
+      GNUNET_TESTBED_service_connect (/* Closure for operation */
+                                      NULL,
+                                      /* The peer whose service to connect to 
*/
+                                      peers[mc_peer->peer],
+                                      /* The name of the service */
+                                      "multicast",
+                                      /* called after a handle to service is 
opened */
+                                      service_connect,
+                                      /* closure for the above callback */
+                                      cls,
+                                      /* called when opening the service 
connection */
+                                      multicast_connect,
+                                      /* called when closing the service 
connection */
+                                      multicast_disconnect,
+                                      /* closure for the above two callbacks */
+                                      cls);
+  }
+}
+
+
+static void
+service_connect (void *cls,
+                 struct GNUNET_TESTBED_Operation *op,
+                 void *ca_result,
+                 const char *emsg)
+{
+  struct MulticastPeerContext *mc_peer = (struct MulticastPeerContext*)cls;
+
+  if (NULL == ca_result)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "Connection adapter not created for peer #%u (%s)\n",
+                mc_peer->peer,
+                GNUNET_i2s (multicast_peers[mc_peer->peer]->id));
+
+    result = GNUNET_SYSERR;
+    GNUNET_SCHEDULER_shutdown();
+  }
+
+  if (0 == mc_peer->peer)
+  {
+    // Get GNUnet identity of members
+    for (int i = 0; i<PEERS_REQUESTED; i++)
+    {
+      pi_op[i] = GNUNET_TESTBED_peer_get_information (peers[i],
+                                                      
GNUNET_TESTBED_PIT_IDENTITY,
+                                                      peer_information_cb,
+                                                      multicast_peers[i]);
+    }
+  }
+}
+
+
+
+/**
+ * Main function inovked from TESTBED once all of the
+ * peers are up and running.  This one then connects
+ * just to the multicast service of peer 0 and 1.
+ * Peer 0 is going to be origin.
+ * Peer 1 is going to be one member.
+ * Origin will start a multicast group and the member will try to join it.
+ * After that we execute some multicast test.
+ *
+ * @param cls closure
+ * @param h the run handle
+ * @param peers started peers for the test
+ * @param PEERS_REQUESTED size of the 'peers' array
+ * @param links_succeeded number of links between peers that were created
+ * @param links_failed number of links testbed was unable to establish
+ */
+static void
+testbed_master (void *cls,
+                struct GNUNET_TESTBED_RunHandle *h,
+                unsigned int num_peers,
+                struct GNUNET_TESTBED_Peer **p,
+                unsigned int links_succeeded,
+                unsigned int links_failed)
+{
+  /* Testbed is ready with peers running and connected in a pre-defined overlay
+     topology (FIXME)  */
+  peers = p;
+  multicast_peers = GNUNET_new_array (PEERS_REQUESTED, struct 
MulticastPeerContext*);
+
+  // Create test contexts for members
+  for (int i = 0; i<PEERS_REQUESTED; i++)
+  {
+    multicast_peers[i] = GNUNET_new (struct MulticastPeerContext);
+    multicast_peers[i]->peer = i;
+    multicast_peers[i]->test_ok = GNUNET_NO;
+  }
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Create origin peer\n");
+  op[0] =
+    GNUNET_TESTBED_service_connect (/* Closure for operation */
+                                    NULL,
+                                    /* The peer whose service to connect to */
+                                    peers[0],
+                                    /* The name of the service */
+                                    "multicast",
+                                    /* called after a handle to service is 
opened */
+                                    service_connect,
+                                    /* closure for the above callback */
+                                    multicast_peers[0],
+                                    /* called when opening the service 
connection */
+                                    multicast_connect,
+                                    /* called when closing the service 
connection */
+                                    multicast_disconnect,
+                                    /* closure for the above two callbacks */
+                                    multicast_peers[0]);
+  /* Schedule a new task on shutdown */
+  GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL);
+  /* Schedule the shutdown task with a delay of a few Seconds */
+  timeout_tid =
+    GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
+                                  (GNUNET_TIME_UNIT_SECONDS, 400),
+                                 &timeout_task,
+                                  NULL);
+}
+
+
+int
+main (int argc, char *argv[])
+{
+  int ret;
+  char const *config_file;
+
+  if (strstr (argv[0], "_line") != NULL)
+  {
+    config_file = "test_multicast_line.conf";
+  }
+  else if (strstr(argv[0], "_star") != NULL)
+  {
+    config_file = "test_multicast_star.conf";
+  }
+  else
+  {
+    config_file = "test_multicast_star.conf";
+  }
+
+  result = GNUNET_SYSERR;
+  ret =
+    GNUNET_TESTBED_test_run ("test-multicast-multipeer",
+                             config_file,
+                             /* number of peers to start */
+                             PEERS_REQUESTED,
+                             /* Event mask - set to 0 for no event 
notifications */
+                             0LL,
+                             /* Controller event callback */
+                             NULL,
+                             /* Closure for controller event callback */
+                             NULL,
+                             /* called when testbed setup is complete */
+                             testbed_master,
+                             /* Closure for the test_master callback */
+                             NULL);
+  if ( (GNUNET_OK != ret) || (GNUNET_OK != result) )
+    return 1;
+  return 0;
+}
+
+/* end of test_multicast_multipeer.c */
diff --git a/src/multicast/test_multicast_star.conf 
b/src/multicast/test_multicast_star.conf
new file mode 100644
index 0000000..516c0e3
--- /dev/null
+++ b/src/multicast/test_multicast_star.conf
@@ -0,0 +1,64 @@
+[testbed]
+HOSTNAME = localhost
+OVERLAY_TOPOLOGY = STAR
+
+[arm]
+GLOBAL_POSTFIX=-L ERROR
+
+[multicast]
+#PREFIX = tmux new-window gdb -x ./cmd.gdb  --args
+#PREFIX = valgrind --leak-check=full
+UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-multicast.sock
+
+[vpn]
+START_ON_DEMAND = NO
+
+[peerinfo]
+# Do not use shipped gnunet HELLOs 
+USE_INCLUDED_HELLOS = NO
+
+# Option to disable all disk IO; only useful for testbed runs
+# (large-scale experiments); disables persistence of HELLOs!
+NO_IO = YES
+
+[cadet]
+ID_ANNOUNCE_TIME = 5 s
+
+[hostlist]
+IMMEDIATE_START = NO
+START_ON_DEMAND = NO
+
+[nat]
+ENABLE_UPNP = NO
+
+[fs]
+IMMEDIATE_START = NO
+START_ON_DEMAND = NO
+
+[vpn]
+IMMEDIATE_START = NO
+START_ON_DEMAND = NO
+
+[revocation]
+IMMEDIATE_START = NO
+START_ON_DEMAND = NO
+
+[gns]
+IMMEDIATE_START = NO
+START_ON_DEMAND = NO
+
+[namestore]
+IMMEDIATE_START = NO
+START_ON_DEMAND = NO
+
+[namecache]
+IMMEDIATE_START = NO
+START_ON_DEMAND = NO
+
+[topology]
+IMMEDIATE_START = NO
+START_ON_DEMAND = NO
+
+[nse]
+WORKBITS = 0
+
diff --git a/src/psyc/.gitignore b/src/psyc/.gitignore
new file mode 100644
index 0000000..14a1753
--- /dev/null
+++ b/src/psyc/.gitignore
@@ -0,0 +1,2 @@
+gnunet-service-psyc
+test_psyc
diff --git a/src/psyc/Makefile.am b/src/psyc/Makefile.am
new file mode 100644
index 0000000..511e3e3
--- /dev/null
+++ b/src/psyc/Makefile.am
@@ -0,0 +1,77 @@
+# This Makefile.am is in the public domain
+AM_CPPFLAGS = -I$(top_srcdir)/src/include
+
+pkgcfgdir= $(pkgdatadir)/config.d/
+
+libexecdir= $(pkglibdir)/libexec/
+
+pkgcfg_DATA = \
+  psyc.conf
+
+
+if MINGW
+ WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols
+endif
+
+if USE_COVERAGE
+  AM_CFLAGS = --coverage -O0
+  XLIB = -lgcov
+endif
+
+lib_LTLIBRARIES = libgnunetpsyc.la
+
+libgnunetpsyc_la_SOURCES = \
+  psyc_api.c psyc.h
+libgnunetpsyc_la_LIBADD = \
+  $(top_builddir)/src/util/libgnunetutil.la \
+  $(top_builddir)/src/psycutil/libgnunetpsycutil.la \
+  $(GN_LIBINTL) $(XLIB)
+libgnunetpsyc_la_LDFLAGS = \
+  $(GN_LIB_LDFLAGS)  $(WINFLAGS) \
+  -version-info 0:0:0
+
+bin_PROGRAMS =
+
+libexec_PROGRAMS = \
+  gnunet-service-psyc
+
+gnunet_service_psyc_SOURCES = \
+  gnunet-service-psyc.c
+gnunet_service_psyc_LDADD = \
+  $(top_builddir)/src/util/libgnunetutil.la \
+  $(top_builddir)/src/statistics/libgnunetstatistics.la \
+  $(top_builddir)/src/multicast/libgnunetmulticast.la \
+  $(top_builddir)/src/psycstore/libgnunetpsycstore.la \
+  $(top_builddir)/src/psycutil/libgnunetpsycutil.la \
+  $(GN_LIBINTL)
+gnunet_service_psyc_CFLAGS = $(AM_CFLAGS)
+
+
+if HAVE_TESTING
+check_PROGRAMS = \
+ test_psyc
+# test_psyc2
+endif
+
+if ENABLE_TEST_RUN
+AM_TESTS_ENVIRONMENT=export 
GNUNET_PREFIX=$${GNUNET_PREFIX:address@hidden@};export 
PATH=$${GNUNET_PREFIX:address@hidden@}/bin:$$PATH;unset XDG_DATA_HOME;unset 
XDG_CONFIG_HOME;
+TESTS = $(check_PROGRAMS)
+endif
+
+test_psyc_SOURCES = \
+ test_psyc.c
+test_psyc_LDADD = \
+  libgnunetpsyc.la \
+  $(top_builddir)/src/psycutil/libgnunetpsycutil.la \
+  $(top_builddir)/src/testing/libgnunettesting.la \
+  $(top_builddir)/src/util/libgnunetutil.la
+#test_psyc2_SOURCES = \
+#  test_psyc2.c
+#test_psyc2_LDADD = \
+#  libgnunetpsyc.la \
+#  $(top_builddir)/src/psycutil/libgnunetpsycutil.la \
+#  $(top_builddir)/src/testbed/libgnunettestbed.la \
+#  $(top_builddir)/src/util/libgnunetutil.la
+
+EXTRA_DIST = \
+  test_psyc.conf
diff --git a/src/psyc/gnunet-service-psyc.c b/src/psyc/gnunet-service-psyc.c
new file mode 100644
index 0000000..6f2f7a9
--- /dev/null
+++ b/src/psyc/gnunet-service-psyc.c
@@ -0,0 +1,2860 @@
+/*
+ * This file is part of GNUnet
+ * Copyright (C) 2013 GNUnet e.V.
+ *
+ * GNUnet is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * GNUnet 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
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+     SPDX-License-Identifier: AGPL3.0-or-later
+ */
+
+/**
+ * @file psyc/gnunet-service-psyc.c
+ * @brief PSYC service
+ * @author Gabor X Toth
+ */
+
+#include <inttypes.h>
+
+#include "platform.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_constants.h"
+#include "gnunet_protocols.h"
+#include "gnunet_statistics_service.h"
+#include "gnunet_multicast_service.h"
+#include "gnunet_psycstore_service.h"
+#include "gnunet_psyc_service.h"
+#include "gnunet_psyc_util_lib.h"
+#include "psyc.h"
+
+
+/**
+ * Handle to our current configuration.
+ */
+static const struct GNUNET_CONFIGURATION_Handle *cfg;
+
+/**
+ * Service handle.
+ */
+static struct GNUNET_SERVICE_Handle *service;
+
+/**
+ * Handle to the statistics service.
+ */
+static struct GNUNET_STATISTICS_Handle *stats;
+
+/**
+ * Handle to the PSYCstore.
+ */
+static struct GNUNET_PSYCSTORE_Handle *store;
+
+/**
+ * All connected masters.
+ * Channel's pub_key_hash -> struct Master
+ */
+static struct GNUNET_CONTAINER_MultiHashMap *masters;
+
+/**
+ * All connected slaves.
+ * Channel's pub_key_hash -> struct Slave
+ */
+static struct GNUNET_CONTAINER_MultiHashMap *slaves;
+
+/**
+ * Connected slaves per channel.
+ * Channel's pub_key_hash -> Slave's pub_key -> struct Slave
+ */
+static struct GNUNET_CONTAINER_MultiHashMap *channel_slaves;
+
+
+/**
+ * Message in the transmission queue.
+ */
+struct TransmitMessage
+{
+  struct TransmitMessage *prev;
+  struct TransmitMessage *next;
+
+  struct GNUNET_SERVICE_Client *client;
+
+  /**
+   * ID assigned to the message.
+   */
+  uint64_t id;
+
+  /**
+   * Size of message.
+   */
+  uint16_t size;
+
+  /**
+   * Type of first message part.
+   */
+  uint16_t first_ptype;
+
+  /**
+   * Type of last message part.
+   */
+  uint16_t last_ptype;
+
+  /* Followed by message */
+};
+
+
+/**
+ * Cache for received message fragments.
+ * Message fragments are only sent to clients after all modifiers arrived.
+ *
+ * chan_key -> MultiHashMap chan_msgs
+ */
+static struct GNUNET_CONTAINER_MultiHashMap *recv_cache;
+
+
+/**
+ * Entry in the chan_msgs hashmap of @a recv_cache:
+ * fragment_id -> RecvCacheEntry
+ */
+struct RecvCacheEntry
+{
+  struct GNUNET_MULTICAST_MessageHeader *mmsg;
+  uint16_t ref_count;
+};
+
+
+/**
+ * Entry in the @a recv_frags hash map of a @a Channel.
+ * message_id -> FragmentQueue
+ */
+struct FragmentQueue
+{
+  /**
+   * Fragment IDs stored in @a recv_cache.
+   */
+  struct GNUNET_CONTAINER_Heap *fragments;
+
+  /**
+   * Total size of received fragments.
+   */
+  uint64_t size;
+
+  /**
+   * Total size of received header fragments (METHOD & MODIFIERs)
+   */
+  uint64_t header_size;
+
+  /**
+   * The @a state_delta field from struct GNUNET_PSYC_MessageMethod.
+   */
+  uint64_t state_delta;
+
+  /**
+   * The @a flags field from struct GNUNET_PSYC_MessageMethod.
+   */
+  uint32_t flags;
+
+  /**
+   * Receive state of message.
+   *
+   * @see MessageFragmentState
+   */
+  uint8_t state;
+
+  /**
+   * Whether the state is already modified in PSYCstore.
+   */
+  uint8_t state_is_modified;
+
+  /**
+   * Is the message queued for delivery to the client?
+   * i.e. added to the recv_msgs queue
+   */
+  uint8_t is_queued;
+};
+
+
+/**
+ * List of connected clients.
+ */
+struct ClientList
+{
+  struct ClientList *prev;
+  struct ClientList *next;
+
+  struct GNUNET_SERVICE_Client *client;
+};
+
+
+struct Operation
+{
+  struct Operation *prev;
+  struct Operation *next;
+
+  struct GNUNET_SERVICE_Client *client;
+  struct Channel *channel;
+  uint64_t op_id;
+  uint32_t flags;
+};
+
+
+/**
+ * Common part of the client context for both a channel master and slave.
+ */
+struct Channel
+{
+  struct ClientList *clients_head;
+  struct ClientList *clients_tail;
+
+  struct Operation *op_head;
+  struct Operation *op_tail;
+
+  struct TransmitMessage *tmit_head;
+  struct TransmitMessage *tmit_tail;
+
+  /**
+   * Current PSYCstore operation.
+   */
+  struct GNUNET_PSYCSTORE_OperationHandle *store_op;
+
+  /**
+   * Received fragments not yet sent to the client.
+   * message_id -> FragmentQueue
+   */
+  struct GNUNET_CONTAINER_MultiHashMap *recv_frags;
+
+  /**
+   * Received message IDs not yet sent to the client.
+   */
+  struct GNUNET_CONTAINER_Heap *recv_msgs;
+
+  /**
+   * Public key of the channel.
+   */
+  struct GNUNET_CRYPTO_EddsaPublicKey pub_key;
+
+  /**
+   * Hash of @a pub_key.
+   */
+  struct GNUNET_HashCode pub_key_hash;
+
+  /**
+   * Last message ID sent to the client.
+   * 0 if there is no such message.
+   */
+  uint64_t max_message_id;
+
+  /**
+   * ID of the last stateful message, where the state operations has been
+   * processed and saved to PSYCstore and which has been sent to the client.
+   * 0 if there is no such message.
+   */
+  uint64_t max_state_message_id;
+
+  /**
+   * Expected value size for the modifier being received from the PSYC service.
+   */
+  uint32_t tmit_mod_value_size_expected;
+
+  /**
+   * Actual value size for the modifier being received from the PSYC service.
+   */
+  uint32_t tmit_mod_value_size;
+
+  /**
+   * Is this channel ready to receive messages from client?
+   * #GNUNET_YES or #GNUNET_NO
+   */
+  uint8_t is_ready;
+
+  /**
+   * Is the client disconnected?
+   * #GNUNET_YES or #GNUNET_NO
+   */
+  uint8_t is_disconnecting;
+
+  /**
+   * Is this a channel master (#GNUNET_YES), or slave (#GNUNET_NO)?
+   */
+  uint8_t is_master;
+
+  union {
+    struct Master *master;
+    struct Slave *slave;
+  };
+};
+
+
+/**
+ * Client context for a channel master.
+ */
+struct Master
+{
+  /**
+   * Channel struct common for Master and Slave
+   */
+  struct Channel channel;
+
+  /**
+   * Private key of the channel.
+   */
+  struct GNUNET_CRYPTO_EddsaPrivateKey priv_key;
+
+  /**
+   * Handle for the multicast origin.
+   */
+  struct GNUNET_MULTICAST_Origin *origin;
+
+  /**
+   * Transmit handle for multicast.
+   */
+  struct GNUNET_MULTICAST_OriginTransmitHandle *tmit_handle;
+
+  /**
+   * Incoming join requests from multicast.
+   * member_pub_key -> struct GNUNET_MULTICAST_JoinHandle *
+   */
+  struct GNUNET_CONTAINER_MultiHashMap *join_reqs;
+
+  /**
+   * Last message ID transmitted to this channel.
+   *
+   * Incremented before sending a message, thus the message_id in messages sent
+   * starts from 1.
+   */
+  uint64_t max_message_id;
+
+  /**
+   * ID of the last message with state operations transmitted to the channel.
+   * 0 if there is no such message.
+   */
+  uint64_t max_state_message_id;
+
+  /**
+   * Maximum group generation transmitted to the channel.
+   */
+  uint64_t max_group_generation;
+
+  /**
+   * @see enum GNUNET_PSYC_Policy
+   */
+  enum GNUNET_PSYC_Policy policy;
+};
+
+
+/**
+ * Client context for a channel slave.
+ */
+struct Slave
+{
+  /**
+   * Channel struct common for Master and Slave
+   */
+  struct Channel channel;
+
+  /**
+   * Private key of the slave.
+   */
+  struct GNUNET_CRYPTO_EcdsaPrivateKey priv_key;
+
+  /**
+   * Public key of the slave.
+   */
+  struct GNUNET_CRYPTO_EcdsaPublicKey pub_key;
+
+  /**
+   * Hash of @a pub_key.
+   */
+  struct GNUNET_HashCode pub_key_hash;
+
+  /**
+   * Handle for the multicast member.
+   */
+  struct GNUNET_MULTICAST_Member *member;
+
+  /**
+   * Transmit handle for multicast.
+   */
+  struct GNUNET_MULTICAST_MemberTransmitHandle *tmit_handle;
+
+  /**
+   * Peer identity of the origin.
+   */
+  struct GNUNET_PeerIdentity origin;
+
+  /**
+   * Number of items in @a relays.
+   */
+  uint32_t relay_count;
+
+  /**
+   * Relays that multicast can use to connect.
+   */
+  struct GNUNET_PeerIdentity *relays;
+
+  /**
+   * Join request to be transmitted to the master on join.
+   */
+  struct GNUNET_PSYC_Message *join_msg;
+
+  /**
+   * Join decision received from multicast.
+   */
+  struct GNUNET_PSYC_JoinDecisionMessage *join_dcsn;
+
+  /**
+   * Maximum request ID for this channel.
+   */
+  uint64_t max_request_id;
+
+  /**
+   * Join flags.
+   */
+  enum GNUNET_PSYC_SlaveJoinFlags join_flags;
+};
+
+
+/**
+ * Client context.
+ */
+struct Client {
+  struct GNUNET_SERVICE_Client *client;
+  struct Channel *channel;
+};
+
+
+struct ReplayRequestKey
+{
+  uint64_t fragment_id;
+  uint64_t message_id;
+  uint64_t fragment_offset;
+  uint64_t flags;
+};
+
+
+static void
+transmit_message (struct Channel *chn);
+
+static uint64_t
+message_queue_run (struct Channel *chn);
+
+static uint64_t
+message_queue_drop (struct Channel *chn);
+
+
+static void
+schedule_transmit_message (void *cls)
+{
+  struct Channel *chn = cls;
+
+  transmit_message (chn);
+}
+
+
+/**
+ * Task run during shutdown.
+ *
+ * @param cls unused
+ */
+static void
+shutdown_task (void *cls)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "shutting down...\n");
+  GNUNET_PSYCSTORE_disconnect (store);
+  if (NULL != stats)
+  {
+    GNUNET_STATISTICS_destroy (stats, GNUNET_YES);
+    stats = NULL;
+  }
+}
+
+
+static struct Operation *
+op_add (struct Channel *chn, struct GNUNET_SERVICE_Client *client,
+        uint64_t op_id, uint32_t flags)
+{
+  struct Operation *op = GNUNET_malloc (sizeof (*op));
+  op->client = client;
+  op->channel = chn;
+  op->op_id = op_id;
+  op->flags = flags;
+  GNUNET_CONTAINER_DLL_insert (chn->op_head, chn->op_tail, op);
+  return op;
+}
+
+
+static void
+op_remove (struct Operation *op)
+{
+  GNUNET_CONTAINER_DLL_remove (op->channel->op_head, op->channel->op_tail, op);
+  GNUNET_free (op);
+}
+
+
+/**
+ * Clean up master data structures after a client disconnected.
+ */
+static void
+cleanup_master (struct Master *mst)
+{
+  struct Channel *chn = &mst->channel;
+
+  GNUNET_CONTAINER_multihashmap_destroy (mst->join_reqs);
+  GNUNET_CONTAINER_multihashmap_remove (masters, &chn->pub_key_hash, mst);
+}
+
+
+/**
+ * Clean up slave data structures after a client disconnected.
+ */
+static void
+cleanup_slave (struct Slave *slv)
+{
+  struct Channel *chn = &slv->channel;
+  struct GNUNET_CONTAINER_MultiHashMap *
+    chn_slv = GNUNET_CONTAINER_multihashmap_get (channel_slaves,
+                                                &chn->pub_key_hash);
+  GNUNET_assert (NULL != chn_slv);
+  GNUNET_CONTAINER_multihashmap_remove (chn_slv, &slv->pub_key_hash, slv);
+
+  if (0 == GNUNET_CONTAINER_multihashmap_size (chn_slv))
+  {
+    GNUNET_CONTAINER_multihashmap_remove (channel_slaves, &chn->pub_key_hash,
+                                          chn_slv);
+    GNUNET_CONTAINER_multihashmap_destroy (chn_slv);
+  }
+  GNUNET_CONTAINER_multihashmap_remove (slaves, &chn->pub_key_hash, slv);
+
+  if (NULL != slv->join_msg)
+  {
+    GNUNET_free (slv->join_msg);
+    slv->join_msg = NULL;
+  }
+  if (NULL != slv->relays)
+  {
+    GNUNET_free (slv->relays);
+    slv->relays = NULL;
+  }
+  GNUNET_CONTAINER_multihashmap_remove (slaves, &chn->pub_key_hash, slv);
+}
+
+
+/**
+ * Clean up channel data structures after a client disconnected.
+ */
+static void
+cleanup_channel (struct Channel *chn)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%p Cleaning up channel %s. master? %u\n",
+              chn,
+              GNUNET_h2s (&chn->pub_key_hash),
+              chn->is_master);
+  message_queue_drop (chn);
+  GNUNET_CONTAINER_multihashmap_destroy (chn->recv_frags);
+  chn->recv_frags = NULL;
+
+  if (NULL != chn->store_op)
+  {
+    GNUNET_PSYCSTORE_operation_cancel (chn->store_op);
+    chn->store_op = NULL;
+  }
+
+  (GNUNET_YES == chn->is_master)
+    ? cleanup_master (chn->master)
+    : cleanup_slave (chn->slave);
+  GNUNET_free (chn);
+}
+
+
+/**
+ * Called whenever a client is disconnected.
+ * Frees our resources associated with that client.
+ *
+ * @param cls closure
+ * @param client identification of the client
+ * @param app_ctx must match @a client
+ */
+static void
+client_notify_disconnect (void *cls,
+                          struct GNUNET_SERVICE_Client *client,
+                          void *app_ctx)
+{
+  struct Client *c = app_ctx;
+  struct Channel *chn = c->channel;
+  GNUNET_free (c);
+
+  if (NULL == chn)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "%p User context is NULL in client_notify_disconnect ()\n",
+                chn);
+    GNUNET_break (0);
+    return;
+  }
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%p Client %p (%s) disconnected from channel %s\n",
+              chn,
+              client,
+              (GNUNET_YES == chn->is_master) ? "master" : "slave",
+              GNUNET_h2s (&chn->pub_key_hash));
+
+  struct ClientList *cli = chn->clients_head;
+  while (NULL != cli)
+  {
+    if (cli->client == client)
+    {
+      GNUNET_CONTAINER_DLL_remove (chn->clients_head, chn->clients_tail, cli);
+      GNUNET_free (cli);
+      break;
+    }
+    cli = cli->next;
+  }
+
+  struct Operation *op = chn->op_head;
+  while (NULL != op)
+  {
+    if (op->client == client)
+    {
+      op->client = NULL;
+      break;
+    }
+    op = op->next;
+  }
+
+  if (NULL == chn->clients_head)
+  { /* Last client disconnected. */
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "%p Last client (%s) disconnected from channel %s\n",
+                chn,
+                (GNUNET_YES == chn->is_master) ? "master" : "slave",
+                GNUNET_h2s (&chn->pub_key_hash));
+    chn->is_disconnecting = GNUNET_YES;
+    cleanup_channel (chn);
+  }
+}
+
+
+/**
+ * A new client connected.
+ *
+ * @param cls NULL
+ * @param client client to add
+ * @param mq message queue for @a client
+ * @return @a client
+ */
+static void *
+client_notify_connect (void *cls,
+                       struct GNUNET_SERVICE_Client *client,
+                       struct GNUNET_MQ_Handle *mq)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Client connected: %p\n", client);
+
+  struct Client *c = GNUNET_malloc (sizeof (*c));
+  c->client = client;
+
+  return c;
+}
+
+
+/**
+ * Send message to all clients connected to the channel.
+ */
+static void
+client_send_msg (const struct Channel *chn,
+                 const struct GNUNET_MessageHeader *msg)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Sending message to clients of channel %p.\n",
+              chn);
+
+  struct ClientList *cli = chn->clients_head;
+  while (NULL != cli)
+  {
+    struct GNUNET_MQ_Envelope *
+      env = GNUNET_MQ_msg_copy (msg);
+
+    GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (cli->client),
+                    env);
+    cli = cli->next;
+  }
+}
+
+
+/**
+ * Send a result code back to the client.
+ *
+ * @param client
+ *        Client that should receive the result code.
+ * @param result_code
+ *        Code to transmit.
+ * @param op_id
+ *        Operation ID in network byte order.
+ * @param data
+ *        Data payload or NULL.
+ * @param data_size
+ *        Size of @a data.
+ */
+static void
+client_send_result (struct GNUNET_SERVICE_Client *client, uint64_t op_id,
+                    int64_t result_code, const void *data, uint16_t data_size)
+{
+  struct GNUNET_OperationResultMessage *res;
+  struct GNUNET_MQ_Envelope *
+    env = GNUNET_MQ_msg_extra (res,
+                               data_size,
+                               GNUNET_MESSAGE_TYPE_PSYC_RESULT_CODE);
+  res->result_code = GNUNET_htonll (result_code);
+  res->op_id = op_id;
+  if (0 < data_size)
+    GNUNET_memcpy (&res[1], data, data_size);
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+             "%p Sending result to client for OP ID %" PRIu64 ": %" PRId64 " 
(size: %u)\n",
+             client,
+              GNUNET_ntohll (op_id),
+              result_code,
+              data_size);
+
+  GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client), env);
+}
+
+
+/**
+ * Closure for join_mem_test_cb()
+ */
+struct JoinMemTestClosure
+{
+  struct GNUNET_CRYPTO_EcdsaPublicKey slave_pub_key;
+  struct Channel *channel;
+  struct GNUNET_MULTICAST_JoinHandle *join_handle;
+  struct GNUNET_PSYC_JoinRequestMessage *join_msg;
+};
+
+
+/**
+ * Membership test result callback used for join requests.
+ */
+static void
+join_mem_test_cb (void *cls, int64_t result,
+                  const char *err_msg, uint16_t err_msg_size)
+{
+  struct JoinMemTestClosure *jcls = cls;
+
+  if (GNUNET_NO == result && GNUNET_YES == jcls->channel->is_master)
+  { /* Pass on join request to client if this is a master channel */
+    struct Master *mst = jcls->channel->master;
+    struct GNUNET_HashCode slave_pub_hash;
+    GNUNET_CRYPTO_hash (&jcls->slave_pub_key, sizeof (jcls->slave_pub_key),
+                        &slave_pub_hash);
+    GNUNET_CONTAINER_multihashmap_put (mst->join_reqs, &slave_pub_hash, 
jcls->join_handle,
+                                       
GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
+    client_send_msg (jcls->channel, &jcls->join_msg->header);
+  }
+  else
+  {
+    if (GNUNET_SYSERR == result)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "Could not perform membership test (%.*s)\n",
+                  err_msg_size, err_msg);
+    }
+    // FIXME: add relays
+    GNUNET_MULTICAST_join_decision (jcls->join_handle, result, 0, NULL, NULL);
+  }
+  GNUNET_free (jcls->join_msg);
+  GNUNET_free (jcls);
+}
+
+
+/**
+ * Incoming join request from multicast.
+ */
+static void
+mcast_recv_join_request (void *cls,
+                         const struct GNUNET_CRYPTO_EcdsaPublicKey 
*slave_pub_key,
+                         const struct GNUNET_MessageHeader *join_msg,
+                         struct GNUNET_MULTICAST_JoinHandle *jh)
+{
+  struct Channel *chn = cls;
+  uint16_t join_msg_size = 0;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%p Got join request.\n",
+              chn);
+  if (NULL != join_msg)
+  {
+    if (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE == ntohs (join_msg->type))
+    {
+      join_msg_size = ntohs (join_msg->size);
+    }
+    else
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                  "%p Got join message with invalid type %u.\n",
+                  chn,
+                  ntohs (join_msg->type));
+    }
+  }
+
+  struct GNUNET_PSYC_JoinRequestMessage *
+    req = GNUNET_malloc (sizeof (*req) + join_msg_size);
+  req->header.size = htons (sizeof (*req) + join_msg_size);
+  req->header.type = htons (GNUNET_MESSAGE_TYPE_PSYC_JOIN_REQUEST);
+  req->slave_pub_key = *slave_pub_key;
+  if (0 < join_msg_size)
+    GNUNET_memcpy (&req[1], join_msg, join_msg_size);
+
+  struct JoinMemTestClosure *jcls = GNUNET_malloc (sizeof (*jcls));
+  jcls->slave_pub_key = *slave_pub_key;
+  jcls->channel = chn;
+  jcls->join_handle = jh;
+  jcls->join_msg = req;
+
+  GNUNET_PSYCSTORE_membership_test (store, &chn->pub_key, slave_pub_key,
+                                    chn->max_message_id, 0,
+                                    &join_mem_test_cb, jcls);
+}
+
+
+/**
+ * Join decision received from multicast.
+ */
+static void
+mcast_recv_join_decision (void *cls, int is_admitted,
+                          const struct GNUNET_PeerIdentity *peer,
+                          uint16_t relay_count,
+                          const struct GNUNET_PeerIdentity *relays,
+                          const struct GNUNET_MessageHeader *join_resp)
+{
+  struct Slave *slv = cls;
+  struct Channel *chn = &slv->channel;
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%p Got join decision: %d\n",
+              slv,
+              is_admitted);
+  if (GNUNET_YES == chn->is_ready)
+  {
+    /* Already admitted */
+    return;
+  }
+
+  uint16_t join_resp_size = (NULL != join_resp) ? ntohs (join_resp->size) : 0;
+  struct GNUNET_PSYC_JoinDecisionMessage *
+    dcsn = slv->join_dcsn = GNUNET_malloc (sizeof (*dcsn) + join_resp_size);
+  dcsn->header.size = htons (sizeof (*dcsn) + join_resp_size);
+  dcsn->header.type = htons (GNUNET_MESSAGE_TYPE_PSYC_JOIN_DECISION);
+  dcsn->is_admitted = htonl (is_admitted);
+  if (0 < join_resp_size)
+    GNUNET_memcpy (&dcsn[1], join_resp, join_resp_size);
+
+  client_send_msg (chn, &dcsn->header);
+
+  if (GNUNET_YES == is_admitted
+      && ! (GNUNET_PSYC_SLAVE_JOIN_LOCAL & slv->join_flags))
+  {
+    chn->is_ready = GNUNET_YES;
+  }
+}
+
+
+static int
+store_recv_fragment_replay (void *cls,
+                            struct GNUNET_MULTICAST_MessageHeader *msg,
+                            enum GNUNET_PSYCSTORE_MessageFlags flags)
+{
+  struct GNUNET_MULTICAST_ReplayHandle *rh = cls;
+
+  GNUNET_MULTICAST_replay_response (rh, &msg->header, GNUNET_MULTICAST_REC_OK);
+  return GNUNET_YES;
+}
+
+
+/**
+ * Received result of GNUNET_PSYCSTORE_fragment_get() for multicast replay.
+ */
+static void
+store_recv_fragment_replay_result (void *cls,
+                                   int64_t result,
+                                   const char *err_msg,
+                                   uint16_t err_msg_size)
+{
+  struct GNUNET_MULTICAST_ReplayHandle *rh = cls;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%p Fragment replay: PSYCSTORE returned %" PRId64 " (%.*s)\n",
+              rh,
+              result,
+              err_msg_size,
+              err_msg);
+  switch (result)
+  {
+  case GNUNET_YES:
+    break;
+
+  case GNUNET_NO:
+    GNUNET_MULTICAST_replay_response (rh, NULL,
+                                      GNUNET_MULTICAST_REC_NOT_FOUND);
+    return;
+
+  case GNUNET_PSYCSTORE_MEMBERSHIP_TEST_FAILED:
+    GNUNET_MULTICAST_replay_response (rh, NULL,
+                                      GNUNET_MULTICAST_REC_ACCESS_DENIED);
+    return;
+
+  case GNUNET_SYSERR:
+    GNUNET_MULTICAST_replay_response (rh, NULL,
+                                      GNUNET_MULTICAST_REC_INTERNAL_ERROR);
+    return;
+  }
+  /* GNUNET_MULTICAST_replay_response frees 'rh' when passed
+   * an error code, so it must be ensured no further processing
+   * is attempted on 'rh'. Maybe this should be refactored as
+   * it doesn't look very intuitive.   --lynX
+   */
+  GNUNET_MULTICAST_replay_response_end (rh);
+}
+
+
+/**
+ * Incoming fragment replay request from multicast.
+ */
+static void
+mcast_recv_replay_fragment (void *cls,
+                            const struct GNUNET_CRYPTO_EcdsaPublicKey 
*slave_pub_key,
+                            uint64_t fragment_id, uint64_t flags,
+                            struct GNUNET_MULTICAST_ReplayHandle *rh)
+
+{
+  struct Channel *chn = cls;
+  GNUNET_PSYCSTORE_fragment_get (store, &chn->pub_key, slave_pub_key,
+                                 fragment_id, fragment_id,
+                                 &store_recv_fragment_replay,
+                                 &store_recv_fragment_replay_result, rh);
+}
+
+
+/**
+ * Incoming message replay request from multicast.
+ */
+static void
+mcast_recv_replay_message (void *cls,
+                           const struct GNUNET_CRYPTO_EcdsaPublicKey 
*slave_pub_key,
+                           uint64_t message_id,
+                           uint64_t fragment_offset,
+                           uint64_t flags,
+                           struct GNUNET_MULTICAST_ReplayHandle *rh)
+{
+  struct Channel *chn = cls;
+  GNUNET_PSYCSTORE_message_get (store, &chn->pub_key, slave_pub_key,
+                                message_id, message_id, 1, NULL,
+                                &store_recv_fragment_replay,
+                                &store_recv_fragment_replay_result, rh);
+}
+
+
+/**
+ * Convert an uint64_t in network byte order to a HashCode
+ * that can be used as key in a MultiHashMap
+ */
+static inline void
+hash_key_from_nll (struct GNUNET_HashCode *key, uint64_t n)
+{
+  /* use little-endian order, as idx_of MultiHashMap casts key to unsigned int 
*/
+  /* TODO: use built-in byte swap functions if available */
+
+  n = ((n <<  8) & 0xFF00FF00FF00FF00ULL) | ((n >>  8) & 
0x00FF00FF00FF00FFULL);
+  n = ((n << 16) & 0xFFFF0000FFFF0000ULL) | ((n >> 16) & 
0x0000FFFF0000FFFFULL);
+
+  *key = (struct GNUNET_HashCode) {};
+  *((uint64_t *) key)
+    = (n << 32) | (n >> 32);
+}
+
+
+/**
+ * Convert an uint64_t in host byte order to a HashCode
+ * that can be used as key in a MultiHashMap
+ */
+static inline void
+hash_key_from_hll (struct GNUNET_HashCode *key, uint64_t n)
+{
+#if __BYTE_ORDER == __BIG_ENDIAN
+  hash_key_from_nll (key, n);
+#elif __BYTE_ORDER == __LITTLE_ENDIAN
+  *key = (struct GNUNET_HashCode) {};
+  *((uint64_t *) key) = n;
+#else
+  #error byteorder undefined
+#endif
+}
+
+
+/**
+ * Initialize PSYC message header.
+ */
+static inline void
+psyc_msg_init (struct GNUNET_PSYC_MessageHeader *pmsg,
+               const struct GNUNET_MULTICAST_MessageHeader *mmsg, uint32_t 
flags)
+{
+  uint16_t size = ntohs (mmsg->header.size);
+  uint16_t psize = sizeof (*pmsg) + size - sizeof (*mmsg);
+
+  pmsg->header.size = htons (psize);
+  pmsg->header.type = htons (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE);
+  pmsg->message_id = mmsg->message_id;
+  pmsg->fragment_offset = mmsg->fragment_offset;
+  pmsg->flags = htonl (flags);
+
+  GNUNET_memcpy (&pmsg[1], &mmsg[1], size - sizeof (*mmsg));
+}
+
+
+/**
+ * Create a new PSYC message from a multicast message for sending it to 
clients.
+ */
+static inline struct GNUNET_PSYC_MessageHeader *
+psyc_msg_new (const struct GNUNET_MULTICAST_MessageHeader *mmsg, uint32_t 
flags)
+{
+  struct GNUNET_PSYC_MessageHeader *pmsg;
+  uint16_t size = ntohs (mmsg->header.size);
+  uint16_t psize = sizeof (*pmsg) + size - sizeof (*mmsg);
+
+  pmsg = GNUNET_malloc (psize);
+  psyc_msg_init (pmsg, mmsg, flags);
+  return pmsg;
+}
+
+
+/**
+ * Send multicast message to all clients connected to the channel.
+ */
+static void
+client_send_mcast_msg (struct Channel *chn,
+                       const struct GNUNET_MULTICAST_MessageHeader *mmsg,
+                       uint32_t flags)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%p Sending multicast message to client. fragment_id: %" PRIu64 
", message_id: %" PRIu64 "\n",
+              chn,
+              GNUNET_ntohll (mmsg->fragment_id),
+              GNUNET_ntohll (mmsg->message_id));
+
+  struct GNUNET_PSYC_MessageHeader *
+    pmsg = GNUNET_PSYC_message_header_create (mmsg, flags);
+  client_send_msg (chn, &pmsg->header);
+  GNUNET_free (pmsg);
+}
+
+
+/**
+ * Send multicast request to all clients connected to the channel.
+ */
+static void
+client_send_mcast_req (struct Master *mst,
+                       const struct GNUNET_MULTICAST_RequestHeader *req)
+{
+  struct Channel *chn = &mst->channel;
+
+  struct GNUNET_PSYC_MessageHeader *pmsg;
+  uint16_t size = ntohs (req->header.size);
+  uint16_t psize = sizeof (*pmsg) + size - sizeof (*req);
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%p Sending multicast request to client. fragment_id: %" PRIu64 
", message_id: %" PRIu64 "\n",
+              chn,
+              GNUNET_ntohll (req->fragment_id),
+              GNUNET_ntohll (req->request_id));
+
+  pmsg = GNUNET_malloc (psize);
+  pmsg->header.size = htons (psize);
+  pmsg->header.type = htons (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE);
+  pmsg->message_id = req->request_id;
+  pmsg->fragment_offset = req->fragment_offset;
+  pmsg->flags = htonl (GNUNET_PSYC_MESSAGE_REQUEST);
+  pmsg->slave_pub_key = req->member_pub_key;
+  GNUNET_memcpy (&pmsg[1], &req[1], size - sizeof (*req));
+
+  client_send_msg (chn, &pmsg->header);
+
+  /* FIXME: save req to PSYCstore so that it can be resent later to clients */
+
+  GNUNET_free (pmsg);
+}
+
+
+/**
+ * Insert a multicast message fragment into the queue belonging to the message.
+ *
+ * @param chn          Channel.
+ * @param mmsg         Multicast message fragment.
+ * @param msg_id_hash  Message ID of @a mmsg in a struct GNUNET_HashCode.
+ * @param first_ptype  First PSYC message part type in @a mmsg.
+ * @param last_ptype   Last PSYC message part type in @a mmsg.
+ */
+static void
+fragment_queue_insert (struct Channel *chn,
+                       const struct GNUNET_MULTICAST_MessageHeader *mmsg,
+                       uint16_t first_ptype, uint16_t last_ptype)
+{
+  const uint16_t size = ntohs (mmsg->header.size);
+  const uint64_t frag_offset = GNUNET_ntohll (mmsg->fragment_offset);
+  struct GNUNET_CONTAINER_MultiHashMap
+    *chan_msgs = GNUNET_CONTAINER_multihashmap_get (recv_cache,
+                                                    &chn->pub_key_hash);
+
+  struct GNUNET_HashCode msg_id_hash;
+  hash_key_from_nll (&msg_id_hash, mmsg->message_id);
+
+  struct FragmentQueue
+    *fragq = GNUNET_CONTAINER_multihashmap_get (chn->recv_frags, &msg_id_hash);
+
+  if (NULL == fragq)
+  {
+    fragq = GNUNET_malloc (sizeof (*fragq));
+    fragq->state = MSG_FRAG_STATE_HEADER;
+    fragq->fragments
+      = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN);
+
+    GNUNET_CONTAINER_multihashmap_put (chn->recv_frags, &msg_id_hash, fragq,
+                                       
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
+
+    if (NULL == chan_msgs)
+    {
+      chan_msgs = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO);
+      GNUNET_CONTAINER_multihashmap_put (recv_cache, &chn->pub_key_hash, 
chan_msgs,
+                                         
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
+    }
+  }
+
+  struct GNUNET_HashCode frag_id_hash;
+  hash_key_from_nll (&frag_id_hash, mmsg->fragment_id);
+  struct RecvCacheEntry
+    *cache_entry = GNUNET_CONTAINER_multihashmap_get (chan_msgs, 
&frag_id_hash);
+  if (NULL == cache_entry)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "%p Adding message fragment to cache. message_id: %" PRIu64 ", 
fragment_id: %" PRIu64 "\n",
+                chn,
+                GNUNET_ntohll (mmsg->message_id),
+                GNUNET_ntohll (mmsg->fragment_id));
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "%p header_size: %" PRIu64 " + %u\n",
+                chn,
+                fragq->header_size,
+                size);
+    cache_entry = GNUNET_malloc (sizeof (*cache_entry));
+    cache_entry->ref_count = 1;
+    cache_entry->mmsg = GNUNET_malloc (size);
+    GNUNET_memcpy (cache_entry->mmsg, mmsg, size);
+    GNUNET_CONTAINER_multihashmap_put (chan_msgs, &frag_id_hash, cache_entry,
+                                       
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
+  }
+  else
+  {
+    cache_entry->ref_count++;
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "%p Message fragment is already in cache. message_id: %" 
PRIu64 ", fragment_id: %" PRIu64 ", ref_count: %u\n",
+                chn,
+                GNUNET_ntohll (mmsg->message_id),
+                GNUNET_ntohll (mmsg->fragment_id),
+                cache_entry->ref_count);
+  }
+
+  if (MSG_FRAG_STATE_HEADER == fragq->state)
+  {
+    if (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_METHOD == first_ptype)
+    {
+      struct GNUNET_PSYC_MessageMethod *
+        pmeth = (struct GNUNET_PSYC_MessageMethod *) &mmsg[1];
+      fragq->state_delta = GNUNET_ntohll (pmeth->state_delta);
+      fragq->flags = ntohl (pmeth->flags);
+    }
+
+    if (last_ptype < GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_DATA)
+    {
+      fragq->header_size += size;
+    }
+    else if (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_METHOD == first_ptype
+             || frag_offset == fragq->header_size)
+    { /* header is now complete */
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                  "%p Header of message %" PRIu64 " is complete.\n",
+                  chn,
+                  GNUNET_ntohll (mmsg->message_id));
+
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                  "%p Adding message %" PRIu64 " to queue.\n",
+                  chn,
+                  GNUNET_ntohll (mmsg->message_id));
+      fragq->state = MSG_FRAG_STATE_DATA;
+    }
+    else
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                  "%p Header of message %" PRIu64 " is NOT complete yet: %" 
PRIu64 " != %" PRIu64 "\n",
+                  chn,
+                  GNUNET_ntohll (mmsg->message_id),
+                  frag_offset,
+                  fragq->header_size);
+    }
+  }
+
+  switch (last_ptype)
+  {
+  case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END:
+    if (frag_offset == fragq->size)
+      fragq->state = MSG_FRAG_STATE_END;
+    else
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                  "%p Message %" PRIu64 " is NOT complete yet: %" PRIu64 " != 
%" PRIu64 "\n",
+                  chn,
+                  GNUNET_ntohll (mmsg->message_id),
+                  frag_offset,
+                  fragq->size);
+    break;
+
+  case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL:
+    /* Drop message without delivering to client if it's a single fragment */
+    fragq->state =
+      (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_METHOD == first_ptype)
+      ? MSG_FRAG_STATE_DROP
+      : MSG_FRAG_STATE_CANCEL;
+  }
+
+  switch (fragq->state)
+  {
+  case MSG_FRAG_STATE_DATA:
+  case MSG_FRAG_STATE_END:
+  case MSG_FRAG_STATE_CANCEL:
+    if (GNUNET_NO == fragq->is_queued)
+    {
+      GNUNET_CONTAINER_heap_insert (chn->recv_msgs, NULL,
+                                    GNUNET_ntohll (mmsg->message_id));
+      fragq->is_queued = GNUNET_YES;
+    }
+  }
+
+  fragq->size += size;
+  GNUNET_CONTAINER_heap_insert (fragq->fragments, NULL,
+                                GNUNET_ntohll (mmsg->fragment_id));
+}
+
+
+/**
+ * Run fragment queue of a message.
+ *
+ * Send fragments of a message in order to client, after all modifiers arrived
+ * from multicast.
+ *
+ * @param chn
+ *        Channel.
+ * @param msg_id
+ *        ID of the message @a fragq belongs to.
+ * @param fragq
+ *        Fragment queue of the message.
+ * @param drop
+ *        Drop message without delivering to client?
+ *        #GNUNET_YES or #GNUNET_NO.
+ */
+static void
+fragment_queue_run (struct Channel *chn, uint64_t msg_id,
+                    struct FragmentQueue *fragq, uint8_t drop)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%p Running message fragment queue for message %" PRIu64 " 
(state: %u).\n",
+              chn,
+              msg_id,
+              fragq->state);
+
+  struct GNUNET_CONTAINER_MultiHashMap
+    *chan_msgs = GNUNET_CONTAINER_multihashmap_get (recv_cache,
+                                                    &chn->pub_key_hash);
+  GNUNET_assert (NULL != chan_msgs);
+  uint64_t frag_id;
+
+  while (GNUNET_YES == GNUNET_CONTAINER_heap_peek2 (fragq->fragments, NULL,
+                                                    &frag_id))
+  {
+    struct GNUNET_HashCode frag_id_hash;
+    hash_key_from_hll (&frag_id_hash, frag_id);
+    struct RecvCacheEntry *cache_entry
+      = GNUNET_CONTAINER_multihashmap_get (chan_msgs, &frag_id_hash);
+    if (cache_entry != NULL)
+    {
+      if (GNUNET_NO == drop)
+      {
+        client_send_mcast_msg (chn, cache_entry->mmsg, 0);
+      }
+      if (cache_entry->ref_count <= 1)
+      {
+        GNUNET_CONTAINER_multihashmap_remove (chan_msgs, &frag_id_hash,
+                                              cache_entry);
+        GNUNET_free (cache_entry->mmsg);
+        GNUNET_free (cache_entry);
+      }
+      else
+      {
+        cache_entry->ref_count--;
+      }
+    }
+#if CACHE_AGING_IMPLEMENTED
+    else if (GNUNET_NO == drop)
+    {
+      /* TODO: fragment not in cache anymore, retrieve it from PSYCstore */
+    }
+#endif
+
+    GNUNET_CONTAINER_heap_remove_root (fragq->fragments);
+  }
+
+  if (MSG_FRAG_STATE_END <= fragq->state)
+  {
+    struct GNUNET_HashCode msg_id_hash;
+    hash_key_from_hll (&msg_id_hash, msg_id);
+
+    GNUNET_CONTAINER_multihashmap_remove (chn->recv_frags, &msg_id_hash, 
fragq);
+    GNUNET_CONTAINER_heap_destroy (fragq->fragments);
+    GNUNET_free (fragq);
+  }
+  else
+  {
+    fragq->is_queued = GNUNET_NO;
+  }
+}
+
+
+struct StateModifyClosure
+{
+  struct Channel *channel;
+  uint64_t msg_id;
+  struct GNUNET_HashCode msg_id_hash;
+};
+
+
+void
+store_recv_state_modify_result (void *cls, int64_t result,
+                                const char *err_msg, uint16_t err_msg_size)
+{
+  struct StateModifyClosure *mcls = cls;
+  struct Channel *chn = mcls->channel;
+  uint64_t msg_id = mcls->msg_id;
+
+  struct FragmentQueue *
+    fragq = GNUNET_CONTAINER_multihashmap_get (chn->recv_frags, 
&mcls->msg_id_hash);
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%p GNUNET_PSYCSTORE_state_modify() returned %" PRId64 " 
(%.*s)\n",
+              chn, result, err_msg_size, err_msg);
+
+  switch (result)
+  {
+  case GNUNET_OK:
+  case GNUNET_NO:
+    if (NULL != fragq)
+      fragq->state_is_modified = GNUNET_YES;
+    if (chn->max_state_message_id < msg_id)
+      chn->max_state_message_id = msg_id;
+    if (chn->max_message_id < msg_id)
+      chn->max_message_id = msg_id;
+
+    if (NULL != fragq)
+      fragment_queue_run (chn, msg_id, fragq, MSG_FRAG_STATE_DROP == 
fragq->state);
+    GNUNET_CONTAINER_heap_remove_root (chn->recv_msgs);
+    message_queue_run (chn);
+    break;
+
+  default:
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "%p GNUNET_PSYCSTORE_state_modify() failed with error %" 
PRId64 " (%.*s)\n",
+                chn, result, err_msg_size, err_msg);
+    /** @todo FIXME: handle state_modify error */
+  }
+}
+
+
+/**
+ * Run message queue.
+ *
+ * Send messages in queue to client in order after a message has arrived from
+ * multicast, according to the following:
+ * - A message is only sent if all of its modifiers arrived.
+ * - A stateful message is only sent if the previous stateful message
+ *   has already been delivered to the client.
+ *
+ * @param chn  Channel.
+ *
+ * @return Number of messages removed from queue and sent to client.
+ */
+static uint64_t
+message_queue_run (struct Channel *chn)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%p Running message queue.\n", chn);
+  uint64_t n = 0;
+  uint64_t msg_id;
+
+  while (GNUNET_YES == GNUNET_CONTAINER_heap_peek2 (chn->recv_msgs, NULL,
+                                                    &msg_id))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "%p Processing message %" PRIu64 " in queue.\n", chn, msg_id);
+    struct GNUNET_HashCode msg_id_hash;
+    hash_key_from_hll (&msg_id_hash, msg_id);
+
+    struct FragmentQueue *
+      fragq = GNUNET_CONTAINER_multihashmap_get (chn->recv_frags, 
&msg_id_hash);
+
+    if (NULL == fragq || fragq->state <= MSG_FRAG_STATE_HEADER)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                  "%p No fragq (%p) or header not complete.\n",
+                  chn, fragq);
+      break;
+    }
+
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "%p Fragment queue entry:  state: %u, state delta: "
+                "%" PRIu64 " - %" PRIu64 " ?= %" PRIu64 "\n",
+                chn, fragq->state, msg_id, fragq->state_delta, 
chn->max_state_message_id);
+
+    if (MSG_FRAG_STATE_DATA <= fragq->state)
+    {
+      /* Check if there's a missing message before the current one */
+      if (GNUNET_PSYC_STATE_NOT_MODIFIED == fragq->state_delta)
+      {
+        GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "%p state NOT modified\n", chn);
+
+        if (!(fragq->flags & GNUNET_PSYC_MESSAGE_ORDER_ANY)
+            && (chn->max_message_id != msg_id - 1
+                && chn->max_message_id != msg_id))
+        {
+          GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                      "%p Out of order message. "
+                      "(%" PRIu64 " != %" PRIu64 " - 1)\n",
+                      chn, chn->max_message_id, msg_id);
+          break;
+          // FIXME: keep track of messages processed in this queue run,
+          //        and only stop after reaching the end
+        }
+      }
+      else
+      {
+        GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "%p state modified\n", chn);
+        if (GNUNET_YES != fragq->state_is_modified)
+        {
+          if (msg_id - fragq->state_delta != chn->max_state_message_id)
+          {
+            GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                        "%p Out of order stateful message. "
+                        "(%" PRIu64 " - %" PRIu64 " != %" PRIu64 ")\n",
+                        chn, msg_id, fragq->state_delta, 
chn->max_state_message_id);
+            break;
+            // FIXME: keep track of messages processed in this queue run,
+            //        and only stop after reaching the end
+          }
+
+          struct StateModifyClosure *mcls = GNUNET_malloc (sizeof (*mcls));
+          mcls->channel = chn;
+          mcls->msg_id = msg_id;
+          mcls->msg_id_hash = msg_id_hash;
+
+          /* Apply modifiers to state in PSYCstore */
+          GNUNET_PSYCSTORE_state_modify (store, &chn->pub_key, msg_id,
+                                         fragq->state_delta,
+                                         store_recv_state_modify_result, mcls);
+          break; // continue after asynchronous state modify result
+        }
+      }
+      chn->max_message_id = msg_id;
+    }
+    fragment_queue_run (chn, msg_id, fragq, MSG_FRAG_STATE_DROP == 
fragq->state);
+    GNUNET_CONTAINER_heap_remove_root (chn->recv_msgs);
+    n++;
+  }
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%p Removed %" PRIu64 " messages from queue.\n", chn, n);
+  return n;
+}
+
+
+/**
+ * Drop message queue of a channel.
+ *
+ * Remove all messages in queue without sending it to clients.
+ *
+ * @param chn  Channel.
+ *
+ * @return Number of messages removed from queue.
+ */
+static uint64_t
+message_queue_drop (struct Channel *chn)
+{
+  uint64_t n = 0;
+  uint64_t msg_id;
+  while (GNUNET_YES == GNUNET_CONTAINER_heap_peek2 (chn->recv_msgs, NULL,
+                                                    &msg_id))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "%p Dropping message %" PRIu64 " from queue.\n", chn, msg_id);
+    struct GNUNET_HashCode msg_id_hash;
+    hash_key_from_hll (&msg_id_hash, msg_id);
+
+    struct FragmentQueue *
+      fragq = GNUNET_CONTAINER_multihashmap_get (chn->recv_frags, 
&msg_id_hash);
+    GNUNET_assert (NULL != fragq);
+    fragment_queue_run (chn, msg_id, fragq, GNUNET_YES);
+    GNUNET_CONTAINER_heap_remove_root (chn->recv_msgs);
+    n++;
+  }
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%p Removed %" PRIu64 " messages from queue.\n", chn, n);
+  return n;
+}
+
+
+/**
+ * Received result of GNUNET_PSYCSTORE_fragment_store().
+ */
+static void
+store_recv_fragment_store_result (void *cls, int64_t result,
+                                  const char *err_msg, uint16_t err_msg_size)
+{
+  struct Channel *chn = cls;
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%p GNUNET_PSYCSTORE_fragment_store() returned %" PRId64 " 
(%.*s)\n",
+              chn, result, err_msg_size, err_msg);
+}
+
+
+/**
+ * Handle incoming message fragment from multicast.
+ *
+ * Store it using PSYCstore and send it to the clients of the channel in order.
+ */
+static void
+mcast_recv_message (void *cls, const struct GNUNET_MULTICAST_MessageHeader 
*mmsg)
+{
+  struct Channel *chn = cls;
+  uint16_t size = ntohs (mmsg->header.size);
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%p Received multicast message of size %u. "
+              "fragment_id=%" PRIu64 ", message_id=%" PRIu64
+              ", fragment_offset=%" PRIu64 ", flags=%" PRIu64 "\n",
+              chn, size,
+              GNUNET_ntohll (mmsg->fragment_id),
+              GNUNET_ntohll (mmsg->message_id),
+              GNUNET_ntohll (mmsg->fragment_offset),
+              GNUNET_ntohll (mmsg->flags));
+
+  GNUNET_PSYCSTORE_fragment_store (store, &chn->pub_key, mmsg, 0,
+                                   &store_recv_fragment_store_result, chn);
+
+  uint16_t first_ptype = 0, last_ptype = 0;
+  int check = GNUNET_PSYC_receive_check_parts (size - sizeof (*mmsg),
+                                               (const char *) &mmsg[1],
+                                               &first_ptype, &last_ptype);
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%p Message check result %d, first part type %u, last part type 
%u\n",
+              chn, check, first_ptype, last_ptype);
+  if (GNUNET_SYSERR == check)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "%p Dropping incoming multicast message with invalid parts.\n",
+                chn);
+    GNUNET_break_op (0);
+    return;
+  }
+
+  fragment_queue_insert (chn, mmsg, first_ptype, last_ptype);
+  message_queue_run (chn);
+}
+
+
+/**
+ * Incoming request fragment from multicast for a master.
+ *
+ * @param cls  Master.
+ * @param req  The request.
+ */
+static void
+mcast_recv_request (void *cls,
+                    const struct GNUNET_MULTICAST_RequestHeader *req)
+{
+  struct Master *mst = cls;
+  uint16_t size = ntohs (req->header.size);
+
+  char *str = GNUNET_CRYPTO_ecdsa_public_key_to_string (&req->member_pub_key);
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%p Received multicast request of size %u from %s.\n",
+              mst, size, str);
+  GNUNET_free (str);
+
+  uint16_t first_ptype = 0, last_ptype = 0;
+  if (GNUNET_SYSERR
+      == GNUNET_PSYC_receive_check_parts (size - sizeof (*req),
+                                          (const char *) &req[1],
+                                          &first_ptype, &last_ptype))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "%p Dropping incoming multicast request with invalid parts.\n",
+                mst);
+    GNUNET_break_op (0);
+    return;
+  }
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Message parts: first: type %u, last: type %u\n",
+              first_ptype, last_ptype);
+
+  /* FIXME: in-order delivery */
+  client_send_mcast_req (mst, req);
+}
+
+
+/**
+ * Response from PSYCstore with the current counter values for a channel 
master.
+ */
+static void
+store_recv_master_counters (void *cls, int result, uint64_t max_fragment_id,
+                            uint64_t max_message_id, uint64_t 
max_group_generation,
+                            uint64_t max_state_message_id)
+{
+  struct Master *mst = cls;
+  struct Channel *chn = &mst->channel;
+  chn->store_op = NULL;
+
+  struct GNUNET_PSYC_CountersResultMessage res;
+  res.header.type = htons (GNUNET_MESSAGE_TYPE_PSYC_MASTER_START_ACK);
+  res.header.size = htons (sizeof (res));
+  res.result_code = htonl (result);
+  res.max_message_id = GNUNET_htonll (max_message_id);
+
+  if (GNUNET_OK == result || GNUNET_NO == result)
+  {
+    mst->max_message_id = max_message_id;
+    chn->max_message_id = max_message_id;
+    chn->max_state_message_id = max_state_message_id;
+    mst->max_group_generation = max_group_generation;
+    mst->origin
+      = GNUNET_MULTICAST_origin_start (cfg, &mst->priv_key, max_fragment_id,
+                                       mcast_recv_join_request,
+                                       mcast_recv_replay_fragment,
+                                       mcast_recv_replay_message,
+                                       mcast_recv_request,
+                                       mcast_recv_message, chn);
+    chn->is_ready = GNUNET_YES;
+  }
+  else
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "%p GNUNET_PSYCSTORE_counters_get() "
+                "returned %d for channel %s.\n",
+                chn, result, GNUNET_h2s (&chn->pub_key_hash));
+  }
+
+  client_send_msg (chn, &res.header);
+}
+
+
+/**
+ * Response from PSYCstore with the current counter values for a channel slave.
+ */
+void
+store_recv_slave_counters (void *cls, int result, uint64_t max_fragment_id,
+                           uint64_t max_message_id, uint64_t 
max_group_generation,
+                           uint64_t max_state_message_id)
+{
+  struct Slave *slv = cls;
+  struct Channel *chn = &slv->channel;
+  chn->store_op = NULL;
+
+  struct GNUNET_PSYC_CountersResultMessage res;
+  res.header.type = htons (GNUNET_MESSAGE_TYPE_PSYC_SLAVE_JOIN_ACK);
+  res.header.size = htons (sizeof (res));
+  res.result_code = htonl (result);
+  res.max_message_id = GNUNET_htonll (max_message_id);
+
+  if (GNUNET_YES == result || GNUNET_NO == result)
+  {
+    chn->max_message_id = max_message_id;
+    chn->max_state_message_id = max_state_message_id;
+    slv->member
+      = GNUNET_MULTICAST_member_join (cfg, &chn->pub_key, &slv->priv_key,
+                                      &slv->origin,
+                                      slv->relay_count, slv->relays,
+                                      &slv->join_msg->header,
+                                      mcast_recv_join_request,
+                                      mcast_recv_join_decision,
+                                      mcast_recv_replay_fragment,
+                                      mcast_recv_replay_message,
+                                      mcast_recv_message, chn);
+    if (NULL != slv->join_msg)
+    {
+      GNUNET_free (slv->join_msg);
+      slv->join_msg = NULL;
+    }
+  }
+  else
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "%p GNUNET_PSYCSTORE_counters_get() "
+                "returned %d for channel %s.\n",
+                chn, result, GNUNET_h2s (&chn->pub_key_hash));
+  }
+
+  client_send_msg (chn, &res.header);
+}
+
+
+static void
+channel_init (struct Channel *chn)
+{
+  chn->recv_msgs
+    = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN);
+  chn->recv_frags = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO);
+}
+
+
+/**
+ * Handle a connecting client starting a channel master.
+ */
+static void
+handle_client_master_start (void *cls,
+                            const struct MasterStartRequest *req)
+{
+  struct Client *c = cls;
+  struct GNUNET_SERVICE_Client *client = c->client;
+
+  struct GNUNET_CRYPTO_EddsaPublicKey pub_key;
+  struct GNUNET_HashCode pub_key_hash;
+
+  GNUNET_CRYPTO_eddsa_key_get_public (&req->channel_key, &pub_key);
+  GNUNET_CRYPTO_hash (&pub_key, sizeof (pub_key), &pub_key_hash);
+
+  struct Master *
+    mst = GNUNET_CONTAINER_multihashmap_get (masters, &pub_key_hash);
+  struct Channel *chn;
+
+  if (NULL == mst)
+  {
+    mst = GNUNET_malloc (sizeof (*mst));
+    mst->policy = ntohl (req->policy);
+    mst->priv_key = req->channel_key;
+    mst->join_reqs = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO);
+
+    chn = c->channel = &mst->channel;
+    chn->master = mst;
+    chn->is_master = GNUNET_YES;
+    chn->pub_key = pub_key;
+    chn->pub_key_hash = pub_key_hash;
+    channel_init (chn);
+
+    GNUNET_CONTAINER_multihashmap_put (masters, &chn->pub_key_hash, chn,
+                                       
GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
+    chn->store_op = GNUNET_PSYCSTORE_counters_get (store, &chn->pub_key,
+                                                   store_recv_master_counters, 
mst);
+  }
+  else
+  {
+    chn = &mst->channel;
+
+    struct GNUNET_PSYC_CountersResultMessage *res;
+    struct GNUNET_MQ_Envelope *
+      env = GNUNET_MQ_msg (res, GNUNET_MESSAGE_TYPE_PSYC_MASTER_START_ACK);
+    res->result_code = htonl (GNUNET_OK);
+    res->max_message_id = GNUNET_htonll (mst->max_message_id);
+
+    GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client), env);
+  }
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%p Client connected as master to channel %s.\n",
+              mst, GNUNET_h2s (&chn->pub_key_hash));
+
+  struct ClientList *cli = GNUNET_malloc (sizeof (*cli));
+  cli->client = client;
+  GNUNET_CONTAINER_DLL_insert (chn->clients_head, chn->clients_tail, cli);
+
+  GNUNET_SERVICE_client_continue (client);
+}
+
+
+static int
+check_client_slave_join (void *cls,
+                         const struct SlaveJoinRequest *req)
+{
+  return GNUNET_OK;
+}
+
+
+/**
+ * Handle a connecting client joining as a channel slave.
+ */
+static void
+handle_client_slave_join (void *cls,
+                          const struct SlaveJoinRequest *req)
+{
+  struct Client *c = cls;
+  struct GNUNET_SERVICE_Client *client = c->client;
+
+  uint16_t req_size = ntohs (req->header.size);
+
+  struct GNUNET_CRYPTO_EcdsaPublicKey slv_pub_key;
+  struct GNUNET_HashCode pub_key_hash, slv_pub_hash;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "got join request from client %p\n",
+              client);
+  GNUNET_CRYPTO_ecdsa_key_get_public (&req->slave_key, &slv_pub_key);
+  GNUNET_CRYPTO_hash (&slv_pub_key, sizeof (slv_pub_key), &slv_pub_hash);
+  GNUNET_CRYPTO_hash (&req->channel_pub_key, sizeof (req->channel_pub_key), 
&pub_key_hash);
+
+  struct GNUNET_CONTAINER_MultiHashMap *
+    chn_slv = GNUNET_CONTAINER_multihashmap_get (channel_slaves, 
&pub_key_hash);
+  struct Slave *slv = NULL;
+  struct Channel *chn;
+
+  if (NULL != chn_slv)
+  {
+    slv = GNUNET_CONTAINER_multihashmap_get (chn_slv, &slv_pub_hash);
+  }
+  if (NULL == slv)
+  {
+    slv = GNUNET_malloc (sizeof (*slv));
+    slv->priv_key = req->slave_key;
+    slv->pub_key = slv_pub_key;
+    slv->pub_key_hash = slv_pub_hash;
+    slv->origin = req->origin;
+    slv->relay_count = ntohl (req->relay_count);
+    slv->join_flags = ntohl (req->flags);
+
+    const struct GNUNET_PeerIdentity *
+      relays = (const struct GNUNET_PeerIdentity *) &req[1];
+    uint16_t relay_size = slv->relay_count * sizeof (*relays);
+    uint16_t join_msg_size = 0;
+
+    if (sizeof (*req) + relay_size + sizeof (struct GNUNET_MessageHeader)
+        <= req_size)
+    {
+      struct GNUNET_PSYC_Message *
+        join_msg = (struct GNUNET_PSYC_Message *) (((char *) &req[1]) + 
relay_size);
+      join_msg_size = ntohs (join_msg->header.size);
+      slv->join_msg = GNUNET_malloc (join_msg_size);
+      GNUNET_memcpy (slv->join_msg, join_msg, join_msg_size);
+    }
+    if (sizeof (*req) + relay_size + join_msg_size != req_size)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "%u + %u + %u != %u\n",
+                  (unsigned int) sizeof (*req),
+                  relay_size,
+                  join_msg_size,
+                  req_size);
+      GNUNET_break (0);
+      GNUNET_SERVICE_client_drop (client);
+      GNUNET_free (slv);
+      return;
+    }
+    if (0 < slv->relay_count)
+    {
+      slv->relays = GNUNET_malloc (relay_size);
+      GNUNET_memcpy (slv->relays, &req[1], relay_size);
+    }
+
+    chn = c->channel = &slv->channel;
+    chn->slave = slv;
+    chn->is_master = GNUNET_NO;
+    chn->pub_key = req->channel_pub_key;
+    chn->pub_key_hash = pub_key_hash;
+    channel_init (chn);
+
+    if (NULL == chn_slv)
+    {
+      chn_slv = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES);
+      GNUNET_CONTAINER_multihashmap_put (channel_slaves, &chn->pub_key_hash, 
chn_slv,
+                                         
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
+    }
+    GNUNET_CONTAINER_multihashmap_put (chn_slv, &slv->pub_key_hash, chn,
+                                       
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
+    GNUNET_CONTAINER_multihashmap_put (slaves, &chn->pub_key_hash, chn,
+                                       
GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
+    chn->store_op = GNUNET_PSYCSTORE_counters_get (store, &chn->pub_key,
+                                                   &store_recv_slave_counters, 
slv);
+  }
+  else
+  {
+    chn = &slv->channel;
+
+    struct GNUNET_PSYC_CountersResultMessage *res;
+
+    struct GNUNET_MQ_Envelope *
+      env = GNUNET_MQ_msg (res, GNUNET_MESSAGE_TYPE_PSYC_SLAVE_JOIN_ACK);
+    res->result_code = htonl (GNUNET_OK);
+    res->max_message_id = GNUNET_htonll (chn->max_message_id);
+
+    GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client), env);
+
+    if (GNUNET_PSYC_SLAVE_JOIN_LOCAL & slv->join_flags)
+    {
+      mcast_recv_join_decision (slv, GNUNET_YES,
+                                NULL, 0, NULL, NULL);
+    }
+    else if (NULL == slv->member)
+    {
+      slv->member
+        = GNUNET_MULTICAST_member_join (cfg, &chn->pub_key, &slv->priv_key,
+                                        &slv->origin,
+                                        slv->relay_count, slv->relays,
+                                        &slv->join_msg->header,
+                                        &mcast_recv_join_request,
+                                        &mcast_recv_join_decision,
+                                        &mcast_recv_replay_fragment,
+                                        &mcast_recv_replay_message,
+                                        &mcast_recv_message, chn);
+      if (NULL != slv->join_msg)
+      {
+        GNUNET_free (slv->join_msg);
+        slv->join_msg = NULL;
+      }
+    }
+    else if (NULL != slv->join_dcsn)
+    {
+      struct GNUNET_MQ_Envelope *
+        env = GNUNET_MQ_msg_copy (&slv->join_dcsn->header);
+      GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client), env);
+    }
+  }
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Client %p connected as slave to channel %s.\n",
+              client,
+              GNUNET_h2s (&chn->pub_key_hash));
+
+  struct ClientList *cli = GNUNET_malloc (sizeof (*cli));
+  cli->client = client;
+  GNUNET_CONTAINER_DLL_insert (chn->clients_head, chn->clients_tail, cli);
+
+  GNUNET_SERVICE_client_continue (client);
+}
+
+
+struct JoinDecisionClosure
+{
+  int32_t is_admitted;
+  struct GNUNET_MessageHeader *msg;
+};
+
+
+/**
+ * Iterator callback for sending join decisions to multicast.
+ */
+static int
+mcast_send_join_decision (void *cls, const struct GNUNET_HashCode 
*pub_key_hash,
+                          void *value)
+{
+  struct JoinDecisionClosure *jcls = cls;
+  struct GNUNET_MULTICAST_JoinHandle *jh = value;
+  // FIXME: add relays
+  GNUNET_MULTICAST_join_decision (jh, jcls->is_admitted, 0, NULL, jcls->msg);
+  return GNUNET_YES;
+}
+
+
+static int
+check_client_join_decision (void *cls,
+                            const struct GNUNET_PSYC_JoinDecisionMessage *dcsn)
+{
+  return GNUNET_OK;
+}
+
+
+/**
+ * Join decision from client.
+ */
+static void
+handle_client_join_decision (void *cls,
+                             const struct GNUNET_PSYC_JoinDecisionMessage 
*dcsn)
+{
+  struct Client *c = cls;
+  struct GNUNET_SERVICE_Client *client = c->client;
+  struct Channel *chn = c->channel;
+  if (NULL == chn)
+  {
+    GNUNET_break (0);
+    GNUNET_SERVICE_client_drop (client);
+    return;
+  }
+  GNUNET_assert (GNUNET_YES == chn->is_master);
+  struct Master *mst = chn->master;
+
+  struct JoinDecisionClosure jcls;
+  jcls.is_admitted = ntohl (dcsn->is_admitted);
+  jcls.msg
+    = (sizeof (*dcsn) + sizeof (*jcls.msg) <= ntohs (dcsn->header.size))
+    ? (struct GNUNET_MessageHeader *) &dcsn[1]
+    : NULL;
+
+  struct GNUNET_HashCode slave_pub_hash;
+  GNUNET_CRYPTO_hash (&dcsn->slave_pub_key, sizeof (dcsn->slave_pub_key),
+                      &slave_pub_hash);
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%p Got join decision (%d) from client for channel %s..\n",
+              mst, jcls.is_admitted, GNUNET_h2s (&chn->pub_key_hash));
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%p ..and slave %s.\n",
+              mst, GNUNET_h2s (&slave_pub_hash));
+
+  GNUNET_CONTAINER_multihashmap_get_multiple (mst->join_reqs, &slave_pub_hash,
+                                              &mcast_send_join_decision, 
&jcls);
+  GNUNET_CONTAINER_multihashmap_remove_all (mst->join_reqs, &slave_pub_hash);
+  GNUNET_SERVICE_client_continue (client);
+}
+
+
+static void
+channel_part_cb (void *cls)
+{
+  struct GNUNET_SERVICE_Client *client = cls;
+  struct GNUNET_MQ_Envelope *env;
+
+  env = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_PSYC_PART_ACK);
+  GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client),
+                  env);
+}
+
+
+static void
+handle_client_part_request (void *cls,
+                            const struct GNUNET_MessageHeader *msg)
+{
+  struct Client *c = cls;
+
+  c->channel->is_disconnecting = GNUNET_YES;
+  if (GNUNET_YES == c->channel->is_master)
+  {
+    struct Master *mst = (struct Master *) c->channel;
+   
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "Got part request from master %p\n",
+                mst);
+    GNUNET_assert (NULL != mst->origin);
+    GNUNET_MULTICAST_origin_stop (mst->origin, channel_part_cb, c->client);
+  }
+  else
+  {
+    struct Slave *slv = (struct Slave *) c->channel;
+
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "Got part request from slave %p\n",
+                slv);
+    GNUNET_assert (NULL != slv->member);
+    GNUNET_MULTICAST_member_part (slv->member, channel_part_cb, c->client);
+  }
+  GNUNET_SERVICE_client_continue (c->client);
+}
+
+
+/**
+ * Send acknowledgement to a client.
+ *
+ * Sent after a message fragment has been passed on to multicast.
+ *
+ * @param chn The channel struct for the client.
+ */
+static void
+send_message_ack (struct Channel *chn, struct GNUNET_SERVICE_Client *client)
+{
+  struct GNUNET_MessageHeader *res;
+  struct GNUNET_MQ_Envelope *
+      env = GNUNET_MQ_msg (res, GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_ACK);
+
+  /* FIXME? */
+  GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client), env);
+}
+
+
+/**
+ * Callback for the transmit functions of multicast.
+ */
+static int
+transmit_notify (void *cls, size_t *data_size, void *data)
+{
+  struct Channel *chn = cls;
+  struct TransmitMessage *tmit_msg = chn->tmit_head;
+
+  if (NULL == tmit_msg || *data_size < tmit_msg->size)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "%p transmit_notify: nothing to send.\n", chn);
+    if (NULL != tmit_msg && *data_size < tmit_msg->size)
+      GNUNET_break (0);
+    *data_size = 0;
+    return GNUNET_NO;
+  }
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%p transmit_notify: sending %u bytes.\n", chn, tmit_msg->size);
+
+  *data_size = tmit_msg->size;
+  GNUNET_memcpy (data, &tmit_msg[1], *data_size);
+
+  int ret
+    = (tmit_msg->last_ptype < GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END)
+    ? GNUNET_NO
+    : GNUNET_YES;
+
+  /* FIXME: handle disconnecting clients */
+  if (NULL != tmit_msg->client)
+    send_message_ack (chn, tmit_msg->client);
+
+  GNUNET_CONTAINER_DLL_remove (chn->tmit_head, chn->tmit_tail, tmit_msg);
+
+  if (NULL != chn->tmit_head)
+  {
+    GNUNET_SCHEDULER_add_now (&schedule_transmit_message, chn);
+  }
+  else if (GNUNET_YES == chn->is_disconnecting
+           && tmit_msg->last_ptype < GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END)
+  {
+    /* FIXME: handle partial message (when still in_transmit) */
+    GNUNET_free (tmit_msg);
+    return GNUNET_SYSERR;
+  }
+  GNUNET_free (tmit_msg);
+  return ret;
+}
+
+
+/**
+ * Callback for the transmit functions of multicast.
+ */
+static int
+master_transmit_notify (void *cls, size_t *data_size, void *data)
+{
+  int ret = transmit_notify (cls, data_size, data);
+
+  if (GNUNET_YES == ret)
+  {
+    struct Master *mst = cls;
+    mst->tmit_handle = NULL;
+  }
+  return ret;
+}
+
+
+/**
+ * Callback for the transmit functions of multicast.
+ */
+static int
+slave_transmit_notify (void *cls, size_t *data_size, void *data)
+{
+  int ret = transmit_notify (cls, data_size, data);
+
+  if (GNUNET_YES == ret)
+  {
+    struct Slave *slv = cls;
+    slv->tmit_handle = NULL;
+  }
+  return ret;
+}
+
+
+/**
+ * Transmit a message from a channel master to the multicast group.
+ */
+static void
+master_transmit_message (struct Master *mst)
+{
+  struct Channel *chn = &mst->channel;
+  struct TransmitMessage *tmit_msg = chn->tmit_head;
+  if (NULL == tmit_msg)
+    return;
+  if (NULL == mst->tmit_handle)
+  {
+    mst->tmit_handle = GNUNET_MULTICAST_origin_to_all (mst->origin,
+                                                       tmit_msg->id,
+                                                       
mst->max_group_generation,
+                                                       &master_transmit_notify,
+                                                       mst);
+  }
+  else
+  {
+    GNUNET_MULTICAST_origin_to_all_resume (mst->tmit_handle);
+  }
+}
+
+
+/**
+ * Transmit a message from a channel slave to the multicast group.
+ */
+static void
+slave_transmit_message (struct Slave *slv)
+{
+  if (NULL == slv->channel.tmit_head)
+    return;
+  if (NULL == slv->tmit_handle)
+  {
+    slv->tmit_handle = GNUNET_MULTICAST_member_to_origin (slv->member,
+                                                          
slv->channel.tmit_head->id,
+                                                          
&slave_transmit_notify,
+                                                          slv);
+  }
+  else
+  {
+    GNUNET_MULTICAST_member_to_origin_resume (slv->tmit_handle);
+  }
+}
+
+
+static void
+transmit_message (struct Channel *chn)
+{
+  chn->is_master
+    ? master_transmit_message (chn->master)
+    : slave_transmit_message (chn->slave);
+}
+
+
+/**
+ * Queue a message from a channel master for sending to the multicast group.
+ */
+static void
+master_queue_message (struct Master *mst, struct TransmitMessage *tmit_msg)
+{
+  if (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_METHOD == tmit_msg->first_ptype)
+  {
+    tmit_msg->id = ++mst->max_message_id;
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "%p master_queue_message: message_id=%" PRIu64 "\n",
+                mst, tmit_msg->id);
+    struct GNUNET_PSYC_MessageMethod *pmeth
+      = (struct GNUNET_PSYC_MessageMethod *) &tmit_msg[1];
+
+    if (pmeth->flags & GNUNET_PSYC_MASTER_TRANSMIT_STATE_RESET)
+    {
+      pmeth->state_delta = GNUNET_htonll (GNUNET_PSYC_STATE_RESET);
+    }
+    else if (pmeth->flags & GNUNET_PSYC_MASTER_TRANSMIT_STATE_MODIFY)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                  "%p master_queue_message: state_delta=%" PRIu64 "\n",
+                  mst, tmit_msg->id - mst->max_state_message_id);
+      pmeth->state_delta = GNUNET_htonll (tmit_msg->id
+                                          - mst->max_state_message_id);
+      mst->max_state_message_id = tmit_msg->id;
+    }
+    else
+    {
+        GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                    "%p master_queue_message: state not modified\n", mst);
+      pmeth->state_delta = GNUNET_htonll (GNUNET_PSYC_STATE_NOT_MODIFIED);
+    }
+
+    if (pmeth->flags & GNUNET_PSYC_MASTER_TRANSMIT_STATE_HASH)
+    {
+      /// @todo add state_hash to PSYC header
+    }
+  }
+}
+
+
+/**
+ * Queue a message from a channel slave for sending to the multicast group.
+ */
+static void
+slave_queue_message (struct Slave *slv, struct TransmitMessage *tmit_msg)
+{
+  if (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_METHOD == tmit_msg->first_ptype)
+  {
+    struct GNUNET_PSYC_MessageMethod *pmeth
+      = (struct GNUNET_PSYC_MessageMethod *) &tmit_msg[1];
+    pmeth->state_delta = GNUNET_htonll (GNUNET_PSYC_STATE_NOT_MODIFIED);
+    tmit_msg->id = ++slv->max_request_id;
+  }
+}
+
+
+/**
+ * Queue PSYC message parts for sending to multicast.
+ *
+ * @param chn
+ *        Channel to send to.
+ * @param client
+ *        Client the message originates from.
+ * @param data_size
+ *        Size of @a data.
+ * @param data
+ *        Concatenated message parts.
+ * @param first_ptype
+ *        First message part type in @a data.
+ * @param last_ptype
+ *        Last message part type in @a data.
+ */
+static struct TransmitMessage *
+queue_message (struct Channel *chn,
+               struct GNUNET_SERVICE_Client *client,
+               size_t data_size,
+               const void *data,
+               uint16_t first_ptype, uint16_t last_ptype)
+{
+  struct TransmitMessage *
+    tmit_msg = GNUNET_malloc (sizeof (*tmit_msg) + data_size);
+  GNUNET_memcpy (&tmit_msg[1], data, data_size);
+  tmit_msg->client = client;
+  tmit_msg->size = data_size;
+  tmit_msg->first_ptype = first_ptype;
+  tmit_msg->last_ptype = last_ptype;
+
+  /* FIXME: separate queue per message ID */
+
+  GNUNET_CONTAINER_DLL_insert_tail (chn->tmit_head, chn->tmit_tail, tmit_msg);
+
+  chn->is_master
+    ? master_queue_message (chn->master, tmit_msg)
+    : slave_queue_message (chn->slave, tmit_msg);
+  return tmit_msg;
+}
+
+
+/**
+ * Cancel transmission of current message.
+ *
+ * @param chn    Channel to send to.
+ * @param client  Client the message originates from.
+ */
+static void
+transmit_cancel (struct Channel *chn, struct GNUNET_SERVICE_Client *client)
+{
+  uint16_t type = GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL;
+
+  struct GNUNET_MessageHeader msg;
+  msg.size = htons (sizeof (msg));
+  msg.type = htons (type);
+
+  queue_message (chn, client, sizeof (msg), &msg, type, type);
+  transmit_message (chn);
+
+  /* FIXME: cleanup */
+}
+
+
+static int
+check_client_psyc_message (void *cls,
+                           const struct GNUNET_MessageHeader *msg)
+{
+  return GNUNET_OK;
+}
+
+
+/**
+ * Incoming message from a master or slave client.
+ */
+static void
+handle_client_psyc_message (void *cls,
+                            const struct GNUNET_MessageHeader *msg)
+{
+  struct Client *c = cls;
+  struct GNUNET_SERVICE_Client *client = c->client;
+  struct Channel *chn = c->channel;
+  if (NULL == chn)
+  {
+    GNUNET_break (0);
+    GNUNET_SERVICE_client_drop (client);
+    return;
+  }
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%p Received message from client.\n", chn);
+  GNUNET_PSYC_log_message (GNUNET_ERROR_TYPE_DEBUG, msg);
+
+  if (GNUNET_YES != chn->is_ready)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "%p Channel is not ready yet, disconnecting client %p.\n",
+                chn,
+                client);
+    GNUNET_break (0);
+    GNUNET_SERVICE_client_drop (client);
+    return;
+  }
+
+  uint16_t size = ntohs (msg->size);
+  if (GNUNET_MULTICAST_FRAGMENT_MAX_PAYLOAD < size - sizeof (*msg))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "%p Message payload too large: %u < %u.\n",
+                chn,
+                (unsigned int) GNUNET_MULTICAST_FRAGMENT_MAX_PAYLOAD,
+                (unsigned int) (size - sizeof (*msg)));
+    GNUNET_break (0);
+    transmit_cancel (chn, client);
+    GNUNET_SERVICE_client_drop (client);
+    return;
+  }
+
+  uint16_t first_ptype = 0, last_ptype = 0;
+  if (GNUNET_SYSERR
+      == GNUNET_PSYC_receive_check_parts (size - sizeof (*msg),
+                                          (const char *) &msg[1],
+                                          &first_ptype, &last_ptype))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "%p Received invalid message part from client.\n", chn);
+    GNUNET_break (0);
+    transmit_cancel (chn, client);
+    GNUNET_SERVICE_client_drop (client);
+    return;
+  }
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%p Received message with first part type %u and last part type 
%u.\n",
+              chn, first_ptype, last_ptype);
+
+  queue_message (chn, client, size - sizeof (*msg), &msg[1],
+                 first_ptype, last_ptype);
+  transmit_message (chn);
+  /* FIXME: send a few ACKs even before transmit_notify is called */
+
+  GNUNET_SERVICE_client_continue (client);
+};
+
+
+/**
+ * Received result of GNUNET_PSYCSTORE_membership_store()
+ */
+static void
+store_recv_membership_store_result (void *cls,
+                                    int64_t result,
+                                    const char *err_msg,
+                                    uint16_t err_msg_size)
+{
+  struct Operation *op = cls;
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%p GNUNET_PSYCSTORE_membership_store() returned %" PRId64 " 
(%.*s)\n",
+              op->channel,
+              result,
+              (int) err_msg_size,
+              err_msg);
+
+  if (NULL != op->client)
+    client_send_result (op->client, op->op_id, result, err_msg, err_msg_size);
+  op_remove (op);
+}
+
+
+/**
+ * Client requests to add/remove a slave in the membership database.
+ */
+static void
+handle_client_membership_store (void *cls,
+                                const struct ChannelMembershipStoreRequest 
*req)
+{
+  struct Client *c = cls;
+  struct GNUNET_SERVICE_Client *client = c->client;
+  struct Channel *chn = c->channel;
+  if (NULL == chn)
+  {
+    GNUNET_break (0);
+    GNUNET_SERVICE_client_drop (client);
+    return;
+  }
+
+  struct Operation *op = op_add (chn, client, req->op_id, 0);
+
+  uint64_t announced_at = GNUNET_ntohll (req->announced_at);
+  uint64_t effective_since = GNUNET_ntohll (req->effective_since);
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%p Received membership store request from client.\n", chn);
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%p did_join: %u, announced_at: %" PRIu64 ", effective_since: %" 
PRIu64 "\n",
+              chn, req->did_join, announced_at, effective_since);
+
+  GNUNET_PSYCSTORE_membership_store (store, &chn->pub_key, &req->slave_pub_key,
+                                     req->did_join, announced_at, 
effective_since,
+                                     0, /* FIXME: group_generation */
+                                     &store_recv_membership_store_result, op);
+  GNUNET_SERVICE_client_continue (client);
+}
+
+
+/**
+ * Received a fragment for GNUNET_PSYCSTORE_fragment_get(),
+ * in response to a history request from a client.
+ */
+static int
+store_recv_fragment_history (void *cls,
+                             struct GNUNET_MULTICAST_MessageHeader *mmsg,
+                             enum GNUNET_PSYCSTORE_MessageFlags flags)
+{
+  struct Operation *op = cls;
+  if (NULL == op->client)
+  { /* Requesting client already disconnected. */
+    return GNUNET_NO;
+  }
+  struct Channel *chn = op->channel;
+
+  struct GNUNET_PSYC_MessageHeader *pmsg;
+  uint16_t msize = ntohs (mmsg->header.size);
+  uint16_t psize = sizeof (*pmsg) + msize - sizeof (*mmsg);
+
+  struct GNUNET_OperationResultMessage *
+    res = GNUNET_malloc (sizeof (*res) + psize);
+  res->header.size = htons (sizeof (*res) + psize);
+  res->header.type = htons (GNUNET_MESSAGE_TYPE_PSYC_HISTORY_RESULT);
+  res->op_id = op->op_id;
+  res->result_code = GNUNET_htonll (GNUNET_OK);
+
+  pmsg = (struct GNUNET_PSYC_MessageHeader *) &res[1];
+  GNUNET_PSYC_message_header_init (pmsg, mmsg, flags | 
GNUNET_PSYC_MESSAGE_HISTORIC);
+  GNUNET_memcpy (&res[1], pmsg, psize);
+
+  /** @todo FIXME: send only to requesting client */
+  client_send_msg (chn, &res->header);
+
+  GNUNET_free (res);
+  return GNUNET_YES;
+}
+
+
+/**
+ * Received the result of GNUNET_PSYCSTORE_fragment_get(),
+ * in response to a history request from a client.
+ */
+static void
+store_recv_fragment_history_result (void *cls, int64_t result,
+                                    const char *err_msg, uint16_t err_msg_size)
+{
+  struct Operation *op = cls;
+  if (NULL == op->client)
+  { /* Requesting client already disconnected. */
+    return;
+  }
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%p History replay #%" PRIu64 ": "
+              "PSYCSTORE returned %" PRId64 " (%.*s)\n",
+              op->channel, GNUNET_ntohll (op->op_id), result, err_msg_size, 
err_msg);
+
+  if (op->flags & GNUNET_PSYC_HISTORY_REPLAY_REMOTE)
+  {
+    /** @todo Multicast replay request for messages not found locally. */
+  }
+
+  client_send_result (op->client, op->op_id, result, err_msg, err_msg_size);
+  op_remove (op);
+}
+
+
+static int
+check_client_history_replay (void *cls,
+                             const struct GNUNET_PSYC_HistoryRequestMessage 
*req)
+{
+  return GNUNET_OK;
+}
+
+
+/**
+ * Client requests channel history.
+ */
+static void
+handle_client_history_replay (void *cls,
+                              const struct GNUNET_PSYC_HistoryRequestMessage 
*req)
+{
+  struct Client *c = cls;
+  struct GNUNET_SERVICE_Client *client = c->client;
+  struct Channel *chn = c->channel;
+  if (NULL == chn)
+  {
+    GNUNET_break (0);
+    GNUNET_SERVICE_client_drop (client);
+    return;
+  }
+
+  uint16_t size = ntohs (req->header.size);
+  const char *method_prefix = (const char *) &req[1];
+
+  if (size < sizeof (*req) + 1
+      || '\0' != method_prefix[size - sizeof (*req) - 1])
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "%p History replay #%" PRIu64 ": "
+                "invalid method prefix. size: %u < %u?\n",
+                chn,
+                GNUNET_ntohll (req->op_id),
+                size,
+                (unsigned int) sizeof (*req) + 1);
+    GNUNET_break (0);
+    GNUNET_SERVICE_client_drop (client);
+    return;
+  }
+
+  struct Operation *op = op_add (chn, client, req->op_id, ntohl (req->flags));
+
+  if (0 == req->message_limit)
+  {
+    GNUNET_PSYCSTORE_message_get (store, &chn->pub_key, NULL,
+                                  GNUNET_ntohll (req->start_message_id),
+                                  GNUNET_ntohll (req->end_message_id),
+                                  0, method_prefix,
+                                  &store_recv_fragment_history,
+                                  &store_recv_fragment_history_result, op);
+  }
+  else
+  {
+    GNUNET_PSYCSTORE_message_get_latest (store, &chn->pub_key, NULL,
+                                         GNUNET_ntohll (req->message_limit),
+                                         method_prefix,
+                                         &store_recv_fragment_history,
+                                         &store_recv_fragment_history_result,
+                                         op);
+  }
+  GNUNET_SERVICE_client_continue (client);
+}
+
+
+/**
+ * Received state var from PSYCstore, send it to client.
+ */
+static int
+store_recv_state_var (void *cls, const char *name,
+                      const void *value, uint32_t value_size)
+{
+  struct Operation *op = cls;
+  struct GNUNET_OperationResultMessage *res;
+  struct GNUNET_MQ_Envelope *env;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%p state_get #%" PRIu64 " - received var from PSYCstore: %s\n",
+              op->channel, GNUNET_ntohll (op->op_id), name);
+
+  if (NULL != name) /* First part */
+  {
+    uint16_t name_size = strnlen (name, GNUNET_PSYC_MODIFIER_MAX_PAYLOAD) + 1;
+    struct GNUNET_PSYC_MessageModifier *mod;
+    env = GNUNET_MQ_msg_extra (res,
+                               sizeof (*mod) + name_size + value_size,
+                               GNUNET_MESSAGE_TYPE_PSYC_STATE_RESULT);
+    res->op_id = op->op_id;
+
+    mod = (struct GNUNET_PSYC_MessageModifier *) &res[1];
+    mod->header.size = htons (sizeof (*mod) + name_size + value_size);
+    mod->header.type = htons (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MODIFIER);
+    mod->name_size = htons (name_size);
+    mod->value_size = htonl (value_size);
+    mod->oper = htons (GNUNET_PSYC_OP_ASSIGN);
+    GNUNET_memcpy (&mod[1], name, name_size);
+    GNUNET_memcpy (((char *) &mod[1]) + name_size, value, value_size);
+  }
+  else /* Continuation */
+  {
+    struct GNUNET_MessageHeader *mod;
+    env = GNUNET_MQ_msg_extra (res,
+                               sizeof (*mod) + value_size,
+                               GNUNET_MESSAGE_TYPE_PSYC_STATE_RESULT);
+    res->op_id = op->op_id;
+
+    mod = (struct GNUNET_MessageHeader *) &res[1];
+    mod->size = htons (sizeof (*mod) + value_size);
+    mod->type = htons (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MOD_CONT);
+    GNUNET_memcpy (&mod[1], value, value_size);
+  }
+
+  // FIXME: client might have been disconnected
+  GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (op->client), env);
+  return GNUNET_YES;
+}
+
+
+/**
+ * Received result of GNUNET_PSYCSTORE_state_get()
+ * or GNUNET_PSYCSTORE_state_get_prefix()
+ */
+static void
+store_recv_state_result (void *cls, int64_t result,
+                         const char *err_msg, uint16_t err_msg_size)
+{
+  struct Operation *op = cls;
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%p state_get #%" PRIu64 ": "
+              "PSYCSTORE returned %" PRId64 " (%.*s)\n",
+              op->channel, GNUNET_ntohll (op->op_id), result, err_msg_size, 
err_msg);
+
+  // FIXME: client might have been disconnected
+  client_send_result (op->client, op->op_id, result, err_msg, err_msg_size);
+  op_remove (op);
+}
+
+
+static int
+check_client_state_get (void *cls,
+                         const struct StateRequest *req)
+{
+  struct Client *c = cls;
+  struct Channel *chn = c->channel;
+  if (NULL == chn)
+  {
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+
+  uint16_t name_size = ntohs (req->header.size) - sizeof (*req);
+  const char *name = (const char *) &req[1];
+  if (0 == name_size || '\0' != name[name_size - 1])
+  {
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+
+  return GNUNET_OK;
+}
+
+
+/**
+ * Client requests best matching state variable from PSYCstore.
+ */
+static void
+handle_client_state_get (void *cls,
+                         const struct StateRequest *req)
+{
+  struct Client *c = cls;
+  struct GNUNET_SERVICE_Client *client = c->client;
+  struct Channel *chn = c->channel;
+
+  const char *name = (const char *) &req[1];
+  struct Operation *op = op_add (chn, client, req->op_id, 0);
+  GNUNET_PSYCSTORE_state_get (store, &chn->pub_key, name,
+                              &store_recv_state_var,
+                              &store_recv_state_result, op);
+  GNUNET_SERVICE_client_continue (client);
+}
+
+
+static int
+check_client_state_get_prefix (void *cls,
+                               const struct StateRequest *req)
+{
+  struct Client *c = cls;
+  struct Channel *chn = c->channel;
+  if (NULL == chn)
+  {
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+
+  uint16_t name_size = ntohs (req->header.size) - sizeof (*req);
+  const char *name = (const char *) &req[1];
+  if (0 == name_size || '\0' != name[name_size - 1])
+  {
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+
+  return GNUNET_OK;
+}
+
+
+/**
+ * Client requests state variables with a given prefix from PSYCstore.
+ */
+static void
+handle_client_state_get_prefix (void *cls,
+                                const struct StateRequest *req)
+{
+  struct Client *c = cls;
+  struct GNUNET_SERVICE_Client *client = c->client;
+  struct Channel *chn = c->channel;
+
+  const char *name = (const char *) &req[1];
+  struct Operation *op = op_add (chn, client, req->op_id, 0);
+  GNUNET_PSYCSTORE_state_get_prefix (store, &chn->pub_key, name,
+                                     &store_recv_state_var,
+                                     &store_recv_state_result, op);
+  GNUNET_SERVICE_client_continue (client);
+}
+
+
+/**
+ * Initialize the PSYC service.
+ *
+ * @param cls Closure.
+ * @param server The initialized server.
+ * @param c Configuration to use.
+ */
+static void
+run (void *cls,
+     const struct GNUNET_CONFIGURATION_Handle *c,
+     struct GNUNET_SERVICE_Handle *svc)
+{
+  cfg = c;
+  service = svc;
+  store = GNUNET_PSYCSTORE_connect (cfg);
+  stats = GNUNET_STATISTICS_create ("psyc", cfg);
+  masters = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES);
+  slaves = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES);
+  channel_slaves = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO);
+  recv_cache = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO);
+  GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL);
+}
+
+
+/**
+ * Define "main" method using service macro.
+ */
+GNUNET_SERVICE_MAIN
+("psyc",
+ GNUNET_SERVICE_OPTION_NONE,
+ &run,
+ &client_notify_connect,
+ &client_notify_disconnect,
+ NULL,
+ GNUNET_MQ_hd_fixed_size (client_master_start,
+                          GNUNET_MESSAGE_TYPE_PSYC_MASTER_START,
+                          struct MasterStartRequest,
+                          NULL),
+ GNUNET_MQ_hd_var_size (client_slave_join,
+                        GNUNET_MESSAGE_TYPE_PSYC_SLAVE_JOIN,
+                        struct SlaveJoinRequest,
+                        NULL),
+ GNUNET_MQ_hd_var_size (client_join_decision,
+                        GNUNET_MESSAGE_TYPE_PSYC_JOIN_DECISION,
+                        struct GNUNET_PSYC_JoinDecisionMessage,
+                        NULL),
+ GNUNET_MQ_hd_fixed_size (client_part_request,
+                          GNUNET_MESSAGE_TYPE_PSYC_PART_REQUEST,
+                          struct GNUNET_MessageHeader,
+                          NULL),
+ GNUNET_MQ_hd_var_size (client_psyc_message,
+                        GNUNET_MESSAGE_TYPE_PSYC_MESSAGE,
+                        struct GNUNET_MessageHeader,
+                        NULL),
+ GNUNET_MQ_hd_fixed_size (client_membership_store,
+                          GNUNET_MESSAGE_TYPE_PSYC_CHANNEL_MEMBERSHIP_STORE,
+                          struct ChannelMembershipStoreRequest,
+                          NULL),
+ GNUNET_MQ_hd_var_size (client_history_replay,
+                        GNUNET_MESSAGE_TYPE_PSYC_HISTORY_REPLAY,
+                        struct GNUNET_PSYC_HistoryRequestMessage,
+                        NULL),
+ GNUNET_MQ_hd_var_size (client_state_get,
+                        GNUNET_MESSAGE_TYPE_PSYC_STATE_GET,
+                        struct StateRequest,
+                        NULL),
+ GNUNET_MQ_hd_var_size (client_state_get_prefix,
+                        GNUNET_MESSAGE_TYPE_PSYC_STATE_GET_PREFIX,
+                        struct StateRequest,
+                        NULL));
+
+/* end of gnunet-service-psyc.c */
diff --git a/src/psyc/psyc.conf.in b/src/psyc/psyc.conf.in
new file mode 100644
index 0000000..764ccfa
--- /dev/null
+++ b/src/psyc/psyc.conf.in
@@ -0,0 +1,12 @@
+[psyc]
+START_ON_DEMAND = @START_ON_DEMAND@
+BINARY = gnunet-service-psyc
+
+UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-psyc.sock
+UNIX_MATCH_UID = YES
+UNIX_MATCH_GID = YES
+
address@hidden@PORT = 2115
+HOSTNAME = localhost
+ACCEPT_FROM = 127.0.0.1;
+ACCEPT_FROM6 = ::1;
diff --git a/src/psyc/psyc.h b/src/psyc/psyc.h
new file mode 100644
index 0000000..74bbf3e
--- /dev/null
+++ b/src/psyc/psyc.h
@@ -0,0 +1,178 @@
+/*
+ * This file is part of GNUnet
+ * Copyright (C) 2013 GNUnet e.V.
+ *
+ * GNUnet is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * GNUnet 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
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+     SPDX-License-Identifier: AGPL3.0-or-later
+ */
+
+/**
+ * @file psyc/psyc.h
+ * @brief Common type definitions for the PSYC service and API.
+ * @author Gabor X Toth
+ */
+
+#ifndef PSYC_H
+#define PSYC_H
+
+#include "platform.h"
+#include "gnunet_psyc_service.h"
+
+
+int
+GNUNET_PSYC_check_message_parts (uint16_t data_size, const char *data,
+                                 uint16_t *first_ptype, uint16_t *last_ptype);
+
+void
+GNUNET_PSYC_log_message (enum GNUNET_ErrorType kind,
+                         const struct GNUNET_MessageHeader *msg);
+
+
+enum MessageState
+{
+  MSG_STATE_START    = 0,
+  MSG_STATE_HEADER   = 1,
+  MSG_STATE_METHOD   = 2,
+  MSG_STATE_MODIFIER = 3,
+  MSG_STATE_MOD_CONT = 4,
+  MSG_STATE_DATA     = 5,
+  MSG_STATE_END      = 6,
+  MSG_STATE_CANCEL   = 7,
+  MSG_STATE_ERROR    = 8,
+};
+
+
+enum MessageFragmentState
+{
+  MSG_FRAG_STATE_START    = 0,
+  MSG_FRAG_STATE_HEADER   = 1,
+  MSG_FRAG_STATE_DATA     = 2,
+  MSG_FRAG_STATE_END      = 3,
+  MSG_FRAG_STATE_CANCEL   = 4,
+  MSG_FRAG_STATE_DROP     = 5,
+};
+
+
+GNUNET_NETWORK_STRUCT_BEGIN
+
+
+/**** library -> service ****/
+
+
+struct MasterStartRequest
+{
+  /**
+   * Type: GNUNET_MESSAGE_TYPE_PSYC_MASTER_START
+   */
+  struct GNUNET_MessageHeader header;
+
+  uint32_t policy GNUNET_PACKED;
+
+  struct GNUNET_CRYPTO_EddsaPrivateKey channel_key;
+};
+
+
+struct SlaveJoinRequest
+{
+  /**
+   * Type: GNUNET_MESSAGE_TYPE_PSYC_SLAVE_JOIN
+   */
+  struct GNUNET_MessageHeader header;
+
+  uint32_t relay_count GNUNET_PACKED;
+
+  struct GNUNET_CRYPTO_EddsaPublicKey channel_pub_key;
+
+  struct GNUNET_CRYPTO_EcdsaPrivateKey slave_key;
+
+  struct GNUNET_PeerIdentity origin;
+
+  uint32_t flags GNUNET_PACKED;
+
+  /* Followed by struct GNUNET_PeerIdentity relays[relay_count] */
+
+  /* Followed by struct GNUNET_MessageHeader join_msg */
+};
+
+
+struct ChannelMembershipStoreRequest
+{
+  /**
+   * Type: GNUNET_MESSAGE_TYPE_PSYC_CHANNEL_MEMBERSHIP_STORE
+   */
+  struct GNUNET_MessageHeader header;
+
+  uint32_t reserved GNUNET_PACKED;
+
+  uint64_t op_id GNUNET_PACKED;
+
+  struct GNUNET_CRYPTO_EcdsaPublicKey slave_pub_key;
+
+  uint64_t announced_at GNUNET_PACKED;
+
+  uint64_t effective_since GNUNET_PACKED;
+
+  uint8_t did_join;
+};
+
+
+struct HistoryRequest
+{
+  /**
+   * Type: GNUNET_MESSAGE_TYPE_PSYC_CHANNEL_HISTORY_REQUEST
+   */
+  struct GNUNET_MessageHeader header;
+
+  uint32_t reserved GNUNET_PACKED;
+
+  /**
+   * ID for this operation.
+   */
+  uint64_t op_id GNUNET_PACKED;
+
+  uint64_t start_message_id GNUNET_PACKED;
+
+  uint64_t end_message_id GNUNET_PACKED;
+
+  uint64_t message_limit GNUNET_PACKED;
+};
+
+
+struct StateRequest
+{
+  /**
+   * Types:
+   * - GNUNET_MESSAGE_TYPE_PSYC_CHANNEL_STATE_GET
+   * - GNUNET_MESSAGE_TYPE_PSYC_CHANNEL_STATE_GET_PREFIX
+   */
+  struct GNUNET_MessageHeader header;
+
+  uint32_t reserved GNUNET_PACKED;
+
+  /**
+   * ID for this operation.
+   */
+  uint64_t op_id GNUNET_PACKED;
+
+  /* Followed by NUL-terminated name. */
+};
+
+
+/**** service -> library ****/
+
+
+GNUNET_NETWORK_STRUCT_END
+
+#endif
diff --git a/src/psyc/psyc_api.c b/src/psyc/psyc_api.c
new file mode 100644
index 0000000..37ea112
--- /dev/null
+++ b/src/psyc/psyc_api.c
@@ -0,0 +1,1584 @@
+/*
+ * This file is part of GNUnet
+ * Copyright (C) 2013 GNUnet e.V.
+ *
+ * GNUnet is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * GNUnet 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
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+     SPDX-License-Identifier: AGPL3.0-or-later
+ */
+
+/**
+ * @file psyc/psyc_api.c
+ * @brief PSYC service; high-level access to the PSYC protocol
+ *        note that clients of this API are NOT expected to
+ *        understand the PSYC message format, only the semantics!
+ *        Parsing (and serializing) the PSYC stream format is done
+ *        within the implementation of the libgnunetpsyc library,
+ *        and this API deliberately exposes as little as possible
+ *        of the actual data stream format to the application!
+ * @author Gabor X Toth
+ */
+
+#include <inttypes.h>
+
+#include "platform.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_multicast_service.h"
+#include "gnunet_psyc_service.h"
+#include "gnunet_psyc_util_lib.h"
+#include "psyc.h"
+
+#define LOG(kind,...) GNUNET_log_from (kind, "psyc-api",__VA_ARGS__)
+
+
+/**
+ * Handle to access PSYC channel operations for both the master and slaves.
+ */
+struct GNUNET_PSYC_Channel
+{
+  /**
+   * Configuration to use.
+   */
+  const struct GNUNET_CONFIGURATION_Handle *cfg;
+
+  /**
+   * Client connection to the service.
+   */
+  struct GNUNET_MQ_Handle *mq;
+
+  /**
+   * Message to send on connect.
+   */
+  struct GNUNET_MQ_Envelope *connect_env;
+
+  /**
+   * Time to wait until we try to reconnect on failure.
+   */
+  struct GNUNET_TIME_Relative reconnect_delay;
+
+  /**
+   * Task for reconnecting when the listener fails.
+   */
+  struct GNUNET_SCHEDULER_Task *reconnect_task;
+
+  /**
+   * Async operations.
+   */
+  struct GNUNET_OP_Handle *op;
+
+  /**
+   * Transmission handle;
+   */
+  struct GNUNET_PSYC_TransmitHandle *tmit;
+
+  /**
+   * Receipt handle;
+   */
+  struct GNUNET_PSYC_ReceiveHandle *recv;
+
+  /**
+   * Function called after disconnected from the service.
+   */
+  GNUNET_ContinuationCallback disconnect_cb;
+
+  /**
+   * Closure for @a disconnect_cb.
+   */
+  void *disconnect_cls;
+
+  /**
+   * Are we polling for incoming messages right now?
+   */
+  uint8_t in_receive;
+
+  /**
+   * Is this a master or slave channel?
+   */
+  uint8_t is_master;
+
+  /**
+   * Is this channel in the process of disconnecting from the service?
+   * #GNUNET_YES or #GNUNET_NO
+   */
+  uint8_t is_disconnecting;
+};
+
+
+/**
+ * Handle for the master of a PSYC channel.
+ */
+struct GNUNET_PSYC_Master
+{
+  struct GNUNET_PSYC_Channel chn;
+
+  GNUNET_PSYC_MasterStartCallback start_cb;
+
+  /**
+   * Join request callback.
+   */
+  GNUNET_PSYC_JoinRequestCallback join_req_cb;
+
+  /**
+   * Closure for the callbacks.
+   */
+  void *cb_cls;
+};
+
+
+/**
+ * Handle for a PSYC channel slave.
+ */
+struct GNUNET_PSYC_Slave
+{
+  struct GNUNET_PSYC_Channel chn;
+
+  GNUNET_PSYC_SlaveConnectCallback connect_cb;
+
+  GNUNET_PSYC_JoinDecisionCallback join_dcsn_cb;
+
+  /**
+   * Closure for the callbacks.
+   */
+  void *cb_cls;
+};
+
+
+/**
+ * Handle that identifies a join request.
+ *
+ * Used to match calls to #GNUNET_PSYC_JoinRequestCallback to the
+ * corresponding calls to GNUNET_PSYC_join_decision().
+ */
+struct GNUNET_PSYC_JoinHandle
+{
+  struct GNUNET_PSYC_Master *mst;
+  struct GNUNET_CRYPTO_EcdsaPublicKey slave_pub_key;
+};
+
+
+/**
+ * Handle for a pending PSYC transmission operation.
+ */
+struct GNUNET_PSYC_SlaveTransmitHandle
+{
+
+};
+
+
+struct GNUNET_PSYC_HistoryRequest
+{
+  /**
+   * Channel.
+   */
+  struct GNUNET_PSYC_Channel *chn;
+
+  /**
+   * Operation ID.
+   */
+  uint64_t op_id;
+
+  /**
+   * Message handler.
+   */
+  struct GNUNET_PSYC_ReceiveHandle *recv;
+
+  /**
+   * Function to call when the operation finished.
+   */
+  GNUNET_ResultCallback result_cb;
+
+  /**
+   * Closure for @a result_cb.
+   */
+  void *cls;
+};
+
+
+struct GNUNET_PSYC_StateRequest
+{
+  /**
+   * Channel.
+   */
+  struct GNUNET_PSYC_Channel *chn;
+
+  /**
+   * Operation ID.
+   */
+  uint64_t op_id;
+
+  /**
+   * State variable result callback.
+   */
+  GNUNET_PSYC_StateVarCallback var_cb;
+
+  /**
+   * Function to call when the operation finished.
+   */
+  GNUNET_ResultCallback result_cb;
+
+  /**
+   * Closure for @a result_cb.
+   */
+  void *cls;
+};
+
+
+static int
+check_channel_result (void *cls,
+                      const struct GNUNET_OperationResultMessage *res)
+{
+  return GNUNET_OK;
+}
+
+
+static void
+handle_channel_result (void *cls,
+                       const struct GNUNET_OperationResultMessage *res)
+{
+  struct GNUNET_PSYC_Channel *chn = cls;
+
+  uint16_t size = ntohs (res->header.size);
+  if (size < sizeof (*res))
+  { /* Error, message too small. */
+    GNUNET_break (0);
+    return;
+  }
+
+  uint16_t data_size = size - sizeof (*res);
+  const char *data = (0 < data_size) ? (void *) &res[1] : NULL;
+  GNUNET_OP_result (chn->op, GNUNET_ntohll (res->op_id),
+                    GNUNET_ntohll (res->result_code),
+                    data, data_size, NULL);
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "handle_channel_result: Received result message with OP ID %" 
PRIu64 "\n",
+              GNUNET_ntohll (res->op_id));
+}
+
+
+static void
+op_recv_history_result (void *cls, int64_t result,
+                        const void *data, uint16_t data_size)
+{
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Received history replay result: %" PRId64 ".\n", result);
+
+  struct GNUNET_PSYC_HistoryRequest *hist = cls;
+
+  if (NULL != hist->result_cb)
+    hist->result_cb (hist->cls, result, data, data_size);
+
+  GNUNET_PSYC_receive_destroy (hist->recv);
+  GNUNET_free (hist);
+}
+
+
+static void
+op_recv_state_result (void *cls, int64_t result,
+                      const void *data, uint16_t data_size)
+{
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Received state request result: %" PRId64 ".\n", result);
+
+  struct GNUNET_PSYC_StateRequest *sr = cls;
+
+  if (NULL != sr->result_cb)
+    sr->result_cb (sr->cls, result, data, data_size);
+
+  GNUNET_free (sr);
+}
+
+
+static int
+check_channel_history_result (void *cls,
+                              const struct GNUNET_OperationResultMessage *res)
+{
+  struct GNUNET_PSYC_MessageHeader *
+    pmsg = (struct GNUNET_PSYC_MessageHeader *) GNUNET_MQ_extract_nested_mh 
(res);
+  uint16_t size = ntohs (res->header.size);
+
+  if ( (NULL == pmsg) ||
+       (size < sizeof (*res) + sizeof (*pmsg)) )
+  { /* Error, message too small. */
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  return GNUNET_OK;
+}
+
+
+static void
+handle_channel_history_result (void *cls,
+                               const struct GNUNET_OperationResultMessage *res)
+{
+  struct GNUNET_PSYC_Channel *chn = cls;
+  struct GNUNET_PSYC_MessageHeader *
+    pmsg = (struct GNUNET_PSYC_MessageHeader *) GNUNET_MQ_extract_nested_mh 
(res);
+  GNUNET_ResultCallback result_cb = NULL;
+  struct GNUNET_PSYC_HistoryRequest *hist = NULL;
+
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "%p Received historic fragment for message #%" PRIu64 ".\n",
+       chn,
+       GNUNET_ntohll (pmsg->message_id));
+
+  if (GNUNET_YES != GNUNET_OP_get (chn->op,
+                                   GNUNET_ntohll (res->op_id),
+                                   &result_cb, (void *) &hist, NULL))
+  { /* Operation not found. */
+    LOG (GNUNET_ERROR_TYPE_WARNING,
+         "%p Replay operation not found for historic fragment of message #%"
+         PRIu64 ".\n",
+         chn, GNUNET_ntohll (pmsg->message_id));
+    return;
+  }
+
+  GNUNET_PSYC_receive_message (hist->recv,
+                               (const struct GNUNET_PSYC_MessageHeader *) 
pmsg);
+}
+
+
+static int
+check_channel_state_result (void *cls,
+                            const struct GNUNET_OperationResultMessage *res)
+{
+  const struct GNUNET_MessageHeader *mod = GNUNET_MQ_extract_nested_mh (res);
+  uint16_t mod_size;
+  uint16_t size;
+
+  if (NULL == mod)
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  mod_size = ntohs (mod->size);
+  size = ntohs (res->header.size);
+  if (size - sizeof (*res) != mod_size)
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  return GNUNET_OK;
+}
+
+
+static void
+handle_channel_state_result (void *cls,
+                             const struct GNUNET_OperationResultMessage *res)
+{
+  struct GNUNET_PSYC_Channel *chn = cls;
+
+  GNUNET_ResultCallback result_cb = NULL;
+  struct GNUNET_PSYC_StateRequest *sr = NULL;
+
+  if (GNUNET_YES != GNUNET_OP_get (chn->op,
+                                   GNUNET_ntohll (res->op_id),
+                                   &result_cb, (void *) &sr, NULL))
+  { /* Operation not found. */
+    return;
+  }
+
+  const struct GNUNET_MessageHeader *mod = GNUNET_MQ_extract_nested_mh (res);
+  if (NULL == mod)
+  {
+    GNUNET_break_op (0);
+    return;
+  }
+  uint16_t mod_size = ntohs (mod->size);
+
+  switch (ntohs (mod->type))
+  {
+  case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MODIFIER:
+  {
+    const struct GNUNET_PSYC_MessageModifier *
+      pmod = (const struct GNUNET_PSYC_MessageModifier *) mod;
+
+    const char *name = (const char *) &pmod[1];
+    uint16_t name_size = ntohs (pmod->name_size);
+    if (0 == name_size
+        || mod_size - sizeof (*pmod) < name_size
+        || '\0' != name[name_size - 1])
+    {
+      GNUNET_break_op (0);
+      return;
+    }
+    sr->var_cb (sr->cls, mod, name, name + name_size,
+                ntohs (pmod->header.size) - sizeof (*pmod),
+                ntohs (pmod->value_size));
+    break;
+  }
+
+  case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MOD_CONT:
+    sr->var_cb (sr->cls, mod, NULL, (const char *) &mod[1],
+                mod_size - sizeof (*mod), 0);
+    break;
+  }
+}
+
+
+static int
+check_channel_message (void *cls,
+                       const struct GNUNET_PSYC_MessageHeader *pmsg)
+{
+  return GNUNET_OK;
+}
+
+
+static void
+handle_channel_message (void *cls,
+                        const struct GNUNET_PSYC_MessageHeader *pmsg)
+{
+  struct GNUNET_PSYC_Channel *chn = cls;
+
+  GNUNET_PSYC_receive_message (chn->recv, pmsg);
+}
+
+
+static void
+handle_channel_message_ack (void *cls,
+                            const struct GNUNET_MessageHeader *msg)
+{
+  struct GNUNET_PSYC_Channel *chn = cls;
+
+  GNUNET_PSYC_transmit_got_ack (chn->tmit);
+}
+
+
+static void
+handle_master_start_ack (void *cls,
+                         const struct GNUNET_PSYC_CountersResultMessage *cres)
+{
+  struct GNUNET_PSYC_Master *mst = cls;
+
+  int32_t result = ntohl (cres->result_code);
+  if (GNUNET_OK != result && GNUNET_NO != result)
+  {
+    LOG (GNUNET_ERROR_TYPE_ERROR, "Could not start master: %ld\n", result);
+    GNUNET_break (0);
+    /* FIXME: disconnect */
+  }
+  if (NULL != mst->start_cb)
+    mst->start_cb (mst->cb_cls, result, GNUNET_ntohll (cres->max_message_id));
+}
+
+
+static int
+check_master_join_request (void *cls,
+                           const struct GNUNET_PSYC_JoinRequestMessage *req)
+{
+  if ( ((sizeof (*req) + sizeof (struct GNUNET_PSYC_Message)) <= ntohs 
(req->header.size)) &&
+       (NULL == GNUNET_MQ_extract_nested_mh (req)) )
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  return GNUNET_OK;
+}
+
+
+static void
+handle_master_join_request (void *cls,
+                            const struct GNUNET_PSYC_JoinRequestMessage *req)
+{
+  struct GNUNET_PSYC_Master *mst = cls;
+
+  if (NULL == mst->join_req_cb)
+    return;
+
+  const struct GNUNET_PSYC_Message *join_msg = NULL;
+  if (sizeof (*req) + sizeof (*join_msg) <= ntohs (req->header.size))
+  {
+    join_msg = (struct GNUNET_PSYC_Message *) GNUNET_MQ_extract_nested_mh 
(req);
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "Received join_msg of type %u and size %u.\n",
+         ntohs (join_msg->header.type),
+         ntohs (join_msg->header.size));
+  }
+
+  struct GNUNET_PSYC_JoinHandle *jh = GNUNET_malloc (sizeof (*jh));
+  jh->mst = mst;
+  jh->slave_pub_key = req->slave_pub_key;
+
+  if (NULL != mst->join_req_cb)
+    mst->join_req_cb (mst->cb_cls, req, &req->slave_pub_key, join_msg, jh);
+}
+
+
+static void
+handle_slave_join_ack (void *cls,
+                       const struct GNUNET_PSYC_CountersResultMessage *cres)
+{
+  struct GNUNET_PSYC_Slave *slv = cls;
+
+  int32_t result = ntohl (cres->result_code);
+  if (GNUNET_YES != result && GNUNET_NO != result)
+  {
+    LOG (GNUNET_ERROR_TYPE_ERROR, "Could not join slave.\n");
+    GNUNET_break (0);
+    /* FIXME: disconnect */
+  }
+  if (NULL != slv->connect_cb)
+    slv->connect_cb (slv->cb_cls, result, GNUNET_ntohll 
(cres->max_message_id));
+}
+
+
+static int
+check_slave_join_decision (void *cls,
+                           const struct GNUNET_PSYC_JoinDecisionMessage *dcsn)
+{
+  return GNUNET_OK;
+}
+
+
+static void
+handle_slave_join_decision (void *cls,
+                            const struct GNUNET_PSYC_JoinDecisionMessage *dcsn)
+{
+  struct GNUNET_PSYC_Slave *slv = cls;
+
+  struct GNUNET_PSYC_Message *pmsg = NULL;
+  if (ntohs (dcsn->header.size) <= sizeof (*dcsn) + sizeof (*pmsg))
+    pmsg = (struct GNUNET_PSYC_Message *) &dcsn[1];
+
+  if (NULL != slv->join_dcsn_cb)
+    slv->join_dcsn_cb (slv->cb_cls, dcsn, ntohl (dcsn->is_admitted), pmsg);
+}
+
+
+static void
+channel_cleanup (struct GNUNET_PSYC_Channel *chn)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "cleaning up channel %p\n",
+              chn);
+  if (NULL != chn->tmit)
+  {
+    GNUNET_PSYC_transmit_destroy (chn->tmit);
+    chn->tmit = NULL;
+  }
+  if (NULL != chn->recv)
+  {
+
+    GNUNET_PSYC_receive_destroy (chn->recv);
+    chn->recv = NULL;
+  }
+  if (NULL != chn->connect_env)
+  {
+    GNUNET_MQ_discard (chn->connect_env);
+    chn->connect_env = NULL;
+  }
+  if (NULL != chn->mq)
+  {
+    GNUNET_MQ_destroy (chn->mq);
+    chn->mq = NULL;
+  }
+  if (NULL != chn->disconnect_cb)
+  {
+    chn->disconnect_cb (chn->disconnect_cls);
+    chn->disconnect_cb = NULL;
+  }
+  GNUNET_free (chn);
+}
+
+
+static void
+handle_channel_part_ack (void *cls,
+                         const struct GNUNET_MessageHeader *msg)
+{
+  struct GNUNET_PSYC_Channel *chn = cls;
+
+  channel_cleanup (chn); 
+}
+
+
+/*** MASTER ***/
+
+
+static void
+master_connect (struct GNUNET_PSYC_Master *mst);
+
+
+static void
+master_reconnect (void *cls)
+{
+  master_connect (cls);
+}
+
+
+/**
+ * Master client disconnected from service.
+ *
+ * Reconnect after backoff period.
+ */
+static void
+master_disconnected (void *cls, enum GNUNET_MQ_Error error)
+{
+  struct GNUNET_PSYC_Master *mst = cls;
+  struct GNUNET_PSYC_Channel *chn = &mst->chn;
+
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Master client disconnected (%d), re-connecting\n",
+       (int) error);
+  if (NULL != chn->tmit)
+  {
+    GNUNET_PSYC_transmit_destroy (chn->tmit);
+    chn->tmit = NULL;
+  }
+  if (NULL != chn->mq)
+  {
+    GNUNET_MQ_destroy (chn->mq);
+    chn->mq = NULL;
+  }
+  chn->reconnect_task = GNUNET_SCHEDULER_add_delayed (chn->reconnect_delay,
+                                                      master_reconnect,
+                                                      mst);
+  chn->reconnect_delay = GNUNET_TIME_STD_BACKOFF (chn->reconnect_delay);
+}
+
+
+static void
+master_connect (struct GNUNET_PSYC_Master *mst)
+{
+  struct GNUNET_PSYC_Channel *chn = &mst->chn;
+
+  struct GNUNET_MQ_MessageHandler handlers[] = {
+    GNUNET_MQ_hd_fixed_size (master_start_ack,
+                             GNUNET_MESSAGE_TYPE_PSYC_MASTER_START_ACK,
+                             struct GNUNET_PSYC_CountersResultMessage,
+                             mst),
+    GNUNET_MQ_hd_var_size (master_join_request,
+                           GNUNET_MESSAGE_TYPE_PSYC_JOIN_REQUEST,
+                           struct GNUNET_PSYC_JoinRequestMessage,
+                           mst),
+    GNUNET_MQ_hd_fixed_size (channel_part_ack,
+                             GNUNET_MESSAGE_TYPE_PSYC_PART_ACK,
+                             struct GNUNET_MessageHeader,
+                             chn),
+    GNUNET_MQ_hd_var_size (channel_message,
+                           GNUNET_MESSAGE_TYPE_PSYC_MESSAGE,
+                           struct GNUNET_PSYC_MessageHeader,
+                           chn),
+    GNUNET_MQ_hd_fixed_size (channel_message_ack,
+                             GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_ACK,
+                             struct GNUNET_MessageHeader,
+                             chn),
+    GNUNET_MQ_hd_var_size (channel_history_result,
+                           GNUNET_MESSAGE_TYPE_PSYC_HISTORY_RESULT,
+                           struct GNUNET_OperationResultMessage,
+                           chn),
+    GNUNET_MQ_hd_var_size (channel_state_result,
+                           GNUNET_MESSAGE_TYPE_PSYC_STATE_RESULT,
+                           struct GNUNET_OperationResultMessage,
+                           chn),
+    GNUNET_MQ_hd_var_size (channel_result,
+                           GNUNET_MESSAGE_TYPE_PSYC_RESULT_CODE,
+                           struct GNUNET_OperationResultMessage,
+                           chn),
+    GNUNET_MQ_handler_end ()
+  };
+
+  chn->mq = GNUNET_CLIENT_connect (chn->cfg,
+                                   "psyc",
+                                   handlers,
+                                   &master_disconnected,
+                                   mst);
+  GNUNET_assert (NULL != chn->mq);
+  chn->tmit = GNUNET_PSYC_transmit_create (chn->mq);
+
+  GNUNET_MQ_send_copy (chn->mq, chn->connect_env);
+}
+
+
+/**
+ * Start a PSYC master channel.
+ *
+ * Will start a multicast group identified by the given ECC key.  Messages
+ * received from group members will be given to the respective handler methods.
+ * If a new member wants to join a group, the "join" method handler will be
+ * invoked; the join handler must then generate a "join" message to approve the
+ * joining of the new member.  The channel can also change group membership
+ * without explicit requests.  Note that PSYC doesn't itself "understand" join
+ * or part messages, the respective methods must call other PSYC functions to
+ * inform PSYC about the meaning of the respective events.
+ *
+ * @param cfg  Configuration to use (to connect to PSYC service).
+ * @param channel_key  ECC key that will be used to sign messages for this
+ *        PSYC session. The public key is used to identify the PSYC channel.
+ *        Note that end-users will usually not use the private key directly, 
but
+ *        rather look it up in GNS for places managed by other users, or select
+ *        a file with the private key(s) when setting up their own channels
+ *        FIXME: we'll likely want to use NOT the p521 curve here, but a 
cheaper
+ *        one in the future.
+ * @param policy  Channel policy specifying join and history restrictions.
+ *        Used to automate join decisions.
+ * @param message_cb  Function to invoke on message parts received from slaves.
+ * @param join_request_cb  Function to invoke when a slave wants to join.
+ * @param master_start_cb  Function to invoke after the channel master started.
+ * @param cls  Closure for @a method and @a join_cb.
+ *
+ * @return Handle for the channel master, NULL on error.
+ */
+struct GNUNET_PSYC_Master *
+GNUNET_PSYC_master_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
+                          const struct GNUNET_CRYPTO_EddsaPrivateKey 
*channel_key,
+                          enum GNUNET_PSYC_Policy policy,
+                          GNUNET_PSYC_MasterStartCallback start_cb,
+                          GNUNET_PSYC_JoinRequestCallback join_request_cb,
+                          GNUNET_PSYC_MessageCallback message_cb,
+                          GNUNET_PSYC_MessagePartCallback message_part_cb,
+                          void *cls)
+{
+  struct GNUNET_PSYC_Master *mst = GNUNET_new (struct GNUNET_PSYC_Master);
+  struct GNUNET_PSYC_Channel *chn = &mst->chn;
+  struct MasterStartRequest *req;
+
+  chn->connect_env = GNUNET_MQ_msg (req,
+                                    GNUNET_MESSAGE_TYPE_PSYC_MASTER_START);
+  req->channel_key = *channel_key;
+  req->policy = policy;
+
+  chn->cfg = cfg;
+  chn->is_master = GNUNET_YES;
+  chn->reconnect_delay = GNUNET_TIME_UNIT_MILLISECONDS;
+
+  chn->op = GNUNET_OP_create ();
+  chn->recv = GNUNET_PSYC_receive_create (message_cb, message_part_cb, cls);
+
+  mst->start_cb = start_cb;
+  mst->join_req_cb = join_request_cb;
+  mst->cb_cls = cls;
+
+  master_connect (mst);
+  return mst;
+}
+
+
+/**
+ * Stop a PSYC master channel.
+ *
+ * @param master PSYC channel master to stop.
+ * @param keep_active  FIXME
+ */
+void
+GNUNET_PSYC_master_stop (struct GNUNET_PSYC_Master *mst,
+                         int keep_active,
+                         GNUNET_ContinuationCallback stop_cb,
+                         void *stop_cls)
+{
+  struct GNUNET_PSYC_Channel *chn = &mst->chn;
+  struct GNUNET_MQ_Envelope *env;
+
+  chn->is_disconnecting = GNUNET_YES;
+  chn->disconnect_cb = stop_cb;
+  chn->disconnect_cls = stop_cls;
+  env = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_PSYC_PART_REQUEST);
+  GNUNET_MQ_send (chn->mq, env);
+}
+
+
+/**
+ * Function to call with the decision made for a join request.
+ *
+ * Must be called once and only once in response to an invocation of the
+ * #GNUNET_PSYC_JoinCallback.
+ *
+ * @param jh Join request handle.
+ * @param is_admitted  #GNUNET_YES    if the join is approved,
+ *                     #GNUNET_NO     if it is disapproved,
+ *                     #GNUNET_SYSERR if we cannot answer the request.
+ * @param relay_count Number of relays given.
+ * @param relays Array of suggested peers that might be useful relays to use
+ *        when joining the multicast group (essentially a list of peers that
+ *        are already part of the multicast group and might thus be willing
+ *        to help with routing).  If empty, only this local peer (which must
+ *        be the multicast origin) is a good candidate for building the
+ *        multicast tree.  Note that it is unnecessary to specify our own
+ *        peer identity in this array.
+ * @param join_resp  Application-dependent join response message.
+ *
+ * @return #GNUNET_OK on success,
+ *         #GNUNET_SYSERR if the message is too large.
+ */
+int
+GNUNET_PSYC_join_decision (struct GNUNET_PSYC_JoinHandle *jh,
+                           int is_admitted,
+                           uint32_t relay_count,
+                           const struct GNUNET_PeerIdentity *relays,
+                           const struct GNUNET_PSYC_Message *join_resp)
+{
+  struct GNUNET_PSYC_Channel *chn = &jh->mst->chn;
+  struct GNUNET_PSYC_JoinDecisionMessage *dcsn;
+  uint16_t join_resp_size
+    = (NULL != join_resp) ? ntohs (join_resp->header.size) : 0;
+  uint16_t relay_size = relay_count * sizeof (*relays);
+
+  if (GNUNET_MULTICAST_FRAGMENT_MAX_PAYLOAD
+      < sizeof (*dcsn) + relay_size + join_resp_size)
+    return GNUNET_SYSERR;
+
+  struct GNUNET_MQ_Envelope *
+    env = GNUNET_MQ_msg_extra (dcsn, relay_size + join_resp_size,
+                               GNUNET_MESSAGE_TYPE_PSYC_JOIN_DECISION);
+  dcsn->is_admitted = htonl (is_admitted);
+  dcsn->slave_pub_key = jh->slave_pub_key;
+
+  if (0 < join_resp_size)
+    GNUNET_memcpy (&dcsn[1], join_resp, join_resp_size);
+
+  GNUNET_MQ_send (chn->mq, env);
+  GNUNET_free (jh);
+  return GNUNET_OK;
+}
+
+
+/**
+ * Send a message to call a method to all members in the PSYC channel.
+ *
+ * @param master Handle to the PSYC channel.
+ * @param method_name Which method should be invoked.
+ * @param notify_mod Function to call to obtain modifiers.
+ * @param notify_data Function to call to obtain fragments of the data.
+ * @param notify_cls Closure for @a notify_mod and @a notify_data.
+ * @param flags Flags for the message being transmitted.
+ *
+ * @return Transmission handle, NULL on error (i.e. more than one request 
queued).
+ */
+struct GNUNET_PSYC_MasterTransmitHandle *
+GNUNET_PSYC_master_transmit (struct GNUNET_PSYC_Master *mst,
+                             const char *method_name,
+                             GNUNET_PSYC_TransmitNotifyModifier notify_mod,
+                             GNUNET_PSYC_TransmitNotifyData notify_data,
+                             void *notify_cls,
+                             enum GNUNET_PSYC_MasterTransmitFlags flags)
+{
+  if (GNUNET_OK
+      == GNUNET_PSYC_transmit_message (mst->chn.tmit, method_name, NULL,
+                                       notify_mod, notify_data, notify_cls,
+                                       flags))
+    return (struct GNUNET_PSYC_MasterTransmitHandle *) mst->chn.tmit;
+  else
+    return NULL;
+}
+
+
+/**
+ * Resume transmission to the channel.
+ *
+ * @param tmit  Handle of the request that is being resumed.
+ */
+void
+GNUNET_PSYC_master_transmit_resume (struct GNUNET_PSYC_MasterTransmitHandle 
*tmit)
+{
+  GNUNET_PSYC_transmit_resume ((struct GNUNET_PSYC_TransmitHandle *) tmit);
+}
+
+
+/**
+ * Abort transmission request to the channel.
+ *
+ * @param tmit  Handle of the request that is being aborted.
+ */
+void
+GNUNET_PSYC_master_transmit_cancel (struct GNUNET_PSYC_MasterTransmitHandle 
*tmit)
+{
+  GNUNET_PSYC_transmit_cancel ((struct GNUNET_PSYC_TransmitHandle *) tmit);
+}
+
+
+/**
+ * Convert a channel @a master to a @e channel handle to access the @e channel
+ * APIs.
+ *
+ * @param master Channel master handle.
+ *
+ * @return Channel handle, valid for as long as @a master is valid.
+ */
+struct GNUNET_PSYC_Channel *
+GNUNET_PSYC_master_get_channel (struct GNUNET_PSYC_Master *master)
+{
+  return &master->chn;
+}
+
+
+/*** SLAVE ***/
+
+
+static void
+slave_connect (struct GNUNET_PSYC_Slave *slv);
+
+
+static void
+slave_reconnect (void *cls)
+{
+  slave_connect (cls);
+}
+
+
+/**
+ * Slave client disconnected from service.
+ *
+ * Reconnect after backoff period.
+ */
+static void
+slave_disconnected (void *cls,
+                    enum GNUNET_MQ_Error error)
+{
+  struct GNUNET_PSYC_Slave *slv = cls;
+  struct GNUNET_PSYC_Channel *chn = &slv->chn;
+
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Slave client disconnected (%d), re-connecting\n",
+       (int) error);
+  if (NULL != chn->tmit)
+  {
+    GNUNET_PSYC_transmit_destroy (chn->tmit);
+    chn->tmit = NULL;
+  }
+  if (NULL != chn->mq)
+  {
+    GNUNET_MQ_destroy (chn->mq);
+    chn->mq = NULL;
+  }
+  chn->reconnect_task = GNUNET_SCHEDULER_add_delayed (chn->reconnect_delay,
+                                                      &slave_reconnect,
+                                                      slv);
+  chn->reconnect_delay = GNUNET_TIME_STD_BACKOFF (chn->reconnect_delay);
+}
+
+
+static void
+slave_connect (struct GNUNET_PSYC_Slave *slv)
+{
+  struct GNUNET_PSYC_Channel *chn = &slv->chn;
+
+  struct GNUNET_MQ_MessageHandler handlers[] = {
+    GNUNET_MQ_hd_fixed_size (slave_join_ack,
+                             GNUNET_MESSAGE_TYPE_PSYC_SLAVE_JOIN_ACK,
+                             struct GNUNET_PSYC_CountersResultMessage,
+                             slv),
+    GNUNET_MQ_hd_var_size (slave_join_decision,
+                           GNUNET_MESSAGE_TYPE_PSYC_JOIN_DECISION,
+                           struct GNUNET_PSYC_JoinDecisionMessage,
+                           slv),
+    GNUNET_MQ_hd_fixed_size (channel_part_ack,
+                             GNUNET_MESSAGE_TYPE_PSYC_PART_ACK,
+                             struct GNUNET_MessageHeader,
+                             chn),
+    GNUNET_MQ_hd_var_size (channel_message,
+                           GNUNET_MESSAGE_TYPE_PSYC_MESSAGE,
+                           struct GNUNET_PSYC_MessageHeader,
+                           chn),
+    GNUNET_MQ_hd_fixed_size (channel_message_ack,
+                             GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_ACK,
+                             struct GNUNET_MessageHeader,
+                             chn),
+    GNUNET_MQ_hd_var_size (channel_history_result,
+                           GNUNET_MESSAGE_TYPE_PSYC_HISTORY_RESULT,
+                           struct GNUNET_OperationResultMessage,
+                           chn),
+    GNUNET_MQ_hd_var_size (channel_state_result,
+                           GNUNET_MESSAGE_TYPE_PSYC_STATE_RESULT,
+                           struct GNUNET_OperationResultMessage,
+                           chn),
+    GNUNET_MQ_hd_var_size (channel_result,
+                           GNUNET_MESSAGE_TYPE_PSYC_RESULT_CODE,
+                           struct GNUNET_OperationResultMessage,
+                           chn),
+    GNUNET_MQ_handler_end ()
+  };
+
+  chn->mq = GNUNET_CLIENT_connect (chn->cfg,
+                                   "psyc",
+                                   handlers,
+                                   &slave_disconnected,
+                                   slv);
+  if (NULL == chn->mq)
+  {
+    chn->reconnect_task = GNUNET_SCHEDULER_add_delayed (chn->reconnect_delay,
+                                                        &slave_reconnect,
+                                                        slv);
+    chn->reconnect_delay = GNUNET_TIME_STD_BACKOFF (chn->reconnect_delay);
+    return;
+  }
+  chn->tmit = GNUNET_PSYC_transmit_create (chn->mq);
+
+  GNUNET_MQ_send_copy (chn->mq, chn->connect_env);
+}
+
+
+/**
+ * Join a PSYC channel.
+ *
+ * The entity joining is always the local peer.  The user must immediately use
+ * the GNUNET_PSYC_slave_transmit() functions to transmit a @e join_msg to the
+ * channel; if the join request succeeds, the channel state (and @e recent
+ * method calls) will be replayed to the joining member.  There is no explicit
+ * notification on failure (as the channel may simply take days to approve,
+ * and disapproval is simply being ignored).
+ *
+ * @param cfg
+ *        Configuration to use.
+ * @param channel_key  ECC public key that identifies the channel we wish to 
join.
+ * @param slave_key  ECC private-public key pair that identifies the slave, and
+ *        used by multicast to sign the join request and subsequent unicast
+ *        requests sent to the master.
+ * @param origin  Peer identity of the origin.
+ * @param relay_count  Number of peers in the @a relays array.
+ * @param relays  Peer identities of members of the multicast group, which 
serve
+ *        as relays and used to join the group at.
+ * @param message_cb  Function to invoke on message parts received from the
+ *        channel, typically at least contains method handlers for @e join and
+ *        @e part.
+ * @param slave_connect_cb  Function invoked once we have connected to the
+ *        PSYC service.
+ * @param join_decision_cb  Function invoked once we have received a join
+ *       decision.
+ * @param cls  Closure for @a message_cb and @a slave_joined_cb.
+ * @param method_name  Method name for the join request.
+ * @param env  Environment containing transient variables for the request, or 
NULL.
+ * @param data  Payload for the join message.
+ * @param data_size  Number of bytes in @a data.
+ *
+ * @return Handle for the slave, NULL on error.
+ */
+struct GNUNET_PSYC_Slave *
+GNUNET_PSYC_slave_join (const struct GNUNET_CONFIGURATION_Handle *cfg,
+                        const struct GNUNET_CRYPTO_EddsaPublicKey 
*channel_pub_key,
+                        const struct GNUNET_CRYPTO_EcdsaPrivateKey *slave_key,
+                        enum GNUNET_PSYC_SlaveJoinFlags flags,
+                        const struct GNUNET_PeerIdentity *origin,
+                        uint32_t relay_count,
+                        const struct GNUNET_PeerIdentity *relays,
+                        GNUNET_PSYC_MessageCallback message_cb,
+                        GNUNET_PSYC_MessagePartCallback message_part_cb,
+                        GNUNET_PSYC_SlaveConnectCallback connect_cb,
+                        GNUNET_PSYC_JoinDecisionCallback join_decision_cb,
+                        void *cls,
+                        const struct GNUNET_PSYC_Message *join_msg)
+{
+  struct GNUNET_PSYC_Slave *slv = GNUNET_malloc (sizeof (*slv));
+  struct GNUNET_PSYC_Channel *chn = &slv->chn;
+  uint16_t relay_size = relay_count * sizeof (*relays);
+  uint16_t join_msg_size;
+  if (NULL == join_msg)
+    join_msg_size = 0;
+  else
+    join_msg_size = ntohs (join_msg->header.size);
+
+  struct SlaveJoinRequest *req;
+  chn->connect_env = GNUNET_MQ_msg_extra (req, relay_size + join_msg_size,
+                                          GNUNET_MESSAGE_TYPE_PSYC_SLAVE_JOIN);
+  req->channel_pub_key = *channel_pub_key;
+  req->slave_key = *slave_key;
+  req->origin = *origin;
+  req->relay_count = htonl (relay_count);
+  req->flags = htonl (flags);
+
+  if (0 < relay_size)
+    GNUNET_memcpy (&req[1], relays, relay_size);
+
+  if (NULL != join_msg)
+    GNUNET_memcpy ((char *) &req[1] + relay_size, join_msg, join_msg_size);
+
+  chn->cfg = cfg;
+  chn->is_master = GNUNET_NO;
+  chn->reconnect_delay = GNUNET_TIME_UNIT_MILLISECONDS;
+
+  chn->op = GNUNET_OP_create ();
+  chn->recv = GNUNET_PSYC_receive_create (message_cb, message_part_cb, cls);
+
+  slv->connect_cb = connect_cb;
+  slv->join_dcsn_cb = join_decision_cb;
+  slv->cb_cls = cls;
+
+  slave_connect (slv);
+  return slv;
+}
+
+
+/**
+ * Part a PSYC channel.
+ *
+ * Will terminate the connection to the PSYC service.  Polite clients should
+ * first explicitly send a part request (via GNUNET_PSYC_slave_transmit()).
+ *
+ * @param slave Slave handle.
+ */
+void
+GNUNET_PSYC_slave_part (struct GNUNET_PSYC_Slave *slv,
+                        int keep_active,
+                        GNUNET_ContinuationCallback part_cb,
+                        void *part_cls)
+{
+  struct GNUNET_PSYC_Channel *chn = &slv->chn;
+  struct GNUNET_MQ_Envelope *env;
+
+  chn->is_disconnecting = GNUNET_YES;
+  chn->disconnect_cb = part_cb;
+  chn->disconnect_cls = part_cls;
+  env = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_PSYC_PART_REQUEST);
+  GNUNET_MQ_send (chn->mq, env);
+}
+
+
+/**
+ * Request a message to be sent to the channel master.
+ *
+ * @param slave Slave handle.
+ * @param method_name Which (PSYC) method should be invoked (on host).
+ * @param notify_mod Function to call to obtain modifiers.
+ * @param notify_data Function to call to obtain fragments of the data.
+ * @param notify_cls Closure for @a notify.
+ * @param flags Flags for the message being transmitted.
+ *
+ * @return Transmission handle, NULL on error (i.e. more than one request
+ *         queued).
+ */
+struct GNUNET_PSYC_SlaveTransmitHandle *
+GNUNET_PSYC_slave_transmit (struct GNUNET_PSYC_Slave *slv,
+                            const char *method_name,
+                            GNUNET_PSYC_TransmitNotifyModifier notify_mod,
+                            GNUNET_PSYC_TransmitNotifyData notify_data,
+                            void *notify_cls,
+                            enum GNUNET_PSYC_SlaveTransmitFlags flags)
+
+{
+  if (GNUNET_OK
+      == GNUNET_PSYC_transmit_message (slv->chn.tmit, method_name, NULL,
+                                       notify_mod, notify_data, notify_cls,
+                                       flags))
+    return (struct GNUNET_PSYC_SlaveTransmitHandle *) slv->chn.tmit;
+  else
+    return NULL;
+}
+
+
+/**
+ * Resume transmission to the master.
+ *
+ * @param tmit Handle of the request that is being resumed.
+ */
+void
+GNUNET_PSYC_slave_transmit_resume (struct GNUNET_PSYC_SlaveTransmitHandle 
*tmit)
+{
+  GNUNET_PSYC_transmit_resume ((struct GNUNET_PSYC_TransmitHandle *) tmit);
+}
+
+
+/**
+ * Abort transmission request to master.
+ *
+ * @param tmit Handle of the request that is being aborted.
+ */
+void
+GNUNET_PSYC_slave_transmit_cancel (struct GNUNET_PSYC_SlaveTransmitHandle 
*tmit)
+{
+  GNUNET_PSYC_transmit_cancel ((struct GNUNET_PSYC_TransmitHandle *) tmit);
+}
+
+
+/**
+ * Convert @a slave to a @e channel handle to access the @e channel APIs.
+ *
+ * @param slv Slave handle.
+ *
+ * @return Channel handle, valid for as long as @a slave is valid.
+ */
+struct GNUNET_PSYC_Channel *
+GNUNET_PSYC_slave_get_channel (struct GNUNET_PSYC_Slave *slv)
+{
+  return &slv->chn;
+}
+
+
+/**
+ * Add a slave to the channel's membership list.
+ *
+ * Note that this will NOT generate any PSYC traffic, it will merely update the
+ * local database to modify how we react to <em>membership test</em> queries.
+ * The channel master still needs to explicitly transmit a @e join message to
+ * notify other channel members and they then also must still call this 
function
+ * in their respective methods handling the @e join message.  This way, how @e
+ * join and @e part operations are exactly implemented is still up to the
+ * application; for example, there might be a @e part_all method to kick out
+ * everyone.
+ *
+ * Note that channel slaves are explicitly trusted to execute such methods
+ * correctly; not doing so correctly will result in either denying other slaves
+ * access or offering access to channel data to non-members.
+ *
+ * @param chn
+ *        Channel handle.
+ * @param slave_pub_key
+ *        Identity of channel slave to add.
+ * @param announced_at
+ *        ID of the message that announced the membership change.
+ * @param effective_since
+ *        Addition of slave is in effect since this message ID.
+ * @param result_cb
+ *        Function to call with the result of the operation.
+ *        The @e result_code argument is #GNUNET_OK on success, or
+ *        #GNUNET_SYSERR on error.  In case of an error, the @e data argument
+ *        can contain an optional error message.
+ * @param cls
+ *        Closure for @a result_cb.
+ */
+void
+GNUNET_PSYC_channel_slave_add (struct GNUNET_PSYC_Channel *chn,
+                               const struct GNUNET_CRYPTO_EcdsaPublicKey 
*slave_pub_key,
+                               uint64_t announced_at,
+                               uint64_t effective_since,
+                               GNUNET_ResultCallback result_cb,
+                               void *cls)
+{
+  struct ChannelMembershipStoreRequest *req;
+  struct GNUNET_MQ_Envelope *
+    env = GNUNET_MQ_msg (req, 
GNUNET_MESSAGE_TYPE_PSYC_CHANNEL_MEMBERSHIP_STORE);
+  req->slave_pub_key = *slave_pub_key;
+  req->announced_at = GNUNET_htonll (announced_at);
+  req->effective_since = GNUNET_htonll (effective_since);
+  req->did_join = GNUNET_YES;
+  req->op_id = GNUNET_htonll (GNUNET_OP_add (chn->op, result_cb, cls, NULL));
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "GNUNET_PSYC_channel_slave_add, OP ID: %" PRIu64 "\n",
+              GNUNET_ntohll (req->op_id));
+  GNUNET_MQ_send (chn->mq, env);
+}
+
+
+/**
+ * Remove a slave from the channel's membership list.
+ *
+ * Note that this will NOT generate any PSYC traffic, it will merely update the
+ * local database to modify how we react to <em>membership test</em> queries.
+ * The channel master still needs to explicitly transmit a @e part message to
+ * notify other channel members and they then also must still call this 
function
+ * in their respective methods handling the @e part message.  This way, how
+ * @e join and @e part operations are exactly implemented is still up to the
+ * application; for example, there might be a @e part_all message to kick out
+ * everyone.
+ *
+ * Note that channel members are explicitly trusted to perform these
+ * operations correctly; not doing so correctly will result in either
+ * denying members access or offering access to channel data to
+ * non-members.
+ *
+ * @param chn
+ *        Channel handle.
+ * @param slave_pub_key
+ *        Identity of channel slave to remove.
+ * @param announced_at
+ *        ID of the message that announced the membership change.
+ * @param result_cb
+ *        Function to call with the result of the operation.
+ *        The @e result_code argument is #GNUNET_OK on success, or
+ *        #GNUNET_SYSERR on error.  In case of an error, the @e data argument
+ *        can contain an optional error message.
+ * @param cls
+ *        Closure for @a result_cb.
+ */
+void
+GNUNET_PSYC_channel_slave_remove (struct GNUNET_PSYC_Channel *chn,
+                                  const struct GNUNET_CRYPTO_EcdsaPublicKey 
*slave_pub_key,
+                                  uint64_t announced_at,
+                                  GNUNET_ResultCallback result_cb,
+                                  void *cls)
+{
+  struct ChannelMembershipStoreRequest *req;
+  struct GNUNET_MQ_Envelope *
+    env = GNUNET_MQ_msg (req, 
GNUNET_MESSAGE_TYPE_PSYC_CHANNEL_MEMBERSHIP_STORE);
+  req->slave_pub_key = *slave_pub_key;
+  req->announced_at = GNUNET_htonll (announced_at);
+  req->did_join = GNUNET_NO;
+  req->op_id = GNUNET_htonll (GNUNET_OP_add (chn->op, result_cb, cls, NULL));
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "GNUNET_PSYC_channel_slave_remove, OP ID: %" PRIu64 "\n",
+              GNUNET_ntohll (req->op_id));
+  GNUNET_MQ_send (chn->mq, env);
+}
+
+
+static struct GNUNET_PSYC_HistoryRequest *
+channel_history_replay (struct GNUNET_PSYC_Channel *chn,
+                        uint64_t start_message_id,
+                        uint64_t end_message_id,
+                        uint64_t message_limit,
+                        const char *method_prefix,
+                        uint32_t flags,
+                        GNUNET_PSYC_MessageCallback message_cb,
+                        GNUNET_PSYC_MessagePartCallback message_part_cb,
+                        GNUNET_ResultCallback result_cb,
+                        void *cls)
+{
+  struct GNUNET_PSYC_HistoryRequestMessage *req;
+  struct GNUNET_PSYC_HistoryRequest *hist = GNUNET_malloc (sizeof (*hist));
+  hist->chn = chn;
+  hist->recv = GNUNET_PSYC_receive_create (message_cb, message_part_cb, cls);
+  hist->result_cb = result_cb;
+  hist->cls = cls;
+  hist->op_id = GNUNET_OP_add (chn->op, op_recv_history_result, hist, NULL);
+
+  GNUNET_assert (NULL != method_prefix);
+  uint16_t method_size = strnlen (method_prefix,
+                                  GNUNET_MAX_MESSAGE_SIZE
+                                  - sizeof (*req)) + 1;
+  GNUNET_assert ('\0' == method_prefix[method_size - 1]);
+
+  struct GNUNET_MQ_Envelope *
+    env = GNUNET_MQ_msg_extra (req, method_size,
+                               GNUNET_MESSAGE_TYPE_PSYC_HISTORY_REPLAY);
+  req->start_message_id = GNUNET_htonll (start_message_id);
+  req->end_message_id = GNUNET_htonll (end_message_id);
+  req->message_limit = GNUNET_htonll (message_limit);
+  req->flags = htonl (flags);
+  req->op_id = GNUNET_htonll (hist->op_id);
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "channel_history_replay, OP ID: %" PRIu64 "\n",
+              GNUNET_ntohll (req->op_id));
+  GNUNET_memcpy (&req[1], method_prefix, method_size);
+
+  GNUNET_MQ_send (chn->mq, env);
+  return hist;
+}
+
+
+/**
+ * Request to replay a part of the message history of the channel.
+ *
+ * Historic messages (but NOT the state at the time) will be replayed and given
+ * to the normal method handlers with a #GNUNET_PSYC_MESSAGE_HISTORIC flag set.
+ *
+ * Messages are retrieved from the local PSYCstore if available,
+ * otherwise requested from the network.
+ *
+ * @param channel
+ *        Which channel should be replayed?
+ * @param start_message_id
+ *        Earliest interesting point in history.
+ * @param end_message_id
+ *        Last (inclusive) interesting point in history.
+ * @param method_prefix
+ *        Retrieve only messages with a matching method prefix.
+ * @param flags
+ *        OR'ed enum GNUNET_PSYC_HistoryReplayFlags
+ * @param result_cb
+ *        Function to call when the requested history has been fully replayed.
+ * @param cls
+ *        Closure for the callbacks.
+ *
+ * @return Handle to cancel history replay operation.
+ */
+struct GNUNET_PSYC_HistoryRequest *
+GNUNET_PSYC_channel_history_replay (struct GNUNET_PSYC_Channel *chn,
+                                    uint64_t start_message_id,
+                                    uint64_t end_message_id,
+                                    const char *method_prefix,
+                                    uint32_t flags,
+                                    GNUNET_PSYC_MessageCallback message_cb,
+                                    GNUNET_PSYC_MessagePartCallback 
message_part_cb,
+                                    GNUNET_ResultCallback result_cb,
+                                    void *cls)
+{
+  return channel_history_replay (chn, start_message_id, end_message_id, 0,
+                                 method_prefix, flags,
+                                 message_cb, message_part_cb, result_cb, cls);
+}
+
+
+/**
+ * Request to replay the latest messages from the message history of the 
channel.
+ *
+ * Historic messages (but NOT the state at the time) will be replayed (given to
+ * the normal method handlers) if available and if access is permitted.
+ *
+ * @param channel
+ *        Which channel should be replayed?
+ * @param message_limit
+ *        Maximum number of messages to replay.
+ * @param method_prefix
+ *        Retrieve only messages with a matching method prefix.
+ *        Use NULL or "" to retrieve all.
+ * @param flags
+ *        OR'ed enum GNUNET_PSYC_HistoryReplayFlags
+ * @param result_cb
+ *        Function to call when the requested history has been fully replayed.
+ * @param cls
+ *        Closure for the callbacks.
+ *
+ * @return Handle to cancel history replay operation.
+ */
+struct GNUNET_PSYC_HistoryRequest *
+GNUNET_PSYC_channel_history_replay_latest (struct GNUNET_PSYC_Channel *chn,
+                                           uint64_t message_limit,
+                                           const char *method_prefix,
+                                           uint32_t flags,
+                                           GNUNET_PSYC_MessageCallback 
message_cb,
+                                           GNUNET_PSYC_MessagePartCallback 
message_part_cb,
+                                           GNUNET_ResultCallback result_cb,
+                                           void *cls)
+{
+  return channel_history_replay (chn, 0, 0, message_limit, method_prefix, 
flags,
+                                 message_cb, message_part_cb, result_cb, cls);
+}
+
+
+void
+GNUNET_PSYC_channel_history_replay_cancel (struct GNUNET_PSYC_Channel *channel,
+                                           struct GNUNET_PSYC_HistoryRequest 
*hist)
+{
+  GNUNET_PSYC_receive_destroy (hist->recv);
+  GNUNET_OP_remove (hist->chn->op, hist->op_id);
+  GNUNET_free (hist);
+}
+
+
+/**
+ * Retrieve the best matching channel state variable.
+ *
+ * If the requested variable name is not present in the state, the nearest
+ * less-specific name is matched; for example, requesting "_a_b" will match 
"_a"
+ * if "_a_b" does not exist.
+ *
+ * @param channel
+ *        Channel handle.
+ * @param full_name
+ *        Full name of the requested variable.
+ *        The actual variable returned might have a shorter name.
+ * @param var_cb
+ *        Function called once when a matching state variable is found.
+ *        Not called if there's no matching state variable.
+ * @param result_cb
+ *        Function called after the operation finished.
+ *        (i.e. all state variables have been returned via @a state_cb)
+ * @param cls
+ *        Closure for the callbacks.
+ */
+static struct GNUNET_PSYC_StateRequest *
+channel_state_get (struct GNUNET_PSYC_Channel *chn,
+                   uint16_t type, const char *name,
+                   GNUNET_PSYC_StateVarCallback var_cb,
+                   GNUNET_ResultCallback result_cb, void *cls)
+{
+  struct StateRequest *req;
+  struct GNUNET_PSYC_StateRequest *sr = GNUNET_malloc (sizeof (*sr));
+  sr->chn = chn;
+  sr->var_cb = var_cb;
+  sr->result_cb = result_cb;
+  sr->cls = cls;
+  sr->op_id = GNUNET_OP_add (chn->op, op_recv_state_result, sr, NULL);
+
+  GNUNET_assert (NULL != name);
+  size_t name_size = strnlen (name, GNUNET_MAX_MESSAGE_SIZE
+                              - sizeof (*req)) + 1;
+  struct GNUNET_MQ_Envelope *
+    env = GNUNET_MQ_msg_extra (req, name_size, type);
+  req->op_id = GNUNET_htonll (sr->op_id);
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "channel_state_get, OP ID: %" PRIu64 "\n",
+              GNUNET_ntohll (req->op_id));
+
+  GNUNET_memcpy (&req[1], name, name_size);
+
+  GNUNET_MQ_send (chn->mq, env);
+  return sr;
+}
+
+
+/**
+ * Retrieve the best matching channel state variable.
+ *
+ * If the requested variable name is not present in the state, the nearest
+ * less-specific name is matched; for example, requesting "_a_b" will match 
"_a"
+ * if "_a_b" does not exist.
+ *
+ * @param channel
+ *        Channel handle.
+ * @param full_name
+ *        Full name of the requested variable.
+ *        The actual variable returned might have a shorter name.
+ * @param var_cb
+ *        Function called once when a matching state variable is found.
+ *        Not called if there's no matching state variable.
+ * @param result_cb
+ *        Function called after the operation finished.
+ *        (i.e. all state variables have been returned via @a state_cb)
+ * @param cls
+ *        Closure for the callbacks.
+ */
+struct GNUNET_PSYC_StateRequest *
+GNUNET_PSYC_channel_state_get (struct GNUNET_PSYC_Channel *chn,
+                               const char *full_name,
+                               GNUNET_PSYC_StateVarCallback var_cb,
+                               GNUNET_ResultCallback result_cb,
+                               void *cls)
+{
+  return channel_state_get (chn, GNUNET_MESSAGE_TYPE_PSYC_STATE_GET,
+                            full_name, var_cb, result_cb, cls);
+
+}
+
+
+/**
+ * Return all channel state variables whose name matches a given prefix.
+ *
+ * A name matches if it starts with the given @a name_prefix, thus requesting
+ * the empty prefix ("") will match all values; requesting "_a_b" will also
+ * return values stored under "_a_b_c".
+ *
+ * The @a state_cb is invoked on all matching state variables asynchronously, 
as
+ * the state is stored in and retrieved from the PSYCstore,
+ *
+ * @param channel
+ *        Channel handle.
+ * @param name_prefix
+ *        Prefix of the state variable name to match.
+ * @param var_cb
+ *        Function called once when a matching state variable is found.
+ *        Not called if there's no matching state variable.
+ * @param result_cb
+ *        Function called after the operation finished.
+ *        (i.e. all state variables have been returned via @a state_cb)
+ * @param cls
+ *        Closure for the callbacks.
+ */
+struct GNUNET_PSYC_StateRequest *
+GNUNET_PSYC_channel_state_get_prefix (struct GNUNET_PSYC_Channel *chn,
+                                      const char *name_prefix,
+                                      GNUNET_PSYC_StateVarCallback var_cb,
+                                      GNUNET_ResultCallback result_cb,
+                                      void *cls)
+{
+  return channel_state_get (chn, GNUNET_MESSAGE_TYPE_PSYC_STATE_GET_PREFIX,
+                            name_prefix, var_cb, result_cb, cls);
+}
+
+
+/**
+ * Cancel a state request operation.
+ *
+ * @param sr
+ *        Handle for the operation to cancel.
+ */
+void
+GNUNET_PSYC_channel_state_get_cancel (struct GNUNET_PSYC_StateRequest *sr)
+{
+  GNUNET_OP_remove (sr->chn->op, sr->op_id);
+  GNUNET_free (sr);
+}
+
+/* end of psyc_api.c */
diff --git a/src/psyc/psyc_test_lib.h b/src/psyc/psyc_test_lib.h
new file mode 100644
index 0000000..0ad9910
--- /dev/null
+++ b/src/psyc/psyc_test_lib.h
@@ -0,0 +1,67 @@
+/*
+ * This file is part of GNUnet
+ * Copyright (C) 2013 GNUnet e.V.
+ *
+ * GNUnet is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * GNUnet 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
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+     SPDX-License-Identifier: AGPL3.0-or-later
+ */
+
+/**
+ * @file psyc/test_psyc_api_join.c
+ * @brief library for writing psyc tests
+ * @author xrs
+ */
+
+#define MAX_TESTBED_OPS 32
+
+struct pctx
+{
+  int idx;
+  
+  struct GNUNET_TESTBED_Peer *testbed_peer;
+  
+  const struct GNUNET_PeerIdentity *peer_id;
+
+  const struct GNUNET_PeerIdentity *peer_id_master;
+
+  /**
+   * Used to simulate egos (not peerid)
+   */
+  const struct GNUNET_CRYPTO_EcdsaPrivateKey *id_key;
+
+  const struct GNUNET_CRYPTO_EcdsaPublicKey *id_pub_key;
+
+  /**
+   * Used to store either GNUNET_PSYC_Master or GNUNET_PSYC_Slave handle
+   */
+  void *psyc;
+
+  struct GNUNET_PSYC_Channel *channel;
+
+  const struct GNUNET_CRYPTO_EddsaPrivateKey *channel_key;
+
+  struct GNUNET_CRYPTO_EddsaPublicKey *channel_pub_key;
+
+  int test_ok;
+};
+
+static struct GNUNET_SCHEDULER_Task *timeout_task_id;
+
+static int result = GNUNET_SYSERR;
+
+static struct GNUNET_TESTBED_Operation *op[MAX_TESTBED_OPS];
+
+static int op_cnt = 0;
+
diff --git a/src/psyc/test_psyc.c b/src/psyc/test_psyc.c
new file mode 100644
index 0000000..b6e27bb
--- /dev/null
+++ b/src/psyc/test_psyc.c
@@ -0,0 +1,1018 @@
+/*
+ * This file is part of GNUnet
+ * Copyright (C) 2013 GNUnet e.V.
+ *
+ * GNUnet is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * GNUnet 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
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+     SPDX-License-Identifier: AGPL3.0-or-later
+ */
+
+/**
+ * @file psyc/test_psyc.c
+ * @brief Tests for the PSYC API.
+ * @author Gabor X Toth
+ * @author Christian Grothoff
+ */
+
+#include <inttypes.h>
+
+#include "platform.h"
+#include "gnunet_crypto_lib.h"
+#include "gnunet_common.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_testing_lib.h"
+#include "gnunet_psyc_util_lib.h"
+#include "gnunet_psyc_service.h"
+
+#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
+
+/**
+ * Return value from 'main'.
+ */
+static int res;
+
+static const struct GNUNET_CONFIGURATION_Handle *cfg;
+
+static struct GNUNET_PeerIdentity this_peer;
+
+/**
+ * Handle for task for timeout termination.
+ */
+static struct GNUNET_SCHEDULER_Task * end_badly_task;
+
+static struct GNUNET_PSYC_Master *mst;
+static struct GNUNET_PSYC_Slave *slv;
+
+static struct GNUNET_PSYC_Channel *mst_chn, *slv_chn;
+
+static struct GNUNET_CRYPTO_EddsaPrivateKey *channel_key;
+static struct GNUNET_CRYPTO_EcdsaPrivateKey *slave_key;
+
+static struct GNUNET_CRYPTO_EddsaPublicKey channel_pub_key;
+static struct GNUNET_CRYPTO_EcdsaPublicKey slave_pub_key;
+
+struct TransmitClosure
+{
+  struct GNUNET_PSYC_MasterTransmitHandle *mst_tmit;
+  struct GNUNET_PSYC_SlaveTransmitHandle *slv_tmit;
+  struct GNUNET_PSYC_Environment *env;
+  struct GNUNET_PSYC_Modifier *mod;
+  char *data[16];
+  const char *mod_value;
+  size_t mod_value_size;
+  uint8_t data_delay[16];
+  uint8_t data_count;
+  uint8_t paused;
+  uint8_t n;
+};
+
+static struct TransmitClosure *tmit;
+
+static uint8_t join_req_count, end_count;
+
+enum
+{
+  TEST_NONE                         = 0,
+  TEST_MASTER_START                 = 1,
+  TEST_SLAVE_JOIN_REJECT            = 2,
+  TEST_SLAVE_JOIN_ACCEPT            = 3,
+  TEST_SLAVE_ADD                    = 4,
+  TEST_SLAVE_REMOVE                 = 5,
+  TEST_SLAVE_TRANSMIT               = 6,
+  TEST_MASTER_TRANSMIT              = 7,
+  TEST_MASTER_HISTORY_REPLAY_LATEST = 8,
+  TEST_SLAVE_HISTORY_REPLAY_LATEST  = 9,
+  TEST_MASTER_HISTORY_REPLAY       = 10,
+  TEST_SLAVE_HISTORY_REPLAY        = 11,
+  TEST_MASTER_STATE_GET            = 12,
+  TEST_SLAVE_STATE_GET             = 13,
+  TEST_MASTER_STATE_GET_PREFIX     = 14,
+  TEST_SLAVE_STATE_GET_PREFIX      = 15,
+} test;
+
+
+static void
+master_transmit ();
+
+static void
+master_history_replay_latest ();
+
+
+static void
+master_stopped (void *cls)
+{
+  if (NULL != tmit)
+  {
+    GNUNET_PSYC_env_destroy (tmit->env);
+    GNUNET_free (tmit);
+    tmit = NULL;
+  }
+  GNUNET_SCHEDULER_shutdown ();
+}
+
+
+static void
+slave_parted (void *cls)
+{
+  if (NULL != mst)
+  {
+    GNUNET_PSYC_master_stop (mst, GNUNET_NO, &master_stopped, NULL);
+    mst = NULL;
+  }
+  else
+    master_stopped (NULL);
+}
+
+
+/**
+ * Clean up all resources used.
+ */
+static void
+cleanup ()
+{
+  if (NULL != slv)
+  {
+    GNUNET_PSYC_slave_part (slv, GNUNET_NO, &slave_parted, NULL);
+    slv = NULL;
+  }
+  else
+    slave_parted (NULL);
+}
+
+
+/**
+ * Terminate the test case (failure).
+ *
+ * @param cls NULL
+ */
+static void
+end_badly (void *cls)
+{
+  res = 1;
+  cleanup ();
+  GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Test FAILED.\n");
+}
+
+
+/**
+ * Terminate the test case (success).
+ *
+ * @param cls NULL
+ */
+static void
+end_normally (void *cls)
+{
+  res = 0;
+  cleanup ();
+  GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Test PASSED.\n");
+}
+
+
+/**
+ * Finish the test case (successfully).
+ */
+static void
+end ()
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Ending tests.\n");
+
+  if (end_badly_task != NULL)
+  {
+    GNUNET_SCHEDULER_cancel (end_badly_task);
+    end_badly_task = NULL;
+  }
+  GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS,
+                               &end_normally, NULL);
+}
+
+
+static void
+master_message_cb (void *cls, const struct GNUNET_PSYC_MessageHeader *msg)
+{
+  GNUNET_assert (NULL != msg);
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Test #%d: Master got PSYC message fragment of size %u "
+              "belonging to message ID %" PRIu64 " with flags %x\n",
+              test, ntohs (msg->header.size),
+              GNUNET_ntohll (msg->message_id), ntohl (msg->flags));
+  // FIXME
+}
+
+
+static void
+master_message_part_cb (void *cls, const struct GNUNET_PSYC_MessageHeader *msg,
+                        const struct GNUNET_MessageHeader *pmsg)
+{
+  GNUNET_assert (NULL != msg && NULL != pmsg);
+
+  uint64_t message_id = GNUNET_ntohll (msg->message_id);
+  uint32_t flags = ntohl (msg->flags);
+
+  uint16_t type = ntohs (pmsg->type);
+  uint16_t size = ntohs (pmsg->size);
+
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Test #%d: Master got message part of type %u and size %u "
+              "belonging to message ID %" PRIu64 " with flags %x\n",
+              test, type, size, message_id, flags);
+
+  switch (test)
+  {
+  case TEST_SLAVE_TRANSMIT:
+    if (GNUNET_PSYC_MESSAGE_REQUEST != flags)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "Test #%d: Unexpected request flags: %x" PRIu32 "\n",
+                  test, flags);
+      GNUNET_assert (0);
+      return;
+    }
+    // FIXME: check rest of message
+
+    if (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END == type)
+      master_transmit ();
+    break;
+
+  case TEST_MASTER_TRANSMIT:
+    if (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END == type && 2 == ++end_count)
+      master_history_replay_latest ();
+    break;
+
+  case TEST_MASTER_HISTORY_REPLAY:
+  case TEST_MASTER_HISTORY_REPLAY_LATEST:
+    if (GNUNET_PSYC_MESSAGE_HISTORIC != flags)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "Test #%d: Unexpected flags for historic message: %x" PRIu32 
"\n",
+                  test, flags);
+      GNUNET_assert (0);
+      return;
+    }
+    break;
+
+  default:
+    GNUNET_assert (0);
+  }
+}
+
+
+static void
+slave_message_cb (void *cls, const struct GNUNET_PSYC_MessageHeader *msg)
+{
+  GNUNET_assert (NULL != msg);
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Test #%d: Slave got PSYC message fragment of size %u "
+              "belonging to message ID %" PRIu64 " with flags %x\n",
+              test, ntohs (msg->header.size),
+              GNUNET_ntohll (msg->message_id), ntohl (msg->flags));
+  // FIXME
+}
+
+
+static void
+slave_message_part_cb (void *cls,
+                       const struct GNUNET_PSYC_MessageHeader *msg,
+                       const struct GNUNET_MessageHeader *pmsg)
+{
+  GNUNET_assert (NULL != msg && NULL != pmsg);
+
+  uint64_t message_id = GNUNET_ntohll (msg->message_id);
+  uint32_t flags = ntohl (msg->flags);
+
+  uint16_t type = ntohs (pmsg->type);
+  uint16_t size = ntohs (pmsg->size);
+
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Test #%d: Slave got message part of type %u and size %u "
+              "belonging to message ID %" PRIu64 " with flags %x\n",
+              test, type, size, message_id, flags);
+
+  switch (test)
+  {
+  case TEST_MASTER_TRANSMIT:
+    if (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END == type && 2 == ++end_count)
+      master_history_replay_latest ();
+    break;
+
+  case TEST_SLAVE_HISTORY_REPLAY:
+  case TEST_SLAVE_HISTORY_REPLAY_LATEST:
+    if (GNUNET_PSYC_MESSAGE_HISTORIC != flags)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "Test #%d: Unexpected flags for historic message: %x" PRIu32 
"\n",
+                  test, flags);
+      GNUNET_assert (0);
+      return;
+    }
+    break;
+
+  default:
+    GNUNET_assert (0);
+  }
+}
+
+
+static void
+state_get_var (void *cls, const struct GNUNET_MessageHeader *mod,
+               const char *name, const void *value,
+               uint32_t value_size, uint32_t full_value_size)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Got state var: %s\n%.*s\n",
+              name,
+              (int) value_size,
+              (const char *) value);
+}
+
+
+/*** Slave state_get_prefix() ***/
+
+static void
+slave_state_get_prefix_result (void *cls, int64_t result,
+                               const void *err_msg, uint16_t err_msg_size)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Test #%d: slave_state_get_prefix:\t%" PRId64 " (%.*s)\n",
+              test, result,
+              (int) err_msg_size,
+              (const char *) err_msg);
+  // FIXME: GNUNET_assert (2 == result);
+  end ();
+}
+
+
+static void
+slave_state_get_prefix ()
+{
+  test = TEST_SLAVE_STATE_GET_PREFIX;
+  GNUNET_PSYC_channel_state_get_prefix (slv_chn, "_foo", state_get_var,
+                                        slave_state_get_prefix_result, NULL);
+}
+
+
+/*** Master state_get_prefix() ***/
+
+
+static void
+master_state_get_prefix_result (void *cls, int64_t result,
+                                const void *err_msg, uint16_t err_msg_size)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Test #%d: master_state_get_prefix:\t%" PRId64 " (%s)\n",
+              test, result, (char *) err_msg);
+  // FIXME: GNUNET_assert (2 == result);
+  slave_state_get_prefix ();
+}
+
+
+static void
+master_state_get_prefix ()
+{
+  test = TEST_MASTER_STATE_GET_PREFIX;
+  GNUNET_PSYC_channel_state_get_prefix (mst_chn, "_foo", state_get_var,
+                                        master_state_get_prefix_result, NULL);
+}
+
+
+/*** Slave state_get() ***/
+
+
+static void
+slave_state_get_result (void *cls, int64_t result,
+                        const void *err_msg, uint16_t err_msg_size)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Test #%d: slave_state_get:\t%" PRId64 " (%.*s)\n",
+              test, result, err_msg_size, (char *) err_msg);
+  // FIXME: GNUNET_assert (2 == result);
+  master_state_get_prefix ();
+}
+
+
+static void
+slave_state_get ()
+{
+  test = TEST_SLAVE_STATE_GET;
+  GNUNET_PSYC_channel_state_get (slv_chn, "_foo_bar_baz", state_get_var,
+                                 slave_state_get_result, NULL);
+}
+
+
+/*** Master state_get() ***/
+
+
+static void
+master_state_get_result (void *cls, int64_t result,
+                         const void *err_msg, uint16_t err_msg_size)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Test #%d: master_state_get:\t%" PRId64 " (%.*s)\n",
+              test, result, err_msg_size, (char *) err_msg);
+  // FIXME: GNUNET_assert (1 == result);
+  slave_state_get ();
+}
+
+
+static void
+master_state_get ()
+{
+  test = TEST_MASTER_STATE_GET;
+  GNUNET_PSYC_channel_state_get (mst_chn, "_foo_bar_baz", state_get_var,
+                                 master_state_get_result, NULL);
+}
+
+
+/*** Slave history_replay() ***/
+
+static void
+slave_history_replay_result (void *cls, int64_t result,
+                             const void *err_msg, uint16_t err_msg_size)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Test #%d: slave_history_replay:\t%" PRId64 " (%.*s)\n",
+              test, result,
+              (int) err_msg_size,
+              (const char *) err_msg);
+  GNUNET_assert (9 == result);
+
+  master_state_get ();
+}
+
+
+static void
+slave_history_replay ()
+{
+  test = TEST_SLAVE_HISTORY_REPLAY;
+  GNUNET_PSYC_channel_history_replay (slv_chn, 1, 1, "",
+                                      GNUNET_PSYC_HISTORY_REPLAY_LOCAL,
+                                      slave_message_cb,
+                                      slave_message_part_cb,
+                                      slave_history_replay_result, NULL);
+}
+
+
+/*** Master history_replay() ***/
+
+
+static void
+master_history_replay_result (void *cls, int64_t result,
+                              const void *err_msg, uint16_t err_msg_size)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Test #%d: master_history_replay:\t%" PRId64 " (%.*s)\n",
+              test, result,
+              (int) err_msg_size,
+              (const char *) err_msg);
+  GNUNET_assert (9 == result);
+
+  slave_history_replay ();
+}
+
+
+static void
+master_history_replay ()
+{
+  test = TEST_MASTER_HISTORY_REPLAY;
+  GNUNET_PSYC_channel_history_replay (mst_chn, 1, 1, "",
+                                      GNUNET_PSYC_HISTORY_REPLAY_LOCAL,
+                                      master_message_cb,
+                                      master_message_part_cb,
+                                      master_history_replay_result, NULL);
+}
+
+
+/*** Slave history_replay_latest() ***/
+
+
+static void
+slave_history_replay_latest_result (void *cls, int64_t result,
+                                    const void *err_msg, uint16_t err_msg_size)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Test #%d: slave_history_replay_latest:\t%" PRId64 " (%.*s)\n",
+              test, result,
+              (int) err_msg_size,
+              (const char *) err_msg);
+  GNUNET_assert (9 == result);
+
+  master_history_replay ();
+}
+
+
+static void
+slave_history_replay_latest ()
+{
+  test = TEST_SLAVE_HISTORY_REPLAY_LATEST;
+  GNUNET_PSYC_channel_history_replay_latest (slv_chn, 1, "",
+                                             GNUNET_PSYC_HISTORY_REPLAY_LOCAL,
+                                             &slave_message_cb,
+                                             &slave_message_part_cb,
+                                             
&slave_history_replay_latest_result,
+                                             NULL);
+}
+
+
+/*** Master history_replay_latest() ***/
+
+
+static void
+master_history_replay_latest_result (void *cls, int64_t result,
+                                     const void *err_msg, uint16_t 
err_msg_size)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Test #%d: master_history_replay_latest:\t%" PRId64 " (%.*s)\n",
+              test, result, err_msg_size, (char *) err_msg);
+  GNUNET_assert (9 == result);
+
+  slave_history_replay_latest ();
+}
+
+
+static void
+master_history_replay_latest ()
+{
+  test = TEST_MASTER_HISTORY_REPLAY_LATEST;
+  GNUNET_PSYC_channel_history_replay_latest (mst_chn, 1, "",
+                                             GNUNET_PSYC_HISTORY_REPLAY_LOCAL,
+                                             &master_message_cb,
+                                             &master_message_part_cb,
+                                             
&master_history_replay_latest_result,
+                                             NULL);
+}
+
+
+static void
+transmit_resume (void *cls)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Test #%d: Transmission resumed.\n", test);
+  struct TransmitClosure *tmit = cls;
+  if (NULL != tmit->mst_tmit)
+    GNUNET_PSYC_master_transmit_resume (tmit->mst_tmit);
+  else
+    GNUNET_PSYC_slave_transmit_resume (tmit->slv_tmit);
+}
+
+
+static int
+tmit_notify_data (void *cls, uint16_t *data_size, void *data)
+{
+  struct TransmitClosure *tmit = cls;
+  if (0 == tmit->data_count)
+  {
+    *data_size = 0;
+    return GNUNET_YES;
+  }
+
+  uint16_t size = strlen (tmit->data[tmit->n]);
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Test #%d: Transmit notify data: %u bytes available, "
+              "processing fragment %u/%u (size %u).\n",
+              test, *data_size, tmit->n + 1, tmit->data_count, size);
+  if (*data_size < size)
+  {
+    *data_size = 0;
+    GNUNET_assert (0);
+    return GNUNET_SYSERR;
+  }
+
+  if (GNUNET_YES != tmit->paused && 0 < tmit->data_delay[tmit->n])
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "Test #%d: Transmission paused.\n", test);
+    tmit->paused = GNUNET_YES;
+    GNUNET_SCHEDULER_add_delayed (
+      GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
+                                     tmit->data_delay[tmit->n]),
+      &transmit_resume, tmit);
+    *data_size = 0;
+    return GNUNET_NO;
+  }
+  tmit->paused = GNUNET_NO;
+
+  *data_size = size;
+  GNUNET_memcpy (data, tmit->data[tmit->n], size);
+
+  return ++tmit->n < tmit->data_count ? GNUNET_NO : GNUNET_YES;
+}
+
+
+static int
+tmit_notify_mod (void *cls, uint16_t *data_size, void *data, uint8_t *oper,
+                 uint32_t *full_value_size)
+{
+  struct TransmitClosure *tmit = cls;
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Test #%d: Transmit notify modifier: %u bytes available, "
+              "%u modifiers left to process.\n",
+              test, *data_size, (unsigned int) GNUNET_PSYC_env_get_count 
(tmit->env));
+
+  uint16_t name_size = 0;
+  size_t value_size = 0;
+  const char *value = NULL;
+
+  if (NULL != oper && NULL != tmit->mod)
+  { /* New modifier */
+    tmit->mod = tmit->mod->next;
+    if (NULL == tmit->mod)
+    { /* No more modifiers, continue with data */
+      *data_size = 0;
+      return GNUNET_YES;
+    }
+
+    GNUNET_assert (tmit->mod->value_size < UINT32_MAX);
+    *full_value_size = tmit->mod->value_size;
+    *oper = tmit->mod->oper;
+    name_size = strlen (tmit->mod->name);
+
+    if (name_size + 1 + tmit->mod->value_size <= *data_size)
+    {
+      *data_size = name_size + 1 + tmit->mod->value_size;
+    }
+    else
+    {
+      tmit->mod_value_size = tmit->mod->value_size;
+      value_size = *data_size - name_size - 1;
+      tmit->mod_value_size -= value_size;
+      tmit->mod_value = tmit->mod->value + value_size;
+    }
+
+    GNUNET_memcpy (data, tmit->mod->name, name_size);
+    ((char *)data)[name_size] = '\0';
+    GNUNET_memcpy ((char *)data + name_size + 1, tmit->mod->value, value_size);
+  }
+  else if (NULL != tmit->mod_value && 0 < tmit->mod_value_size)
+  { /* Modifier continuation */
+    value = tmit->mod_value;
+    if (tmit->mod_value_size <= *data_size)
+    {
+      value_size = tmit->mod_value_size;
+      tmit->mod_value = NULL;
+    }
+    else
+    {
+      value_size = *data_size;
+      tmit->mod_value += value_size;
+    }
+    tmit->mod_value_size -= value_size;
+
+    if (*data_size < value_size)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                  "value larger than buffer: %u < %zu\n",
+                  *data_size, value_size);
+      *data_size = 0;
+      return GNUNET_NO;
+    }
+
+    *data_size = value_size;
+    GNUNET_memcpy (data, value, value_size);
+  }
+
+  return GNUNET_NO;
+}
+
+
+static void
+slave_join ();
+
+
+static void
+slave_transmit ()
+{
+  test = TEST_SLAVE_TRANSMIT;
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Test #%d: Slave sending request to master.\n", test);
+
+  tmit = GNUNET_new (struct TransmitClosure);
+  tmit->env = GNUNET_PSYC_env_create ();
+  GNUNET_PSYC_env_add (tmit->env, GNUNET_PSYC_OP_ASSIGN,
+                              "_abc", "abc def", 7);
+  GNUNET_PSYC_env_add (tmit->env, GNUNET_PSYC_OP_ASSIGN,
+                              "_abc_def", "abc def ghi", 11);
+  tmit->mod = GNUNET_PSYC_env_head (tmit->env);
+  tmit->n = 0;
+  tmit->data[0] = "slave test";
+  tmit->data_count = 1;
+  tmit->slv_tmit
+    = GNUNET_PSYC_slave_transmit (slv, "_request_test", &tmit_notify_mod,
+                                  &tmit_notify_data, tmit,
+                                  GNUNET_PSYC_SLAVE_TRANSMIT_NONE);
+}
+
+
+static void
+slave_remove_cb (void *cls, int64_t result,
+                 const void *err_msg, uint16_t err_msg_size)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Test #%d: slave_remove:\t%" PRId64 " (%.*s)\n",
+              test, result, err_msg_size, (char *) err_msg);
+
+  slave_transmit ();
+}
+
+
+static void
+slave_remove ()
+{
+  test = TEST_SLAVE_REMOVE;
+  struct GNUNET_PSYC_Channel *chn = GNUNET_PSYC_master_get_channel (mst);
+  GNUNET_PSYC_channel_slave_remove (chn, &slave_pub_key, 2,
+                                    &slave_remove_cb, chn);
+}
+
+
+static void
+slave_add_cb (void *cls, int64_t result,
+              const void *err_msg, uint16_t err_msg_size)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Test #%d: slave_add:\t%" PRId64 " (%.*s)\n",
+              test, result, err_msg_size, (char *) err_msg);
+  slave_remove ();
+}
+
+
+static void
+slave_add ()
+{
+  test = TEST_SLAVE_ADD;
+  struct GNUNET_PSYC_Channel *chn = GNUNET_PSYC_master_get_channel (mst);
+  GNUNET_PSYC_channel_slave_add (chn, &slave_pub_key, 2, 2, &slave_add_cb, 
chn);
+}
+
+
+static void
+schedule_second_slave_join (void *cls)
+{
+  slave_join (TEST_SLAVE_JOIN_ACCEPT);
+}
+
+
+static void
+first_slave_parted (void *cls)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "First slave parted.\n");
+  GNUNET_SCHEDULER_add_now (&schedule_second_slave_join, NULL);
+}
+
+
+static void
+schedule_first_slave_part (void *cls)
+{
+  GNUNET_PSYC_slave_part (slv, GNUNET_NO, &first_slave_parted, NULL);
+}
+
+
+static void
+join_decision_cb (void *cls,
+                  const struct GNUNET_PSYC_JoinDecisionMessage *dcsn,
+                  int is_admitted,
+                  const struct GNUNET_PSYC_Message *join_msg)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Test #%d: Slave got join decision: %d\n", test, is_admitted);
+
+  switch (test)
+  {
+  case TEST_SLAVE_JOIN_REJECT:
+    GNUNET_assert (0 == is_admitted);
+    GNUNET_assert (1 == join_req_count);
+    GNUNET_SCHEDULER_add_now (&schedule_first_slave_part, NULL);
+    break;
+
+  case TEST_SLAVE_JOIN_ACCEPT:
+    GNUNET_assert (1 == is_admitted);
+    GNUNET_assert (2 == join_req_count);
+    slave_add ();
+    break;
+
+  default:
+    GNUNET_break (0);
+  }
+}
+
+
+static void
+join_request_cb (void *cls,
+                 const struct GNUNET_PSYC_JoinRequestMessage *req,
+                 const struct GNUNET_CRYPTO_EcdsaPublicKey *slave_key,
+                 const struct GNUNET_PSYC_Message *join_msg,
+                 struct GNUNET_PSYC_JoinHandle *jh)
+{
+  struct GNUNET_HashCode slave_key_hash;
+  GNUNET_CRYPTO_hash (slave_key, sizeof (*slave_key), &slave_key_hash);
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Test #%d: Got join request #%u from %s.\n",
+              test, join_req_count, GNUNET_h2s (&slave_key_hash));
+
+  /* Reject first request */
+  int is_admitted = (0 < join_req_count++) ? GNUNET_YES : GNUNET_NO;
+  GNUNET_PSYC_join_decision (jh, is_admitted, 0, NULL, NULL);
+}
+
+
+static void
+slave_connect_cb (void *cls, int result, uint64_t max_message_id)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Test #%d: Slave connected: %d, max_message_id: %" PRIu64 "\n",
+              test, result, max_message_id);
+  GNUNET_assert (TEST_SLAVE_JOIN_REJECT == test || TEST_SLAVE_JOIN_ACCEPT == 
test);
+  GNUNET_assert (GNUNET_OK == result || GNUNET_NO == result);
+}
+
+
+static void
+slave_join (int t)
+{
+  test = t;
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Test #%d: Joining slave.\n", t);
+
+  struct GNUNET_PeerIdentity origin = this_peer;
+  struct GNUNET_PSYC_Environment *env = GNUNET_PSYC_env_create ();
+  GNUNET_PSYC_env_add (env, GNUNET_PSYC_OP_ASSIGN,
+                              "_foo", "bar baz", 7);
+  GNUNET_PSYC_env_add (env, GNUNET_PSYC_OP_ASSIGN,
+                              "_foo_bar", "foo bar baz", 11);
+  struct GNUNET_PSYC_Message *
+    join_msg = GNUNET_PSYC_message_create ("_request_join", env, "some data", 
9);
+
+  slv = GNUNET_PSYC_slave_join (cfg,
+                                &channel_pub_key,
+                                slave_key,
+                                GNUNET_PSYC_SLAVE_JOIN_NONE,
+                                &origin,
+                                0,
+                                NULL,
+                                &slave_message_cb,
+                                &slave_message_part_cb,
+                                &slave_connect_cb,
+                                &join_decision_cb,
+                                NULL,
+                                join_msg);
+  GNUNET_free (join_msg);
+  slv_chn = GNUNET_PSYC_slave_get_channel (slv);
+  GNUNET_PSYC_env_destroy (env);
+}
+
+
+static void
+master_transmit ()
+{
+  test = TEST_MASTER_TRANSMIT;
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Test #%d: Master sending message to all.\n", test);
+  end_count = 0;
+
+  uint32_t i, j;
+
+  char *name_max = "_test_max";
+  uint8_t name_max_size = sizeof ("_test_max");
+  char *val_max = GNUNET_malloc (GNUNET_PSYC_MODIFIER_MAX_PAYLOAD);
+  for (i = 0; i < GNUNET_PSYC_MODIFIER_MAX_PAYLOAD; i++)
+    val_max[i] = (0 == i % 10000) ? '0' + i / 10000 : '.';
+
+  char *name_cont = "_test_cont";
+  uint8_t name_cont_size = sizeof ("_test_cont");
+  char *val_cont = GNUNET_malloc (GNUNET_PSYC_MODIFIER_MAX_PAYLOAD
+                                  + GNUNET_PSYC_MOD_CONT_MAX_PAYLOAD);
+  for (i = 0; i < GNUNET_PSYC_MODIFIER_MAX_PAYLOAD - name_cont_size; i++)
+    val_cont[i] = (0 == i % 10000) ? '0' + i / 10000 : ':';
+  for (j = 0; j < GNUNET_PSYC_MOD_CONT_MAX_PAYLOAD; j++, i++)
+    val_cont[i] = (0 == j % 10000) ? '0' + j / 10000 : '!';
+
+  tmit = GNUNET_new (struct TransmitClosure);
+  tmit->env = GNUNET_PSYC_env_create ();
+  GNUNET_PSYC_env_add (tmit->env, GNUNET_PSYC_OP_ASSIGN,
+                              "_foo", "bar baz", 7);
+  GNUNET_PSYC_env_add (tmit->env, GNUNET_PSYC_OP_ASSIGN,
+                              name_max, val_max,
+                              GNUNET_PSYC_MODIFIER_MAX_PAYLOAD
+                              - name_max_size);
+  GNUNET_PSYC_env_add (tmit->env, GNUNET_PSYC_OP_ASSIGN,
+                              "_foo_bar", "foo bar baz", 11);
+  GNUNET_PSYC_env_add (tmit->env, GNUNET_PSYC_OP_ASSIGN,
+                              name_cont, val_cont,
+                              GNUNET_PSYC_MODIFIER_MAX_PAYLOAD - name_cont_size
+                              + GNUNET_PSYC_MOD_CONT_MAX_PAYLOAD);
+  tmit->mod = GNUNET_PSYC_env_head (tmit->env);
+  tmit->data[0] = "foo";
+  tmit->data[1] =  GNUNET_malloc (GNUNET_PSYC_DATA_MAX_PAYLOAD + 1);
+  for (i = 0; i < GNUNET_PSYC_DATA_MAX_PAYLOAD; i++)
+    tmit->data[1][i] = (0 == i % 10000) ? '0' + i / 10000 : '_';
+  tmit->data[2] = "foo bar";
+  tmit->data[3] = "foo bar baz";
+  tmit->data_delay[1] = 3;
+  tmit->data_count = 4;
+  tmit->mst_tmit
+    = GNUNET_PSYC_master_transmit (mst, "_notice_test", &tmit_notify_mod,
+                                   &tmit_notify_data, tmit,
+                                   GNUNET_PSYC_MASTER_TRANSMIT_INC_GROUP_GEN);
+}
+
+
+static void
+master_start_cb (void *cls, int result, uint64_t max_message_id)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Test #%d: Master started: %d, max_message_id: %" PRIu64 "\n",
+              test, result, max_message_id);
+  GNUNET_assert (TEST_MASTER_START == test);
+  GNUNET_assert (GNUNET_OK == result || GNUNET_NO == result);
+  slave_join (TEST_SLAVE_JOIN_REJECT);
+}
+
+
+static void
+master_start ()
+{
+  test = TEST_MASTER_START;
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Test #%d: Starting master.\n", test);
+  mst = GNUNET_PSYC_master_start (cfg, channel_key, 
GNUNET_PSYC_CHANNEL_PRIVATE,
+                                  &master_start_cb, &join_request_cb,
+                                  &master_message_cb, &master_message_part_cb,
+                                  NULL);
+  mst_chn = GNUNET_PSYC_master_get_channel (mst);
+}
+
+
+static void
+schedule_master_start (void *cls)
+{
+  master_start ();
+}
+
+
+/**
+ * Main function of the test, run from scheduler.
+ *
+ * @param cls NULL
+ * @param cfg configuration we use (also to connect to PSYC service)
+ * @param peer handle to access more of the peer (not used)
+ */
+static void
+#if DEBUG_TEST_PSYC
+run (void *cls, char *const *args, const char *cfgfile,
+     const struct GNUNET_CONFIGURATION_Handle *c)
+#else
+run (void *cls,
+     const struct GNUNET_CONFIGURATION_Handle *c,
+     struct GNUNET_TESTING_Peer *peer)
+#endif
+{
+  cfg = c;
+  end_badly_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, &end_badly, NULL);
+
+  GNUNET_CRYPTO_get_peer_identity (cfg, &this_peer);
+
+  channel_key = GNUNET_CRYPTO_eddsa_key_create ();
+  slave_key = GNUNET_CRYPTO_ecdsa_key_create ();
+
+  GNUNET_CRYPTO_eddsa_key_get_public (channel_key, &channel_pub_key);
+  GNUNET_CRYPTO_ecdsa_key_get_public (slave_key, &slave_pub_key);
+
+#if DEBUG_TEST_PSYC
+  master_start ();
+#else
+  /* Allow some time for the services to initialize. */
+  GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
+                                &schedule_master_start, NULL);
+#endif
+}
+
+
+int
+main (int argc, char *argv[])
+{
+  res = 1;
+#if DEBUG_TEST_PSYC
+  const struct GNUNET_GETOPT_CommandLineOption opts[] = {
+    GNUNET_GETOPT_OPTION_END
+  };
+  if (GNUNET_OK != GNUNET_PROGRAM_run (argc, argv, "test-psyc",
+                                       "test-psyc [options]",
+                                       opts, &run, NULL))
+    return 1;
+#else
+  if (0 != GNUNET_TESTING_peer_run ("test-psyc", "test_psyc.conf", &run, NULL))
+    return 1;
+#endif
+  return res;
+}
+
+/* end of test_psyc.c */
diff --git a/src/psyc/test_psyc2.c b/src/psyc/test_psyc2.c
new file mode 100644
index 0000000..c6e7237
--- /dev/null
+++ b/src/psyc/test_psyc2.c
@@ -0,0 +1,284 @@
+/*
+ * This file is part of GNUnet
+ * Copyright (C) 2013 GNUnet e.V.
+ *
+ * GNUnet is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * GNUnet 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
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+     SPDX-License-Identifier: AGPL3.0-or-later
+ */
+
+/**
+ * @file psyc/test_psyc2.c
+ * @brief Testbed test for the PSYC API.
+ * @author xrs
+ */
+
+#include "platform.h"
+#include "gnunet_crypto_lib.h"
+#include "gnunet_common.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_testbed_service.h"
+#include "gnunet_psyc_util_lib.h"
+#include "gnunet_psyc_service.h"
+
+#define PEERS_REQUESTED 2
+
+static int result;
+
+static struct GNUNET_SCHEDULER_Task *timeout_tid;
+static struct pctx **pctx;
+
+static struct GNUNET_CRYPTO_EddsaPrivateKey *channel_key;
+static struct GNUNET_CRYPTO_EddsaPublicKey channel_pub_key;
+
+static struct GNUNET_CRYPTO_EcdsaPrivateKey *slave_key;
+static struct GNUNET_CRYPTO_EcdsaPublicKey slave_pub_key;
+
+/**
+ * Task To perform tests
+ */
+static struct GNUNET_SCHEDULER_Task *test_task;
+
+/**
+ * Peer id couter
+ */
+static unsigned int pids;
+
+struct pctx
+{
+  int idx;
+  struct GNUNET_TESTBED_Peer *peer;
+  const struct GNUNET_PeerIdentity *id;
+
+  struct GNUNET_TESTBED_Operation *op; 
+
+  /**
+   * psyc service handle
+   */
+  void *psyc;
+  struct GNUNET_PSYC_Master *mst;
+  struct GNUNET_PSYC_Slave *slv;
+
+  /**
+   * result for test on peer
+   */
+  int test_ok;
+};
+
+static void
+shutdown_task (void *cls)
+{
+  if (NULL != pctx)
+  {
+    if (NULL != pctx[0]->mst)
+      GNUNET_PSYC_master_stop (pctx[0]->mst, GNUNET_NO, NULL, NULL);  
+
+    for (int i=0; i < PEERS_REQUESTED; i++)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Operation done.\n");
+      GNUNET_TESTBED_operation_done (pctx[i]->op);
+      GNUNET_free_non_null (pctx[i]);
+    }
+    GNUNET_free (pctx);
+  }
+
+  if (NULL != timeout_tid)
+    GNUNET_SCHEDULER_cancel (timeout_tid);
+}
+
+static void
+timeout_task (void *cls)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Timeout!\n");
+  result = GNUNET_SYSERR;
+  GNUNET_SCHEDULER_shutdown ();
+}
+
+static void 
+start_test (void *cls)
+{
+}
+
+static void
+pinfo_cb (void *cls,
+          struct GNUNET_TESTBED_Operation *operation,
+          const struct GNUNET_TESTBED_PeerInformation *pinfo,
+          const char *emsg)
+{
+  struct pctx *pc = (struct pctx*) cls;
+
+  pc->id = pinfo->result.id;
+
+  pids++;
+  if (pids < (PEERS_REQUESTED - 1))
+    return;
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got all IDs, starting test\n");
+  test_task = GNUNET_SCHEDULER_add_now (&start_test, NULL);
+}
+
+static void
+mst_start_cb () 
+{
+}
+
+static void 
+join_request_cb ()
+{
+}
+
+static void
+mst_message_cb ()
+{
+}
+
+static void
+mst_message_part_cb ()
+{
+}
+
+static void 
+slv_message_cb ()
+{
+}
+
+static void 
+slv_message_part_cb ()
+{
+}
+
+static void
+slv_connect_cb () 
+{
+}
+
+static void
+join_decision_cb ()
+{
+}
+
+static void *
+psyc_ca (void *cls,
+         const struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+  struct GNUNET_PSYC_Message *join_msg = NULL;
+  struct pctx *pc = (struct pctx *) cls;
+
+  if (0 == pc->idx)
+  {
+    pc->mst = GNUNET_PSYC_master_start (cfg, channel_key, 
+                                        GNUNET_PSYC_CHANNEL_PRIVATE,
+                                        &mst_start_cb, &join_request_cb,
+                                        &mst_message_cb, &mst_message_part_cb,
+                                        NULL);
+    return pc->mst;
+  }
+
+  pc->slv = GNUNET_PSYC_slave_join (cfg, &channel_pub_key, slave_key,
+                                    GNUNET_PSYC_SLAVE_JOIN_NONE,
+                                    &pid, 0, NULL, &slv_message_cb, 
+                                    &slv_message_part_cb,
+                                    &slv_connect_cb, &join_decision_cb, 
+                                    NULL, join_msg);
+  return pc->slv;
+} 
+
+static void
+psyc_da (void *cls,
+         void *op_result)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Disconnected from service.\n");
+} 
+
+static void
+service_connect (void *cls,
+                 struct GNUNET_TESTBED_Operation *op,
+                 void *ca_result,
+                 const char *emsg)
+{
+  struct pctx *pc = (struct pctx *) cls;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Connected to service\n");
+
+  GNUNET_assert (NULL != ca_result);
+
+  // FIXME: we need a simple service handle to connect to the service, then 
+  // get peer information and AFTER that make PSYC ops. Compare to CADET. 
+  pc->psyc = ca_result;
+
+  GNUNET_TESTBED_peer_get_information (pc->peer, 
+                                       GNUNET_TESTBED_PIT_IDENTITY, 
+                                       pinfo_cb, pc);
+}
+
+static void
+testbed_master (void *cls,
+     struct GNUNET_TESTBED_RunHandle *h,
+     unsigned int num_peers,
+     struct GNUNET_TESTBED_Peer **p,
+     unsigned int links_succeeded,
+     unsigned int links_failed)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Connected to testbed_master()\n");
+
+  // Create ctx for peers
+  pctx = GNUNET_new_array (PEERS_REQUESTED, struct pctx*);
+  for (int i = 0; i<PEERS_REQUESTED; i++) 
+  {
+    pctx[i] = GNUNET_new (struct pctx);
+    pctx[i]->idx = i;
+    pctx[i]->peer = p[i];
+    pctx[i]->id = NULL;
+    pctx[i]->mst = NULL;
+    pctx[i]->op = NULL;
+    pctx[i]->test_ok = GNUNET_NO;
+  }
+
+  channel_key = GNUNET_CRYPTO_eddsa_key_create ();
+  slave_key = GNUNET_CRYPTO_ecdsa_key_create ();
+
+  GNUNET_CRYPTO_eddsa_key_get_public (channel_key, &channel_pub_key);
+  GNUNET_CRYPTO_ecdsa_key_get_public (slave_key, &slave_pub_key);
+
+  pctx[0]->op = 
+    GNUNET_TESTBED_service_connect (NULL, p[0], "psyc", service_connect, 
+                                    pctx[0], psyc_ca, psyc_da, pctx[0]);
+
+  GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL); 
+
+  timeout_tid = 
+    GNUNET_SCHEDULER_add_delayed 
(GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 5),
+                                  &timeout_task, NULL);
+}
+
+int
+main (int argc, char *argv[])
+{
+  int ret;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO, "test\n");
+
+  result = GNUNET_SYSERR;
+
+  ret = GNUNET_TESTBED_test_run ("test-psyc2", "test_psyc.conf",
+                                 PEERS_REQUESTED, 0LL, NULL, NULL, 
+                                 testbed_master, NULL);
+
+  if ((GNUNET_OK != ret) || (GNUNET_OK != result))
+    return 1;
+
+  return 0;
+}
+
+/* end of test-psyc2.c */
diff --git a/src/psyc/test_psyc_api_join.c b/src/psyc/test_psyc_api_join.c
new file mode 100644
index 0000000..419fa11
--- /dev/null
+++ b/src/psyc/test_psyc_api_join.c
@@ -0,0 +1,282 @@
+/*
+ * This file is part of GNUnet
+ * Copyright (C) 2013 GNUnet e.V.
+ *
+ * GNUnet is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * GNUnet 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
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+     SPDX-License-Identifier: AGPL3.0-or-later
+ */
+
+/**
+ * @file psyc/test_psyc_api_join.c
+ * @brief Testbed test for the PSYC API.
+ * @author xrs
+ */
+
+/**
+ * Lessons Learned:
+ * - define topology in config
+ * - psyc slave join needs part to end (same with master)
+ * - GNUNET_SCHEDULER_add_delayed return value will outdate at call time
+ * - main can not contain GNUNET_log()
+ */
+
+#include "platform.h"
+#include "gnunet_crypto_lib.h"
+#include "gnunet_common.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_testbed_service.h"
+#include "gnunet_psyc_util_lib.h"
+#include "gnunet_psyc_service.h"
+#include "psyc_test_lib.h"
+
+static struct pctx PEERS[2];
+
+static int pids;
+
+
+static void 
+shutdown_task (void *cls)
+{
+  if (NULL != timeout_task_id) {
+    GNUNET_SCHEDULER_cancel (timeout_task_id);
+    timeout_task_id = NULL;
+  }
+
+  for (int i=0;i<2;i++) {
+    GNUNET_free (PEERS[i].channel_pub_key);
+
+    if (NULL != PEERS[i].psyc)
+    {
+      if (0 == i) 
+        GNUNET_PSYC_master_stop (PEERS[i].psyc, GNUNET_NO, NULL, NULL);
+      else 
+        GNUNET_PSYC_slave_part (PEERS[i].psyc, GNUNET_NO, NULL, NULL);
+    }
+  }
+
+  for (int i=0;i<MAX_TESTBED_OPS;i++)
+    if (NULL != op[i])
+      GNUNET_TESTBED_operation_done (op[i]);
+
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Shut down!\n");
+}
+
+static void
+timeout_task (void *cls)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Timeout!\n");
+
+  timeout_task_id = NULL;
+
+  result = GNUNET_SYSERR;
+  GNUNET_SCHEDULER_shutdown ();
+}
+
+static void
+join_decision_cb (void *cls,
+                  const struct GNUNET_PSYC_JoinDecisionMessage *dcsn,
+                  int is_admitted,
+                  const struct GNUNET_PSYC_Message *join_msg)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "slave: got join decision: %s\n", 
+              (GNUNET_YES == is_admitted) ? "admitted":"rejected");
+
+  result = (GNUNET_YES == is_admitted) ? GNUNET_OK : GNUNET_SYSERR;
+
+  GNUNET_SCHEDULER_shutdown ();
+}
+
+static void
+join_request_cb (void *cls,
+                 const struct GNUNET_PSYC_JoinRequestMessage *req,
+                 const struct GNUNET_CRYPTO_EcdsaPublicKey *slave_key,
+                 const struct GNUNET_PSYC_Message *join_msg,
+                 struct GNUNET_PSYC_JoinHandle *jh)
+{
+  struct GNUNET_HashCode slave_key_hash;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO, "master: got join request.\n");
+
+  GNUNET_CRYPTO_hash (slave_key, sizeof (*slave_key), &slave_key_hash);
+
+  GNUNET_PSYC_join_decision (jh, GNUNET_YES, 0, NULL, NULL);
+}
+
+static void
+psyc_da ()
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO, "disconnect form PSYC service\n");
+}
+
+static void *
+psyc_ca (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg) 
+{
+  struct pctx *peer = (struct pctx*) cls;
+
+  // Case: master role
+  if (0 == peer->idx) {
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Connecting to PSYC as master ...\n");
+
+    peer->psyc = (struct GNUNET_PSYC_Master *) 
+      GNUNET_PSYC_master_start (cfg,
+                                peer->channel_key,
+                                GNUNET_PSYC_CHANNEL_PRIVATE,
+                                NULL,
+                                join_request_cb,
+                                NULL,
+                                NULL,
+                                cls);
+    return peer->psyc;
+  }
+
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Connecting to PSYC as slave ...\n");
+
+  struct GNUNET_PSYC_Environment *env = GNUNET_PSYC_env_create ();
+  GNUNET_PSYC_env_add (env, GNUNET_PSYC_OP_ASSIGN, "_foo", "bar baz", 7);
+  GNUNET_PSYC_env_add (env, GNUNET_PSYC_OP_ASSIGN, "_foo_bar", "foo bar baz", 
11);
+
+  struct GNUNET_PSYC_Message *
+    join_msg = GNUNET_PSYC_message_create ("_request_join", env, "some data", 
40);
+
+  peer->psyc = (struct GNUNET_PSYC_Slave *)
+    GNUNET_PSYC_slave_join (cfg,
+                            peer->channel_pub_key,
+                            peer->id_key,
+                            GNUNET_PSYC_SLAVE_JOIN_NONE,
+                            peer->peer_id_master, 
+                            0,
+                            NULL,
+                            NULL,
+                            NULL,
+                            NULL,
+                            join_decision_cb,
+                            cls,
+                            join_msg);
+
+  GNUNET_free (join_msg);
+  peer->channel = GNUNET_PSYC_slave_get_channel (peer->psyc);
+  GNUNET_PSYC_env_destroy (env);
+
+  return peer->psyc;
+}
+
+static void
+service_connect (void *cls,
+                 struct GNUNET_TESTBED_Operation *op,
+                 void *ca_result,
+                 const char *emsg)
+{
+  GNUNET_assert (NULL != ca_result);
+
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Connected to the service\n");
+}
+
+static void 
+connect_to_services (void *cls)
+{
+  for (int i = 0; i < 2; i++)
+  {
+    PEERS[i].peer_id_master = PEERS[0].peer_id;
+
+    op[op_cnt++] = 
+      GNUNET_TESTBED_service_connect (NULL, PEERS[i].testbed_peer, "psyc", 
+                                      &service_connect, &PEERS[i], &psyc_ca, 
+                                      &psyc_da, &PEERS[i]);
+  }
+}
+
+static void
+pinfo_cb (void *cls,
+          struct GNUNET_TESTBED_Operation *operation,
+          const struct GNUNET_TESTBED_PeerInformation *pinfo,
+          const char *emsg)
+{
+  struct pctx *peer = (struct pctx*) cls;
+
+  peer->peer_id = pinfo->result.id;
+
+  pids++;
+  if (pids < 2)
+    return;
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Got all IDs, starting test\n");
+
+  GNUNET_SCHEDULER_add_now (&connect_to_services, NULL);
+}
+
+static void
+testbed_master (void *cls,
+     struct GNUNET_TESTBED_RunHandle *h,
+     unsigned int num_peers,
+     struct GNUNET_TESTBED_Peer **p,
+     unsigned int links_succeeded,
+     unsigned int links_failed)
+{
+  struct GNUNET_CRYPTO_EddsaPrivateKey *channel_key = NULL;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Connected to testbed_master\n");
+
+  // Set up shutdown logic
+  GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL); 
+  timeout_task_id = 
+    GNUNET_SCHEDULER_add_delayed 
(GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 15),
+                                  &timeout_task, NULL);
+  GNUNET_assert (NULL != timeout_task_id);
+
+  // Set up channel key
+  channel_key = GNUNET_CRYPTO_eddsa_key_create ();
+  GNUNET_assert (NULL != channel_key);
+  
+  // Set up information contexts for peers
+  for (int i=0 ; i < 2 ; i++)
+  {
+    PEERS[i].idx = i;
+    PEERS[i].testbed_peer = p[i];
+
+    // Create "egos"
+    PEERS[i].id_key = GNUNET_CRYPTO_ecdsa_key_create ();
+
+    // Set up channel keys shared by master and slave
+    PEERS[i].channel_key = channel_key; 
+
+    PEERS[i].channel_pub_key = 
+      GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_EddsaPublicKey));
+    // Get public key 
+    GNUNET_CRYPTO_eddsa_key_get_public (PEERS[i].channel_key, 
+                                        PEERS[i].channel_pub_key);
+    // Get peerinfo
+    op[op_cnt++] = 
+      GNUNET_TESTBED_peer_get_information (p[i], 
+                                           GNUNET_TESTBED_PIT_IDENTITY, 
+                                           pinfo_cb, &PEERS[i]);
+  }
+}
+
+int 
+main (int argc, char *argv[])
+{
+  int ret; 
+
+  ret = GNUNET_TESTBED_test_run ("test_psyc_api_join", "test_psyc.conf",
+                                 2, 0LL, NULL, NULL, 
+                                 &testbed_master, NULL);
+
+  if ( (GNUNET_OK != ret) || (GNUNET_OK != result) )
+    return 1;
+
+  return 0;
+}
+
+/* end of test_psyc_api_join.c */
diff --git a/src/psycstore/.gitignore b/src/psycstore/.gitignore
new file mode 100644
index 0000000..5ec7832
--- /dev/null
+++ b/src/psycstore/.gitignore
@@ -0,0 +1,5 @@
+gnunet-service-psycstore
+test_plugin_psycstore_mysql
+test_plugin_psycstore_sqlite
+test_plugin_psycstore_postgres
+test_psycstore
diff --git a/src/psycstore/Makefile.am b/src/psycstore/Makefile.am
new file mode 100644
index 0000000..557bb42
--- /dev/null
+++ b/src/psycstore/Makefile.am
@@ -0,0 +1,155 @@
+# This Makefile.am is in the public domain
+AM_CPPFLAGS = -I$(top_srcdir)/src/include
+
+plugindir = $(libdir)/gnunet
+
+pkgcfgdir= $(pkgdatadir)/config.d/
+
+libexecdir= $(pkglibdir)/libexec/
+
+pkgcfg_DATA = \
+  psycstore.conf
+
+
+if MINGW
+ WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols
+endif
+
+if USE_COVERAGE
+  AM_CFLAGS = --coverage -O0
+  XLIB = -lgcov
+endif
+
+if HAVE_MYSQL
+MYSQL_PLUGIN = libgnunet_plugin_psycstore_mysql.la
+if HAVE_TESTING
+MYSQL_TESTS = test_plugin_psycstore_mysql
+endif
+endif
+
+if HAVE_POSTGRESQL
+POSTGRES_PLUGIN = libgnunet_plugin_psycstore_postgres.la
+if HAVE_TESTING
+POSTGRES_TESTS = test_plugin_psycstore_postgres
+endif
+endif
+
+if HAVE_SQLITE
+SQLITE_PLUGIN = libgnunet_plugin_psycstore_sqlite.la
+if HAVE_TESTING
+SQLITE_TESTS = test_plugin_psycstore_sqlite
+endif
+endif
+
+lib_LTLIBRARIES = libgnunetpsycstore.la
+
+libgnunetpsycstore_la_SOURCES = \
+  psycstore_api.c \
+  psycstore.h
+libgnunetpsycstore_la_LIBADD = \
+  $(top_builddir)/src/util/libgnunetutil.la \
+  $(GN_LIBINTL) $(XLIB)
+libgnunetpsycstore_la_LDFLAGS = \
+  $(GN_LIB_LDFLAGS)  $(WINFLAGS) \
+  -version-info 0:0:0
+
+bin_PROGRAMS =
+
+libexec_PROGRAMS = \
+ gnunet-service-psycstore
+
+gnunet_service_psycstore_SOURCES = \
+ gnunet-service-psycstore.c
+gnunet_service_psycstore_LDADD = \
+  $(top_builddir)/src/statistics/libgnunetstatistics.la \
+  $(top_builddir)/src/util/libgnunetutil.la \
+  $(top_builddir)/src/psycutil/libgnunetpsycutil.la \
+  $(GN_LIBINTL)
+
+plugin_LTLIBRARIES = \
+  $(SQLITE_PLUGIN) \
+  $(MYSQL_PLUGIN) \
+  $(POSTGRES_PLUGIN)
+
+
+libgnunet_plugin_psycstore_mysql_la_SOURCES = \
+  plugin_psycstore_mysql.c
+libgnunet_plugin_psycstore_mysql_la_LIBADD = \
+  libgnunetpsycstore.la  \
+  $(top_builddir)/src/my/libgnunetmy.la \
+  $(top_builddir)/src/mysql/libgnunetmysql.la \
+  $(top_builddir)/src/statistics/libgnunetstatistics.la \
+  $(top_builddir)/src/util/libgnunetutil.la $(XLIBS) \
+  $(LTLIBINTL)
+libgnunet_plugin_psycstore_mysql_la_LDFLAGS = \
+ $(GN_PLUGIN_LDFLAGS)
+
+libgnunet_plugin_psycstore_postgres_la_SOURCES = \
+  plugin_psycstore_postgres.c
+libgnunet_plugin_psycstore_postgres_la_LIBADD = \
+  libgnunetpsycstore.la  \
+  $(top_builddir)/src/pq/libgnunetpq.la  \
+  $(top_builddir)/src/statistics/libgnunetstatistics.la \
+  $(top_builddir)/src/util/libgnunetutil.la $(XLIBS) -lpq \
+  $(LTLIBINTL)
+libgnunet_plugin_psycstore_postgres_la_LDFLAGS = \
+  $(GN_PLUGIN_LDFLAGS) $(POSTGRESQL_LDFLAGS)
+libgnunet_plugin_psycstore_postgres_la_CPPFLAGS = \
+  $(POSTGRESQL_CPPFLAGS) $(AM_CPPFLAGS)
+
+
+libgnunet_plugin_psycstore_sqlite_la_SOURCES = \
+  plugin_psycstore_sqlite.c
+libgnunet_plugin_psycstore_sqlite_la_LIBADD = \
+  libgnunetpsycstore.la  \
+  $(top_builddir)/src/statistics/libgnunetstatistics.la \
+  $(top_builddir)/src/util/libgnunetutil.la $(XLIBS) -lsqlite3 \
+  $(LTLIBINTL)
+libgnunet_plugin_psycstore_sqlite_la_LDFLAGS = \
+ $(GN_PLUGIN_LDFLAGS)
+
+
+if HAVE_SQLITE
+if HAVE_TESTING
+check_PROGRAMS = \
+ $(SQLITE_TESTS) \
+ $(MYSQL_TESTS) \
+ $(POSTGRES_TESTS) \
+ test_psycstore
+endif
+endif
+
+if ENABLE_TEST_RUN
+AM_TESTS_ENVIRONMENT=export 
GNUNET_PREFIX=$${GNUNET_PREFIX:address@hidden@};export 
PATH=$${GNUNET_PREFIX:address@hidden@}/bin:$$PATH;unset XDG_DATA_HOME;unset 
XDG_CONFIG_HOME;
+TESTS = $(check_PROGRAMS)
+endif
+
+test_psycstore_SOURCES = \
+ test_psycstore.c
+test_psycstore_LDADD = \
+  libgnunetpsycstore.la \
+  $(top_builddir)/src/testing/libgnunettesting.la \
+  $(top_builddir)/src/util/libgnunetutil.la
+
+EXTRA_DIST = \
+  test_psycstore.conf
+
+
+test_plugin_psycstore_sqlite_SOURCES = \
+ test_plugin_psycstore.c
+test_plugin_psycstore_sqlite_LDADD = \
+  $(top_builddir)/src/testing/libgnunettesting.la \
+  $(top_builddir)/src/util/libgnunetutil.la
+
+test_plugin_psycstore_mysql_SOURCES = \
+ test_plugin_psycstore.c
+test_plugin_psycstore_mysql_LDADD = \
+  $(top_builddir)/src/testing/libgnunettesting.la \
+  $(top_builddir)/src/util/libgnunetutil.la
+
+test_plugin_psycstore_postgres_SOURCES = \
+ test_plugin_psycstore.c
+test_plugin_psycstore_postgres_LDADD = \
+ $(top_builddir)/src/testing/libgnunettesting.la \
+ $(top_builddir)/src/util/libgnunetutil.la
+
diff --git a/src/psycstore/gnunet-service-psycstore.c 
b/src/psycstore/gnunet-service-psycstore.c
new file mode 100644
index 0000000..9aebd3e
--- /dev/null
+++ b/src/psycstore/gnunet-service-psycstore.c
@@ -0,0 +1,1049 @@
+/**
+ * This file is part of GNUnet
+ * Copyright (C) 2013 GNUnet e.V.
+ *
+ * GNUnet is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * GNUnet 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
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+     SPDX-License-Identifier: AGPL3.0-or-later
+ */
+
+/**
+ * @file psycstore/gnunet-service-psycstore.c
+ * @brief PSYCstore service
+ * @author Gabor X Toth
+ * @author Christian Grothoff
+ */
+
+#include <inttypes.h>
+
+#include "platform.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_constants.h"
+#include "gnunet_protocols.h"
+#include "gnunet_statistics_service.h"
+#include "gnunet_psyc_util_lib.h"
+#include "gnunet_psycstore_service.h"
+#include "gnunet_psycstore_plugin.h"
+#include "psycstore.h"
+
+
+/**
+ * Handle to our current configuration.
+ */
+static const struct GNUNET_CONFIGURATION_Handle *cfg;
+
+/**
+ * Service handle.
+ */
+static struct GNUNET_SERVICE_Handle *service;
+
+/**
+ * Handle to the statistics service.
+ */
+static struct GNUNET_STATISTICS_Handle *stats;
+
+/**
+ * Database handle
+ */
+static struct GNUNET_PSYCSTORE_PluginFunctions *db;
+
+/**
+ * Name of the database plugin
+ */
+static char *db_lib_name;
+
+
+/**
+ * Task run during shutdown.
+ *
+ * @param cls unused
+ */
+static void
+shutdown_task (void *cls)
+{
+  if (NULL != stats)
+  {
+    GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
+    stats = NULL;
+  }
+  GNUNET_break (NULL == GNUNET_PLUGIN_unload (db_lib_name, db));
+  GNUNET_free (db_lib_name);
+  db_lib_name = NULL;
+}
+
+
+/**
+ * Send a result code back to the client.
+ *
+ * @param client
+ *        Client that should receive the result code.
+ * @param result_code
+ *        Code to transmit.
+ * @param op_id
+ *        Operation ID in network byte order.
+ * @param err_msg
+ *        Error message to include (or NULL for none).
+ */
+static void
+send_result_code (struct GNUNET_SERVICE_Client *client,
+                  uint64_t op_id,
+                  int64_t result_code,
+                  const char *err_msg)
+{
+  struct OperationResult *res;
+  size_t err_size = 0;
+
+  if (NULL != err_msg)
+    err_size = strnlen (err_msg,
+                        GNUNET_MAX_MESSAGE_SIZE - sizeof (*res) - 1) + 1;
+  struct GNUNET_MQ_Envelope *
+    env = GNUNET_MQ_msg_extra (res, err_size,
+                               GNUNET_MESSAGE_TYPE_PSYCSTORE_RESULT_CODE);
+  res->result_code = GNUNET_htonll (result_code - INT64_MIN);
+  res->op_id = op_id;
+  if (0 < err_size)
+  {
+    GNUNET_memcpy (&res[1], err_msg, err_size);
+    ((char *) &res[1])[err_size - 1] = '\0';
+  }
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+             "Sending result to client: %" PRId64 " (%s)\n",
+             result_code, err_msg);
+  GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client), env);
+}
+
+
+enum
+{
+  MEMBERSHIP_TEST_NOT_NEEDED = 0,
+  MEMBERSHIP_TEST_NEEDED = 1,
+  MEMBERSHIP_TEST_DONE = 2,
+} MessageMembershipTest;
+
+
+struct SendClosure
+{
+  struct GNUNET_SERVICE_Client *client;
+
+  /**
+   * Channel's public key.
+   */
+  struct GNUNET_CRYPTO_EddsaPublicKey channel_key;
+
+  /**
+   * Slave's public key.
+   */
+  struct GNUNET_CRYPTO_EcdsaPublicKey slave_key;
+
+  /**
+   * Operation ID.
+   */
+  uint64_t op_id;
+
+  /**
+   * Membership test result.
+   */
+  int membership_test_result;
+
+  /**
+   * Do membership test with @a slave_key before returning fragment?
+   * @see enum MessageMembershipTest
+   */
+  uint8_t membership_test;
+};
+
+
+static int
+send_fragment (void *cls, struct GNUNET_MULTICAST_MessageHeader *msg,
+               enum GNUNET_PSYCSTORE_MessageFlags flags)
+{
+  struct SendClosure *sc = cls;
+  struct FragmentResult *res;
+
+  if (MEMBERSHIP_TEST_NEEDED == sc->membership_test)
+  {
+    sc->membership_test = MEMBERSHIP_TEST_DONE;
+    sc->membership_test_result
+      = db->membership_test (db->cls, &sc->channel_key, &sc->slave_key,
+                             GNUNET_ntohll (msg->message_id));
+    switch (sc->membership_test_result)
+    {
+    case GNUNET_YES:
+      break;
+
+    case GNUNET_NO:
+    case GNUNET_SYSERR:
+      return GNUNET_NO;
+    }
+  }
+
+  size_t msg_size = ntohs (msg->header.size);
+
+  struct GNUNET_MQ_Envelope *
+    env = GNUNET_MQ_msg_extra (res, msg_size,
+                               GNUNET_MESSAGE_TYPE_PSYCSTORE_RESULT_FRAGMENT);
+  res->op_id = sc->op_id;
+  res->psycstore_flags = htonl (flags);
+  GNUNET_memcpy (&res[1], msg, msg_size);
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+             "Sending fragment %llu to client\n",
+             (unsigned long long) GNUNET_ntohll (msg->fragment_id));
+  GNUNET_free (msg);
+
+  GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (sc->client), env);
+  return GNUNET_YES;
+}
+
+
+static int
+send_state_var (void *cls, const char *name,
+                const void *value, uint32_t value_size)
+{
+  struct SendClosure *sc = cls;
+  struct StateResult *res;
+  size_t name_size = strlen (name) + 1;
+
+  /** @todo FIXME: split up value into 64k chunks */
+
+  struct GNUNET_MQ_Envelope *
+    env = GNUNET_MQ_msg_extra (res, name_size + value_size,
+                               GNUNET_MESSAGE_TYPE_PSYCSTORE_RESULT_STATE);
+  res->op_id = sc->op_id;
+  res->name_size = htons (name_size);
+  GNUNET_memcpy (&res[1], name, name_size);
+  GNUNET_memcpy ((char *) &res[1] + name_size, value, value_size);
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Sending state variable %s to client\n", name);
+
+  GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (sc->client), env);
+  return GNUNET_OK;
+}
+
+
+static void
+handle_client_membership_store (void *cls,
+                                const struct MembershipStoreRequest *req)
+{
+  struct GNUNET_SERVICE_Client *client = cls;
+
+  int ret = db->membership_store (db->cls, &req->channel_key, &req->slave_key,
+                                  req->did_join,
+                                  GNUNET_ntohll (req->announced_at),
+                                  GNUNET_ntohll (req->effective_since),
+                                  GNUNET_ntohll (req->group_generation));
+
+  if (ret != GNUNET_OK)
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                _("Failed to store membership information!\n"));
+
+  send_result_code (client, req->op_id, ret, NULL);
+  GNUNET_SERVICE_client_continue (client);
+}
+
+
+static void
+handle_client_membership_test (void *cls,
+                               const struct MembershipTestRequest *req)
+{
+  struct GNUNET_SERVICE_Client *client = cls;
+
+  int ret = db->membership_test (db->cls, &req->channel_key, &req->slave_key,
+                                 GNUNET_ntohll (req->message_id));
+  switch (ret)
+  {
+  case GNUNET_YES:
+  case GNUNET_NO:
+    break;
+  default:
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                _("Failed to test membership!\n"));
+  }
+
+  send_result_code (client, req->op_id, ret, NULL);
+  GNUNET_SERVICE_client_continue (client);
+}
+
+
+static int
+check_client_fragment_store (void *cls,
+                             const struct FragmentStoreRequest *req)
+{
+  return GNUNET_OK;
+}
+
+
+static void
+handle_client_fragment_store (void *cls,
+                              const struct FragmentStoreRequest *req)
+{
+  struct GNUNET_SERVICE_Client *client = cls;
+
+  const struct GNUNET_MessageHeader *
+    msg = GNUNET_MQ_extract_nested_mh (req);
+  if (NULL == msg
+      || ntohs (msg->size) < sizeof (struct GNUNET_MULTICAST_MessageHeader))
+  {
+    GNUNET_break (0);
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                _("Dropping invalid fragment\n"));
+    GNUNET_SERVICE_client_drop (client);
+    return;
+  }
+
+  int ret = db->fragment_store (db->cls, &req->channel_key,
+                                (const struct GNUNET_MULTICAST_MessageHeader *)
+                                msg, ntohl (req->psycstore_flags));
+
+  if (ret != GNUNET_OK)
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                _("Failed to store fragment\n"));
+
+  send_result_code (client, req->op_id, ret, NULL);
+  GNUNET_SERVICE_client_continue (client);
+}
+
+
+static void
+handle_client_fragment_get (void *cls,
+                            const struct FragmentGetRequest *req)
+{
+  struct GNUNET_SERVICE_Client *client = cls;
+
+  struct SendClosure
+    sc = { .op_id = req->op_id,
+           .client = client,
+           .channel_key = req->channel_key,
+           .slave_key = req->slave_key,
+           .membership_test = req->do_membership_test };
+
+  int64_t ret;
+  uint64_t ret_frags = 0;
+  uint64_t first_fragment_id = GNUNET_ntohll (req->first_fragment_id);
+  uint64_t last_fragment_id = GNUNET_ntohll (req->last_fragment_id);
+  uint64_t limit = GNUNET_ntohll (req->fragment_limit);
+
+  if (0 == limit)
+    ret = db->fragment_get (db->cls, &req->channel_key,
+                            first_fragment_id, last_fragment_id,
+                            &ret_frags, send_fragment, &sc);
+  else
+    ret = db->fragment_get_latest (db->cls, &req->channel_key, limit,
+                                   &ret_frags, send_fragment, &sc);
+
+  switch (ret)
+  {
+  case GNUNET_YES:
+  case GNUNET_NO:
+    if (MEMBERSHIP_TEST_DONE == sc.membership_test)
+    {
+      switch (sc.membership_test_result)
+      {
+      case GNUNET_YES:
+        break;
+
+      case GNUNET_NO:
+        ret = GNUNET_PSYCSTORE_MEMBERSHIP_TEST_FAILED;
+        break;
+
+      case GNUNET_SYSERR:
+        ret = GNUNET_SYSERR;
+        break;
+      }
+    }
+    break;
+  default:
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                _("Failed to get fragment!\n"));
+  }
+  send_result_code (client, req->op_id, (ret < 0) ? ret : ret_frags, NULL);
+  GNUNET_SERVICE_client_continue (client);
+}
+
+
+static int
+check_client_message_get (void *cls,
+                          const struct MessageGetRequest *req)
+{
+  return GNUNET_OK;
+}
+
+
+static void
+handle_client_message_get (void *cls,
+                           const struct MessageGetRequest *req)
+{
+  struct GNUNET_SERVICE_Client *client = cls;
+
+  uint16_t size = ntohs (req->header.size);
+  const char *method_prefix = (const char *) &req[1];
+
+  if (size < sizeof (*req) + 1
+      || '\0' != method_prefix[size - sizeof (*req) - 1])
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Message get: invalid method prefix. size: %u < %u?\n",
+                size,
+                (unsigned int) (sizeof (*req) + 1));
+    GNUNET_break (0);
+    GNUNET_SERVICE_client_drop (client);
+    return;
+  }
+
+  struct SendClosure
+    sc = { .op_id = req->op_id,
+           .client = client,
+           .channel_key = req->channel_key,
+           .slave_key = req->slave_key,
+           .membership_test = req->do_membership_test };
+
+  int64_t ret;
+  uint64_t ret_frags = 0;
+  uint64_t first_message_id = GNUNET_ntohll (req->first_message_id);
+  uint64_t last_message_id = GNUNET_ntohll (req->last_message_id);
+  uint64_t msg_limit = GNUNET_ntohll (req->message_limit);
+  uint64_t frag_limit = GNUNET_ntohll (req->fragment_limit);
+
+  /** @todo method_prefix */
+  if (0 == msg_limit)
+    ret = db->message_get (db->cls, &req->channel_key,
+                           first_message_id, last_message_id, frag_limit,
+                           &ret_frags, send_fragment, &sc);
+  else
+    ret = db->message_get_latest (db->cls, &req->channel_key, msg_limit,
+                                  &ret_frags, send_fragment, &sc);
+
+  switch (ret)
+  {
+  case GNUNET_YES:
+  case GNUNET_NO:
+    break;
+  default:
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                _("Failed to get message!\n"));
+  }
+
+  send_result_code (client, req->op_id, (ret < 0) ? ret : ret_frags, NULL);
+  GNUNET_SERVICE_client_continue (client);
+}
+
+
+static void
+handle_client_message_get_fragment (void *cls,
+                                    const struct MessageGetFragmentRequest 
*req)
+{
+  struct GNUNET_SERVICE_Client *client = cls;
+
+  struct SendClosure
+    sc = { .op_id = req->op_id, .client = client,
+           .channel_key = req->channel_key, .slave_key = req->slave_key,
+           .membership_test = req->do_membership_test };
+
+  int ret = db->message_get_fragment (db->cls, &req->channel_key,
+                                      GNUNET_ntohll (req->message_id),
+                                      GNUNET_ntohll (req->fragment_offset),
+                                      &send_fragment, &sc);
+  switch (ret)
+  {
+  case GNUNET_YES:
+  case GNUNET_NO:
+    break;
+  default:
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                _("Failed to get message fragment!\n"));
+  }
+
+  send_result_code (client, req->op_id, ret, NULL);
+  GNUNET_SERVICE_client_continue (client);
+}
+
+
+static void
+handle_client_counters_get (void *cls,
+                            const struct OperationRequest *req)
+{
+  struct GNUNET_SERVICE_Client *client = cls;
+
+  struct CountersResult *res;
+  struct GNUNET_MQ_Envelope *
+    env = GNUNET_MQ_msg (res, GNUNET_MESSAGE_TYPE_PSYCSTORE_RESULT_COUNTERS);
+
+  int ret = db->counters_message_get (db->cls, &req->channel_key,
+                                      &res->max_fragment_id, 
&res->max_message_id,
+                                      &res->max_group_generation);
+  switch (ret)
+  {
+  case GNUNET_OK:
+    ret = db->counters_state_get (db->cls, &req->channel_key,
+                                  &res->max_state_message_id);
+  case GNUNET_NO:
+    break;
+  default:
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                _("Failed to get master counters!\n"));
+  }
+
+  res->result_code = htonl (ret);
+  res->op_id = req->op_id;
+  res->max_fragment_id = GNUNET_htonll (res->max_fragment_id);
+  res->max_message_id = GNUNET_htonll (res->max_message_id);
+  res->max_group_generation = GNUNET_htonll (res->max_group_generation);
+  res->max_state_message_id = GNUNET_htonll (res->max_state_message_id);
+
+  GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client), env);
+  GNUNET_SERVICE_client_continue (client);
+}
+
+
+struct StateModifyClosure
+{
+  const struct GNUNET_CRYPTO_EddsaPublicKey channel_key;
+  struct GNUNET_PSYC_ReceiveHandle *recv;
+  enum GNUNET_PSYC_MessageState msg_state;
+  char mod_oper;
+  char *mod_name;
+  char *mod_value;
+  uint32_t mod_value_size;
+  uint32_t mod_value_remaining;
+};
+
+
+static void
+recv_state_message_part (void *cls,
+                         const struct GNUNET_PSYC_MessageHeader *msg,
+                         const struct GNUNET_MessageHeader *pmsg)
+{
+  struct StateModifyClosure *scls = cls;
+  uint16_t psize;
+
+  if (NULL == msg)
+  { // FIXME: error on unknown message
+    return;
+  }
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "recv_state_message_part()  message_id: %" PRIu64
+              ", fragment_offset: %" PRIu64 ", flags: %u\n",
+              GNUNET_ntohll (msg->message_id),
+              GNUNET_ntohll (msg->fragment_offset),
+              ntohl (msg->flags));
+
+  if (NULL == pmsg)
+  {
+    scls->msg_state = GNUNET_PSYC_MESSAGE_STATE_ERROR;
+    return;
+  }
+
+  switch (ntohs (pmsg->type))
+  {
+  case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_METHOD:
+  {
+    scls->msg_state = GNUNET_PSYC_MESSAGE_STATE_METHOD;
+    break;
+  }
+
+  case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MODIFIER:
+  {
+    struct GNUNET_PSYC_MessageModifier *
+      pmod = (struct GNUNET_PSYC_MessageModifier *) pmsg;
+    psize = ntohs (pmod->header.size);
+    uint16_t name_size = ntohs (pmod->name_size);
+    uint32_t value_size = ntohl (pmod->value_size);
+
+    const char *name = (const char *) &pmod[1];
+    const void *value = name + name_size;
+
+    if (GNUNET_PSYC_OP_SET != pmod->oper)
+    { // Apply non-transient operation.
+      if (psize == sizeof (*pmod) + name_size + value_size)
+      {
+        db->state_modify_op (db->cls, &scls->channel_key,
+                             pmod->oper, name, value, value_size);
+      }
+      else
+      {
+        scls->mod_oper = pmod->oper;
+        scls->mod_name = GNUNET_malloc (name_size);
+        GNUNET_memcpy (scls->mod_name, name, name_size);
+
+        scls->mod_value_size = value_size;
+        scls->mod_value = GNUNET_malloc (scls->mod_value_size);
+        scls->mod_value_remaining
+          = scls->mod_value_size - (psize - sizeof (*pmod) - name_size);
+        GNUNET_memcpy (scls->mod_value, value, value_size - 
scls->mod_value_remaining);
+      }
+    }
+    scls->msg_state = GNUNET_PSYC_MESSAGE_STATE_MODIFIER;
+    break;
+  }
+
+  case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MOD_CONT:
+    if (GNUNET_PSYC_OP_SET != scls->mod_oper)
+    {
+      if (scls->mod_value_remaining == 0)
+      {
+        GNUNET_break_op (0);
+        scls->msg_state = GNUNET_PSYC_MESSAGE_STATE_ERROR;
+      }
+      psize = ntohs (pmsg->size);
+      GNUNET_memcpy (scls->mod_value + (scls->mod_value_size - 
scls->mod_value_remaining),
+              &pmsg[1], psize - sizeof (*pmsg));
+      scls->mod_value_remaining -= psize - sizeof (*pmsg);
+      if (0 == scls->mod_value_remaining)
+      {
+        db->state_modify_op (db->cls, &scls->channel_key,
+                             scls->mod_oper, scls->mod_name,
+                             scls->mod_value, scls->mod_value_size);
+        GNUNET_free (scls->mod_name);
+        GNUNET_free (scls->mod_value);
+        scls->mod_oper = 0;
+        scls->mod_name = NULL;
+        scls->mod_value = NULL;
+        scls->mod_value_size = 0;
+      }
+    }
+    scls->msg_state = GNUNET_PSYC_MESSAGE_STATE_MOD_CONT;
+    break;
+
+  case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_DATA:
+    scls->msg_state = GNUNET_PSYC_MESSAGE_STATE_DATA;
+    break;
+
+  case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END:
+    scls->msg_state = GNUNET_PSYC_MESSAGE_STATE_END;
+    break;
+
+  default:
+    scls->msg_state = GNUNET_PSYC_MESSAGE_STATE_ERROR;
+  }
+}
+
+
+static int
+recv_state_fragment (void *cls, struct GNUNET_MULTICAST_MessageHeader *msg,
+                     enum GNUNET_PSYCSTORE_MessageFlags flags)
+{
+  struct StateModifyClosure *scls = cls;
+
+  if (NULL == scls->recv)
+  {
+    scls->recv = GNUNET_PSYC_receive_create (NULL, recv_state_message_part,
+                                             scls);
+  }
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "recv_state_fragment: %" PRIu64 "\n", GNUNET_ntohll 
(msg->fragment_id));
+
+  struct GNUNET_PSYC_MessageHeader *
+    pmsg = GNUNET_PSYC_message_header_create (msg, flags);
+  GNUNET_PSYC_receive_message (scls->recv, pmsg);
+  GNUNET_free (pmsg);
+
+  return GNUNET_YES;
+}
+
+
+static void
+handle_client_state_modify (void *cls,
+                            const struct StateModifyRequest *req)
+{
+  struct GNUNET_SERVICE_Client *client = cls;
+
+  uint64_t message_id = GNUNET_ntohll (req->message_id);
+  uint64_t state_delta = GNUNET_ntohll (req->state_delta);
+  uint64_t ret_frags = 0;
+  struct StateModifyClosure
+    scls = { .channel_key = req->channel_key };
+
+  int ret = db->state_modify_begin (db->cls, &req->channel_key,
+                                    message_id, state_delta);
+
+  if (GNUNET_OK != ret)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                _("Failed to begin modifying state: %d\n"), ret);
+  }
+  else
+  {
+    ret = db->message_get (db->cls, &req->channel_key,
+                           message_id, message_id, 0,
+                           &ret_frags, recv_state_fragment, &scls);
+    if (GNUNET_OK != ret)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  _("Failed to modify state: %d\n"), ret);
+      GNUNET_break (0);
+    }
+    else
+    {
+      if (GNUNET_OK != db->state_modify_end (db->cls, &req->channel_key, 
message_id))
+      {
+        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                    _("Failed to end modifying state!\n"));
+        GNUNET_break (0);
+      }
+    }
+    if (NULL != scls.recv)
+    {
+      GNUNET_PSYC_receive_destroy (scls.recv);
+    }
+  }
+
+  send_result_code (client, req->op_id, ret, NULL);
+  GNUNET_SERVICE_client_continue (client);
+}
+
+
+static int
+check_client_state_sync (void *cls,
+                         const struct StateSyncRequest *req)
+{
+  return GNUNET_OK;
+}
+
+
+/** @todo FIXME: stop processing further state sync messages after an error */
+static void
+handle_client_state_sync (void *cls,
+                          const struct StateSyncRequest *req)
+{
+  struct GNUNET_SERVICE_Client *client = cls;
+
+  int ret = GNUNET_SYSERR;
+  const char *name = (const char *) &req[1];
+  uint16_t name_size = ntohs (req->name_size);
+
+  if (name_size <= 2 || '\0' != name[name_size - 1])
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                _("Tried to set invalid state variable name!\n"));
+    GNUNET_break_op (0);
+  }
+  else
+  {
+    ret = GNUNET_OK;
+
+    if (req->flags & STATE_OP_FIRST)
+    {
+      ret = db->state_sync_begin (db->cls, &req->channel_key);
+    }
+    if (ret != GNUNET_OK)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  _("Failed to begin synchronizing state!\n"));
+    }
+    else
+    {
+      ret = db->state_sync_assign (db->cls, &req->channel_key, name,
+                                   name + ntohs (req->name_size),
+                                   ntohs (req->header.size) - sizeof (*req)
+                                   - ntohs (req->name_size));
+    }
+
+    if (GNUNET_OK == ret && req->flags & STATE_OP_LAST)
+    {
+      ret = db->state_sync_end (db->cls, &req->channel_key,
+                                GNUNET_ntohll (req->max_state_message_id),
+                                GNUNET_ntohll (req->state_hash_message_id));
+      if (ret != GNUNET_OK)
+        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                    _("Failed to end synchronizing state!\n"));
+    }
+  }
+  send_result_code (client, req->op_id, ret, NULL);
+  GNUNET_SERVICE_client_continue (client);
+}
+
+
+static void
+handle_client_state_reset (void *cls,
+                           const struct OperationRequest *req)
+{
+  struct GNUNET_SERVICE_Client *client = cls;
+
+  int ret = db->state_reset (db->cls, &req->channel_key);
+
+  if (ret != GNUNET_OK)
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                _("Failed to reset state!\n"));
+
+  send_result_code (client, req->op_id, ret, NULL);
+  GNUNET_SERVICE_client_continue (client);
+}
+
+
+static void
+handle_client_state_hash_update (void *cls,
+                                 const struct StateHashUpdateRequest *req)
+{
+  struct GNUNET_SERVICE_Client *client = cls;
+
+  int ret = db->state_reset (db->cls, &req->channel_key);
+  if (ret != GNUNET_OK)
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                _("Failed to reset state!\n"));
+
+  send_result_code (client, req->op_id, ret, NULL);
+  GNUNET_SERVICE_client_continue (client);
+}
+
+
+static int
+check_client_state_get (void *cls,
+                        const struct OperationRequest *req)
+{
+  return GNUNET_OK;
+}
+
+
+static void
+handle_client_state_get (void *cls,
+                         const struct OperationRequest *req)
+{
+  struct GNUNET_SERVICE_Client *client = cls;
+
+  struct SendClosure sc = { .op_id = req->op_id, .client = client };
+  int64_t ret = GNUNET_SYSERR;
+  const char *name = (const char *) &req[1];
+  uint16_t name_size = ntohs (req->header.size) - sizeof (*req);
+
+  if (name_size <= 2 || '\0' != name[name_size - 1])
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                _("Tried to get invalid state variable name!\n"));
+    GNUNET_break (0);
+  }
+  else
+  {
+    ret = db->state_get (db->cls, &req->channel_key, name,
+                         &send_state_var, &sc);
+    if (GNUNET_NO == ret && name_size >= 5) /* min: _a_b\0 */
+    {
+      char *p, *n = GNUNET_malloc (name_size);
+      GNUNET_memcpy (n, name, name_size);
+      while (&n[1] < (p = strrchr (n, '_')) && GNUNET_NO == ret)
+      {
+        *p = '\0';
+        ret = db->state_get (db->cls, &req->channel_key, n,
+                             &send_state_var, &sc);
+      }
+      GNUNET_free (n);
+    }
+  }
+  switch (ret)
+  {
+  case GNUNET_OK:
+  case GNUNET_NO:
+    break;
+  default:
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                _("Failed to get state variable!\n"));
+  }
+
+  send_result_code (client, req->op_id, ret, NULL);
+  GNUNET_SERVICE_client_continue (client);
+}
+
+
+static int
+check_client_state_get_prefix (void *cls,
+                               const struct OperationRequest *req)
+{
+  return GNUNET_OK;
+}
+
+
+static void
+handle_client_state_get_prefix (void *cls,
+                                const struct OperationRequest *req)
+{
+  struct GNUNET_SERVICE_Client *client = cls;
+
+  struct SendClosure sc = { .op_id = req->op_id, .client = client };
+  int64_t ret = GNUNET_SYSERR;
+  const char *name = (const char *) &req[1];
+  uint16_t name_size = ntohs (req->header.size) - sizeof (*req);
+
+  if (name_size <= 1 || '\0' != name[name_size - 1])
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                _("Tried to get invalid state variable name!\n"));
+    GNUNET_break (0);
+  }
+  else
+  {
+    ret = db->state_get_prefix (db->cls, &req->channel_key, name,
+                                &send_state_var, &sc);
+  }
+  switch (ret)
+  {
+  case GNUNET_OK:
+  case GNUNET_NO:
+    break;
+  default:
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                _("Failed to get state variable!\n"));
+  }
+
+  send_result_code (client, req->op_id, ret, NULL);
+  GNUNET_SERVICE_client_continue (client);
+}
+
+
+/**
+ * A new client connected.
+ *
+ * @param cls NULL
+ * @param client client to add
+ * @param mq message queue for @a client
+ * @return @a client
+ */
+static void *
+client_notify_connect (void *cls,
+                       struct GNUNET_SERVICE_Client *client,
+                       struct GNUNET_MQ_Handle *mq)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Client connected: %p\n", client);
+
+  return client;
+}
+
+
+/**
+ * Called whenever a client is disconnected.
+ * Frees our resources associated with that client.
+ *
+ * @param cls closure
+ * @param client identification of the client
+ * @param app_ctx must match @a client
+ */
+static void
+client_notify_disconnect (void *cls,
+                          struct GNUNET_SERVICE_Client *client,
+                          void *app_ctx)
+{
+}
+
+
+/**
+ * Initialize the PSYCstore service.
+ *
+ * @param cls Closure.
+ * @param server The initialized server.
+ * @param c Configuration to use.
+ */
+static void
+run (void *cls,
+     const struct GNUNET_CONFIGURATION_Handle *c,
+     struct GNUNET_SERVICE_Handle *svc)
+{
+  cfg = c;
+  service = svc;
+
+  /* Loading database plugin */
+  char *database;
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_string (cfg, "psycstore", "database",
+                                             &database))
+  {
+    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+                              "psycstore",
+                              "database");
+  }
+  else
+  {
+    GNUNET_asprintf (&db_lib_name,
+                    "libgnunet_plugin_psycstore_%s",
+                    database);
+    db = GNUNET_PLUGIN_load (db_lib_name, (void *) cfg);
+    GNUNET_free (database);
+  }
+  if (NULL == db)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+               "Could not load database backend `%s'\n",
+               db_lib_name);
+    GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
+    return;
+  }
+
+  stats = GNUNET_STATISTICS_create ("psycstore", cfg);
+  GNUNET_SCHEDULER_add_shutdown (shutdown_task,
+                                NULL);
+}
+
+/**
+ * Define "main" method using service macro.
+ */
+GNUNET_SERVICE_MAIN
+("psycstore",
+ GNUNET_SERVICE_OPTION_NONE,
+ run,
+ client_notify_connect,
+ client_notify_disconnect,
+ NULL,
+ GNUNET_MQ_hd_fixed_size (client_membership_store,
+                          GNUNET_MESSAGE_TYPE_PSYCSTORE_MEMBERSHIP_STORE,
+                          struct MembershipStoreRequest,
+                          NULL),
+ GNUNET_MQ_hd_fixed_size (client_membership_test,
+                          GNUNET_MESSAGE_TYPE_PSYCSTORE_MEMBERSHIP_TEST,
+                          struct MembershipTestRequest,
+                          NULL),
+ GNUNET_MQ_hd_var_size (client_fragment_store,
+                        GNUNET_MESSAGE_TYPE_PSYCSTORE_FRAGMENT_STORE,
+                        struct FragmentStoreRequest,
+                        NULL),
+ GNUNET_MQ_hd_fixed_size (client_fragment_get,
+                          GNUNET_MESSAGE_TYPE_PSYCSTORE_FRAGMENT_GET,
+                          struct FragmentGetRequest,
+                          NULL),
+ GNUNET_MQ_hd_var_size (client_message_get,
+                        GNUNET_MESSAGE_TYPE_PSYCSTORE_MESSAGE_GET,
+                        struct MessageGetRequest,
+                        NULL),
+ GNUNET_MQ_hd_fixed_size (client_message_get_fragment,
+                          GNUNET_MESSAGE_TYPE_PSYCSTORE_MESSAGE_GET_FRAGMENT,
+                          struct MessageGetFragmentRequest,
+                          NULL),
+ GNUNET_MQ_hd_fixed_size (client_counters_get,
+                          GNUNET_MESSAGE_TYPE_PSYCSTORE_COUNTERS_GET,
+                          struct OperationRequest,
+                          NULL),
+ GNUNET_MQ_hd_fixed_size (client_state_modify,
+                          GNUNET_MESSAGE_TYPE_PSYCSTORE_STATE_MODIFY,
+                          struct StateModifyRequest,
+                          NULL),
+ GNUNET_MQ_hd_var_size (client_state_sync,
+                        GNUNET_MESSAGE_TYPE_PSYCSTORE_STATE_SYNC,
+                        struct StateSyncRequest,
+                        NULL),
+ GNUNET_MQ_hd_fixed_size (client_state_reset,
+                          GNUNET_MESSAGE_TYPE_PSYCSTORE_STATE_RESET,
+                          struct OperationRequest,
+                          NULL),
+ GNUNET_MQ_hd_fixed_size (client_state_hash_update,
+                          GNUNET_MESSAGE_TYPE_PSYCSTORE_STATE_HASH_UPDATE,
+                          struct StateHashUpdateRequest,
+                          NULL),
+ GNUNET_MQ_hd_var_size (client_state_get,
+                        GNUNET_MESSAGE_TYPE_PSYCSTORE_STATE_GET,
+                        struct OperationRequest,
+                        NULL),
+ GNUNET_MQ_hd_var_size (client_state_get_prefix,
+                        GNUNET_MESSAGE_TYPE_PSYCSTORE_STATE_GET_PREFIX,
+                        struct OperationRequest,
+                        NULL));
+
+/* end of gnunet-service-psycstore.c */
diff --git a/src/psycstore/plugin_psycstore_mysql.c 
b/src/psycstore/plugin_psycstore_mysql.c
new file mode 100644
index 0000000..c36b6f7
--- /dev/null
+++ b/src/psycstore/plugin_psycstore_mysql.c
@@ -0,0 +1,1960 @@
+/*
+ * This file is part of GNUnet
+ * Copyright (C) 2013 GNUnet e.V.
+ *
+ * GNUnet is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * GNUnet 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
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+     SPDX-License-Identifier: AGPL3.0-or-later
+ */
+
+/**
+ * @file psycstore/plugin_psycstore_mysql.c
+ * @brief mysql-based psycstore backend
+ * @author Gabor X Toth
+ * @author Christian Grothoff
+ * @author Christophe Genevey
+ */
+
+#include "platform.h"
+#include "gnunet_psycstore_plugin.h"
+#include "gnunet_psycstore_service.h"
+#include "gnunet_multicast_service.h"
+#include "gnunet_crypto_lib.h"
+#include "gnunet_psyc_util_lib.h"
+#include "psycstore.h"
+#include "gnunet_my_lib.h"
+#include "gnunet_mysql_lib.h"
+#include <mysql/mysql.h>
+
+/**
+ * After how many ms "busy" should a DB operation fail for good?  A
+ * low value makes sure that we are more responsive to requests
+ * (especially PUTs).  A high value guarantees a higher success rate
+ * (SELECTs in iterate can take several seconds despite LIMIT=1).
+ *
+ * The default value of 1s should ensure that users do not experience
+ * huge latencies while at the same time allowing operations to
+ * succeed with reasonable probability.
+ */
+#define BUSY_TIMEOUT_MS 1000
+
+#define DEBUG_PSYCSTORE GNUNET_EXTRA_LOGGING
+
+/**
+ * Log an error message at log-level 'level' that indicates
+ * a failure of the command 'cmd' on file 'filename'
+ * with the message given by strerror(errno).
+ */
+#define LOG_MYSQL(db, level, cmd, stmt)                                 \
+  do {                                                                  \
+    GNUNET_log_from (level, "psycstore-mysql",                          \
+                     _("`%s' failed at %s:%d with error: %s\n"),        \
+                     cmd, __FILE__, __LINE__,                           \
+                     mysql_stmt_error 
(GNUNET_MYSQL_statement_get_stmt(stmt))); \
+  } while (0)
+
+#define LOG(kind,...) GNUNET_log_from (kind, "psycstore-mysql", __VA_ARGS__)
+
+enum Transactions {
+  TRANSACTION_NONE = 0,
+  TRANSACTION_STATE_MODIFY,
+  TRANSACTION_STATE_SYNC,
+};
+
+/**
+ * Context for all functions in this plugin.
+ */
+struct Plugin
+{
+
+  const struct GNUNET_CONFIGURATION_Handle *cfg;
+
+  /**
+   * MySQL context.
+   */
+  struct GNUNET_MYSQL_Context *mc;
+
+  /**
+   * Current transaction.
+   */
+  enum Transactions transaction;
+
+  /**
+   * Precompiled SQL for channel_key_store()
+   */
+  struct GNUNET_MYSQL_StatementHandle *insert_channel_key;
+
+  /**
+   * Precompiled SQL for slave_key_store()
+   */
+  struct GNUNET_MYSQL_StatementHandle *insert_slave_key;
+
+  /**
+   * Precompiled SQL for membership_store()
+   */
+  struct GNUNET_MYSQL_StatementHandle *insert_membership;
+
+  /**
+   * Precompiled SQL for membership_test()
+   */
+  struct GNUNET_MYSQL_StatementHandle *select_membership;
+
+  /**
+   * Precompiled SQL for fragment_store()
+   */
+  struct GNUNET_MYSQL_StatementHandle *insert_fragment;
+
+  /**
+   * Precompiled SQL for message_add_flags()
+   */
+  struct GNUNET_MYSQL_StatementHandle *update_message_flags;
+
+  /**
+   * Precompiled SQL for fragment_get()
+   */
+  struct GNUNET_MYSQL_StatementHandle *select_fragments;
+
+  /**
+   * Precompiled SQL for fragment_get()
+   */
+  struct GNUNET_MYSQL_StatementHandle *select_latest_fragments;
+
+  /**
+   * Precompiled SQL for message_get()
+   */
+  struct GNUNET_MYSQL_StatementHandle *select_messages;
+
+  /**
+   * Precompiled SQL for message_get()
+   */
+  struct GNUNET_MYSQL_StatementHandle *select_latest_messages;
+
+  /**
+   * Precompiled SQL for message_get_fragment()
+   */
+  struct GNUNET_MYSQL_StatementHandle *select_message_fragment;
+
+  /**
+   * Precompiled SQL for counters_get_message()
+   */
+  struct GNUNET_MYSQL_StatementHandle *select_counters_message;
+
+  /**
+   * Precompiled SQL for counters_get_state()
+   */
+  struct GNUNET_MYSQL_StatementHandle *select_counters_state;
+
+  /**
+   * Precompiled SQL for state_modify_end()
+   */
+  struct GNUNET_MYSQL_StatementHandle *update_state_hash_message_id;
+
+  /**
+   * Precompiled SQL for state_sync_end()
+   */
+  struct GNUNET_MYSQL_StatementHandle *update_max_state_message_id;
+
+  /**
+   * Precompiled SQL for state_modify_op()
+   */
+  struct GNUNET_MYSQL_StatementHandle *insert_state_current;
+
+  /**
+   * Precompiled SQL for state_modify_end()
+   */
+  struct GNUNET_MYSQL_StatementHandle *delete_state_empty;
+
+  /**
+   * Precompiled SQL for state_set_signed()
+   */
+  struct GNUNET_MYSQL_StatementHandle *update_state_signed;
+
+  /**
+   * Precompiled SQL for state_sync()
+   */
+  struct GNUNET_MYSQL_StatementHandle *insert_state_sync;
+
+  /**
+   * Precompiled SQL for state_sync()
+   */
+  struct GNUNET_MYSQL_StatementHandle *delete_state;
+
+  /**
+   * Precompiled SQL for state_sync()
+   */
+  struct GNUNET_MYSQL_StatementHandle *insert_state_from_sync;
+
+  /**
+   * Precompiled SQL for state_sync()
+   */
+  struct GNUNET_MYSQL_StatementHandle *delete_state_sync;
+
+  /**
+   * Precompiled SQL for state_get_signed()
+   */
+  struct GNUNET_MYSQL_StatementHandle *select_state_signed;
+
+  /**
+   * Precompiled SQL for state_get()
+   */
+  struct GNUNET_MYSQL_StatementHandle *select_state_one;
+
+  /**
+   * Precompiled SQL for state_get_prefix()
+   */
+  struct GNUNET_MYSQL_StatementHandle *select_state_prefix;
+
+};
+
+#if DEBUG_PSYCSTORE
+
+static void
+mysql_trace (void *cls, const char *sql)
+{
+  LOG(GNUNET_ERROR_TYPE_DEBUG, "MYSQL query:\n%s\n", sql);
+}
+
+#endif
+
+
+/**
+ * @brief Prepare a SQL statement
+ *
+ * @param dbh handle to the database
+ * @param sql SQL statement, UTF-8 encoded
+ * @param stmt set to the prepared statement
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
+ */
+static int
+mysql_prepare (struct GNUNET_MYSQL_Context *mc,
+              const char *sql,
+              struct GNUNET_MYSQL_StatementHandle **stmt)
+{
+  *stmt = GNUNET_MYSQL_statement_prepare (mc,
+                                          sql);
+
+  if (NULL == *stmt)
+  {
+    LOG (GNUNET_ERROR_TYPE_ERROR,
+         _("Error preparing SQL query: %s\n  %s\n"),
+         mysql_stmt_error (GNUNET_MYSQL_statement_get_stmt (*stmt)),
+         sql);
+    return GNUNET_SYSERR;
+  }
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Prepared `%s' / %p\n",
+       sql,
+       stmt);
+  return GNUNET_OK;
+}
+
+
+/**
+ * Initialize the database connections and associated
+ * data structures (create tables and indices
+ * as needed as well).
+ *
+ * @param plugin the plugin context (state for this module)
+ * @return #GNUNET_OK on success
+ */
+static int
+database_setup (struct Plugin *plugin)
+{
+  /* Open database and precompile statements */
+  plugin->mc = GNUNET_MYSQL_context_create (plugin->cfg,
+                                            "psycstore-mysql");
+
+  if (NULL == plugin->mc)
+  {
+    LOG (GNUNET_ERROR_TYPE_ERROR,
+         _("Unable to initialize Mysql.\n"));
+    return GNUNET_SYSERR;
+  }
+
+#define STMT_RUN(sql) \
+  if (GNUNET_OK != \
+      GNUNET_MYSQL_statement_run (plugin->mc, \
+                                  sql)) \
+  { \
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, \
+                _("Failed to run SQL statement `%s'\n"), \
+                sql); \
+    return GNUNET_SYSERR; \
+  }
+
+  /* Create tables */
+  STMT_RUN ("CREATE TABLE IF NOT EXISTS channels (\n"
+            " id BIGINT UNSIGNED AUTO_INCREMENT,\n"
+            " pub_key BLOB(32),\n"
+            " max_state_message_id BIGINT UNSIGNED,\n"
+            " state_hash_message_id BIGINT UNSIGNED,\n"
+            " PRIMARY KEY(id),\n"
+            " UNIQUE KEY(pub_key(32))\n"
+            ");");
+
+  STMT_RUN ("CREATE TABLE IF NOT EXISTS slaves (\n"
+            " id BIGINT UNSIGNED AUTO_INCREMENT,\n"
+            " pub_key BLOB(32),\n"
+            " PRIMARY KEY(id),\n"
+            " UNIQUE KEY(pub_key(32))\n"
+            ");");
+
+  STMT_RUN ("CREATE TABLE IF NOT EXISTS membership (\n"
+            "  channel_id BIGINT UNSIGNED NOT NULL REFERENCES channels(id),\n"
+            "  slave_id BIGINT UNSIGNED NOT NULL REFERENCES slaves(id),\n"
+            "  did_join TINYINT NOT NULL,\n"
+            "  announced_at BIGINT UNSIGNED NOT NULL,\n"
+            "  effective_since BIGINT UNSIGNED NOT NULL,\n"
+            "  group_generation BIGINT UNSIGNED NOT NULL\n"
+            ");");
+
+/*** FIX because IF NOT EXISTS doesn't work ***/
+  GNUNET_MYSQL_statement_run (plugin->mc,
+                              "CREATE INDEX idx_membership_channel_id_slave_id 
"
+                              "ON membership (channel_id, slave_id);");
+
+  /** @todo messages table: add method_name column */
+  STMT_RUN ("CREATE TABLE IF NOT EXISTS messages (\n"
+            "  channel_id BIGINT UNSIGNED NOT NULL REFERENCES channels(id),\n"
+            "  hop_counter BIGINT UNSIGNED NOT NULL,\n"
+            "  signature BLOB,\n"
+            "  purpose BLOB,\n"
+            "  fragment_id BIGINT UNSIGNED NOT NULL,\n"
+            "  fragment_offset BIGINT UNSIGNED NOT NULL,\n"
+                              "  message_id BIGINT UNSIGNED NOT NULL,\n"
+            "  group_generation BIGINT UNSIGNED NOT NULL,\n"
+            "  multicast_flags BIGINT UNSIGNED NOT NULL,\n"
+            "  psycstore_flags BIGINT UNSIGNED NOT NULL,\n"
+            "  data BLOB,\n"
+            "  PRIMARY KEY (channel_id, fragment_id),\n"
+            "  UNIQUE KEY(channel_id, message_id, fragment_offset)\n"
+            ");");
+
+  STMT_RUN ("CREATE TABLE IF NOT EXISTS state (\n"
+            "  channel_id BIGINT UNSIGNED NOT NULL REFERENCES channels(id),\n"
+            "  name TEXT NOT NULL,\n"
+            "  value_current BLOB,\n"
+            "  value_signed BLOB\n"
+            //"  PRIMARY KEY (channel_id, name(255))\n"
+            ");");
+
+  STMT_RUN ("CREATE TABLE IF NOT EXISTS state_sync (\n"
+            "  channel_id BIGINT UNSIGNED NOT NULL REFERENCES channels(id),\n"
+            "  name TEXT NOT NULL,\n"
+            "  value BLOB\n"
+            //"  PRIMARY KEY (channel_id, name(255))\n"
+            ");");
+#undef STMT_RUN
+
+  /* Prepare statements */
+#define PREP(stmt,handle)                                    \
+  if (GNUNET_OK != mysql_prepare (plugin->mc, stmt, handle)) \
+  { \
+    GNUNET_break (0); \
+    return GNUNET_SYSERR; \
+  }
+  PREP ("INSERT IGNORE INTO channels (pub_key) VALUES (?);",
+        &plugin->insert_channel_key);
+  PREP ("INSERT IGNORE INTO slaves (pub_key) VALUES (?);",
+        &plugin->insert_slave_key);
+  PREP ("INSERT INTO membership\n"
+        " (channel_id, slave_id, did_join, announced_at,\n"
+        "  effective_since, group_generation)\n"
+        "VALUES ((SELECT id FROM channels WHERE pub_key = ?),\n"
+        "        (SELECT id FROM slaves WHERE pub_key = ?),\n"
+        "        ?, ?, ?, ?);",
+        &plugin->insert_membership);
+  PREP ("SELECT did_join FROM membership\n"
+        "WHERE channel_id = (SELECT id FROM channels WHERE pub_key = ?)\n"
+        "      AND slave_id = (SELECT id FROM slaves WHERE pub_key = ?)\n"
+        "      AND effective_since <= ? AND did_join = 1\n"
+        "ORDER BY announced_at DESC LIMIT 1;",
+        &plugin->select_membership);
+
+  PREP ("INSERT IGNORE INTO messages\n"
+        " (channel_id, hop_counter, signature, purpose,\n"
+        "  fragment_id, fragment_offset, message_id,\n"
+        "  group_generation, multicast_flags, psycstore_flags, data)\n"
+        "VALUES ((SELECT id FROM channels WHERE pub_key = ?),\n"
+        "        ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);",
+        &plugin->insert_fragment);
+
+  PREP ("UPDATE messages\n"
+        "SET psycstore_flags = psycstore_flags | ?\n"
+        "WHERE channel_id = (SELECT id FROM channels WHERE pub_key = ?)\n"
+        "      AND message_id = ? AND fragment_offset = 0;",
+        &plugin->update_message_flags);
+
+  PREP ("SELECT hop_counter, signature, purpose, fragment_id,\n"
+        "       fragment_offset, message_id, group_generation,\n"
+        "       multicast_flags, psycstore_flags, data\n"
+        "FROM messages\n"
+        "WHERE channel_id = (SELECT id FROM channels WHERE pub_key = ?)\n"
+        "      AND ? <= fragment_id AND fragment_id <= ? LIMIT 1;",
+        &plugin->select_fragments);
+
+  /** @todo select_messages: add method_prefix filter */
+  PREP ("SELECT hop_counter, signature, purpose, fragment_id,\n"
+        "       fragment_offset, message_id, group_generation,\n"
+        "       multicast_flags, psycstore_flags, data\n"
+        "FROM messages\n"
+        "WHERE channel_id = (SELECT id FROM channels WHERE pub_key = ?)\n"
+        "      AND ? <= message_id AND message_id <= ?\n"
+        "LIMIT ?;",
+        &plugin->select_messages);
+
+  PREP ("SELECT * FROM\n"
+        "(SELECT hop_counter, signature, purpose, fragment_id,\n"
+        "        fragment_offset, message_id, group_generation,\n"
+        "        multicast_flags, psycstore_flags, data\n"
+        " FROM messages\n"
+        " WHERE channel_id = (SELECT id FROM channels WHERE pub_key = ?)\n"
+        " ORDER BY fragment_id DESC\n"
+        " LIMIT ?)\n"
+        "ORDER BY fragment_id;",
+        &plugin->select_latest_fragments);
+
+  /** @todo select_latest_messages: add method_prefix filter */
+  PREP ("SELECT hop_counter, signature, purpose, fragment_id,\n"
+        "       fragment_offset, message_id, group_generation,\n"
+        "        multicast_flags, psycstore_flags, data\n"
+        "FROM messages\n"
+        "WHERE channel_id = (SELECT id FROM channels WHERE pub_key = ?)\n"
+        "      AND message_id IN\n"
+        "      (SELECT message_id\n"
+        "       FROM messages\n"
+        "       WHERE channel_id = (SELECT id FROM channels WHERE pub_key = 
?)\n"
+        "       GROUP BY message_id\n"
+        "       ORDER BY message_id\n"
+        "       DESC LIMIT ?)\n"
+        "ORDER BY fragment_id;",
+        &plugin->select_latest_messages);
+
+  PREP ("SELECT hop_counter, signature, purpose, fragment_id,\n"
+        "       fragment_offset, message_id, group_generation,\n"
+        "       multicast_flags, psycstore_flags, data\n"
+        "FROM messages\n"
+        "WHERE channel_id = (SELECT id FROM channels WHERE pub_key = ?)\n"
+        "      AND message_id = ? AND fragment_offset = ?;",
+        &plugin->select_message_fragment);
+
+  PREP ("SELECT fragment_id, message_id, group_generation\n"
+        "FROM messages\n"
+        "WHERE channel_id = (SELECT id FROM channels WHERE pub_key = ?)\n"
+        "ORDER BY fragment_id DESC LIMIT 1;",
+        &plugin->select_counters_message);
+
+  PREP ("SELECT max_state_message_id\n"
+        "FROM channels\n"
+        "WHERE pub_key = ? AND max_state_message_id IS NOT NULL;",
+        &plugin->select_counters_state);
+
+  PREP ("UPDATE channels\n"
+        "SET max_state_message_id = ?\n"
+        "WHERE pub_key = ?;",
+        &plugin->update_max_state_message_id);
+
+  PREP ("UPDATE channels\n"
+        "SET state_hash_message_id = ?\n"
+        "WHERE pub_key = ?;",
+        &plugin->update_state_hash_message_id);
+
+  PREP ("REPLACE INTO state\n"
+        "  (channel_id, name, value_current, value_signed)\n"
+        "SELECT new.channel_id, new.name, new.value_current, 
old.value_signed\n"
+        "FROM (SELECT (SELECT id FROM channels WHERE pub_key = ?) AS 
channel_id,\n"
+        "             (SELECT ?) AS name,\n"
+        "             (SELECT ?) AS value_current\n"
+        "     ) AS new\n"
+        "LEFT JOIN (SELECT channel_id, name, value_signed\n"
+        "           FROM state) AS old\n"
+        "ON new.channel_id = old.channel_id AND new.name = old.name;",
+        &plugin->insert_state_current);
+
+  PREP ("DELETE FROM state\n"
+        "WHERE channel_id = (SELECT id FROM channels WHERE pub_key = ?)\n"
+        "      AND (value_current IS NULL OR length(value_current) = 0)\n"
+        "      AND (value_signed IS NULL OR length(value_signed) = 0);",
+        &plugin->delete_state_empty);
+
+  PREP ("UPDATE state\n"
+        "SET value_signed = value_current\n"
+        "WHERE channel_id = (SELECT id FROM channels WHERE pub_key = ?);",
+        &plugin->update_state_signed);
+
+  PREP ("DELETE FROM state\n"
+        "WHERE channel_id = (SELECT id FROM channels WHERE pub_key = ?);",
+        &plugin->delete_state);
+
+  PREP ("INSERT INTO state_sync (channel_id, name, value)\n"
+        "VALUES ((SELECT id FROM channels WHERE pub_key = ?), ?, ?);",
+        &plugin->insert_state_sync);
+
+  PREP ("INSERT INTO state\n"
+        " (channel_id, name, value_current, value_signed)\n"
+        "SELECT channel_id, name, value, value\n"
+        "FROM state_sync\n"
+        "WHERE channel_id = (SELECT id FROM channels WHERE pub_key = ?);",
+        &plugin->insert_state_from_sync);
+
+  PREP ("DELETE FROM state_sync\n"
+        "WHERE channel_id = (SELECT id FROM channels WHERE pub_key = ?);",
+        &plugin->delete_state_sync);
+
+  PREP ("SELECT value_current\n"
+        "FROM state\n"
+        "WHERE channel_id = (SELECT id FROM channels WHERE pub_key = ?)\n"
+        "      AND name = ?;",
+        &plugin->select_state_one);
+
+  PREP ("SELECT name, value_current\n"
+        "FROM state\n"
+        "WHERE channel_id = (SELECT id FROM channels WHERE pub_key = ?)\n"
+        "      AND (name = ? OR substr(name, 1, ?) = ?);",
+        &plugin->select_state_prefix);
+
+  PREP ("SELECT name, value_signed\n"
+        "FROM state\n"
+        "WHERE channel_id = (SELECT id FROM channels WHERE pub_key = ?)"
+        "      AND value_signed IS NOT NULL;",
+        &plugin->select_state_signed);
+#undef PREP
+
+  return GNUNET_OK;
+}
+
+
+/**
+ * Shutdown database connection and associate data
+ * structures.
+ * @param plugin the plugin context (state for this module)
+ */
+static void
+database_shutdown (struct Plugin *plugin)
+{
+  GNUNET_MYSQL_context_destroy (plugin->mc);
+}
+
+
+/**
+ * Execute a prepared statement with a @a channel_key argument.
+ *
+ * @param plugin Plugin handle.
+ * @param stmt Statement to execute.
+ * @param channel_key Public key of the channel.
+ *
+ * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+ */
+static int
+exec_channel (struct Plugin *plugin, struct GNUNET_MYSQL_StatementHandle *stmt,
+              const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key)
+{
+  struct GNUNET_MY_QueryParam params[] = {
+    GNUNET_MY_query_param_auto_from_type (channel_key),
+    GNUNET_MY_query_param_end
+  };
+
+  if (GNUNET_OK != GNUNET_MY_exec_prepared (plugin->mc, stmt, params))
+  {
+    LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+              "mysql exec_channel", stmt);
+  }
+
+  if (0 != mysql_stmt_reset (GNUNET_MYSQL_statement_get_stmt (stmt)))
+  {
+    LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+              "mysql_stmt_reset", stmt);
+    return GNUNET_SYSERR;
+  }
+
+  return GNUNET_OK;
+}
+
+
+/**
+ * Begin a transaction.
+ */
+static int
+transaction_begin (struct Plugin *plugin, enum Transactions transaction)
+{
+  if (GNUNET_OK != GNUNET_MYSQL_statement_run (plugin->mc, "BEGIN"))
+  {
+    LOG(GNUNET_ERROR_TYPE_ERROR, "transaction_begin failed");
+    return GNUNET_SYSERR;
+  }
+
+  plugin->transaction = transaction;
+  return GNUNET_OK;
+}
+
+
+/**
+ * Commit current transaction.
+ */
+static int
+transaction_commit (struct Plugin *plugin)
+{
+  if (GNUNET_OK != GNUNET_MYSQL_statement_run (plugin->mc, "COMMIT"))
+  {
+    LOG(GNUNET_ERROR_TYPE_ERROR, "transaction_commit failed");
+    return GNUNET_SYSERR;
+  }
+
+  plugin->transaction = TRANSACTION_NONE;
+  return GNUNET_OK;
+}
+
+
+/**
+ * Roll back current transaction.
+ */
+static int
+transaction_rollback (struct Plugin *plugin)
+{
+  if (GNUNET_OK != GNUNET_MYSQL_statement_run (plugin->mc, "ROLLBACK"))
+  {
+    LOG(GNUNET_ERROR_TYPE_ERROR, "transaction_rollback failed");
+    return GNUNET_SYSERR;
+  }
+
+  plugin->transaction = TRANSACTION_NONE;
+  return GNUNET_OK;
+}
+
+
+static int
+channel_key_store (struct Plugin *plugin,
+                   const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key)
+{
+  struct GNUNET_MYSQL_StatementHandle *stmt = plugin->insert_channel_key;
+
+  struct GNUNET_MY_QueryParam params[] = {
+    GNUNET_MY_query_param_auto_from_type (channel_key),
+    GNUNET_MY_query_param_end
+  };
+
+  if (GNUNET_OK != GNUNET_MY_exec_prepared (plugin->mc, stmt, params))
+  {
+    LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+              "mysql exec_prepared", stmt);
+    return GNUNET_SYSERR;
+  }
+
+  if (0 != mysql_stmt_reset (GNUNET_MYSQL_statement_get_stmt (stmt)))
+  {
+    LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+              "mysql_stmt_reset", stmt);
+    return GNUNET_SYSERR;
+  }
+
+  return GNUNET_OK;
+}
+
+
+static int
+slave_key_store (struct Plugin *plugin,
+                 const struct GNUNET_CRYPTO_EcdsaPublicKey *slave_key)
+{
+  struct GNUNET_MYSQL_StatementHandle *stmt = plugin->insert_slave_key;
+
+  struct GNUNET_MY_QueryParam params[] = {
+    GNUNET_MY_query_param_auto_from_type (slave_key),
+    GNUNET_MY_query_param_end
+  };
+
+  if (GNUNET_OK != GNUNET_MY_exec_prepared (plugin->mc, stmt, params))
+  {
+    LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+              "mysql exec_prepared", stmt);
+    return GNUNET_SYSERR;
+  }
+
+  if (0 != mysql_stmt_reset (GNUNET_MYSQL_statement_get_stmt (stmt)))
+  {
+    LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+              "mysql_stmt_reset", stmt);
+    return GNUNET_SYSERR;
+  }
+
+  return GNUNET_OK;
+}
+
+
+/**
+ * Store join/leave events for a PSYC channel in order to be able to answer
+ * membership test queries later.
+ *
+ * @see GNUNET_PSYCSTORE_membership_store()
+ *
+ * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+ */
+static int
+mysql_membership_store (void *cls,
+                         const struct GNUNET_CRYPTO_EddsaPublicKey 
*channel_key,
+                         const struct GNUNET_CRYPTO_EcdsaPublicKey *slave_key,
+                         int did_join,
+                         uint64_t announced_at,
+                         uint64_t effective_since,
+                         uint64_t group_generation)
+{
+  struct Plugin *plugin = cls;
+
+  uint32_t idid_join = (uint32_t)did_join;
+
+  struct GNUNET_MYSQL_StatementHandle *stmt = plugin->insert_membership;
+
+  GNUNET_assert (TRANSACTION_NONE == plugin->transaction);
+
+  if (announced_at > INT64_MAX ||
+      effective_since > INT64_MAX ||
+      group_generation > INT64_MAX)
+  {
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+
+  if (GNUNET_OK != channel_key_store (plugin, channel_key)
+      || GNUNET_OK != slave_key_store (plugin, slave_key))
+    return GNUNET_SYSERR;
+
+  struct GNUNET_MY_QueryParam params[] = {
+    GNUNET_MY_query_param_auto_from_type (channel_key),
+    GNUNET_MY_query_param_auto_from_type (slave_key),
+    GNUNET_MY_query_param_uint32 (&idid_join),
+    GNUNET_MY_query_param_uint64 (&announced_at),
+    GNUNET_MY_query_param_uint64 (&effective_since),
+    GNUNET_MY_query_param_uint64 (&group_generation),
+    GNUNET_MY_query_param_end
+  };
+
+  if (GNUNET_OK != GNUNET_MY_exec_prepared (plugin->mc, stmt, params))
+  {
+    LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+              "mysql exec_prepared", stmt);
+    return GNUNET_SYSERR;
+  }
+
+  if (0 != mysql_stmt_reset (GNUNET_MYSQL_statement_get_stmt (stmt)))
+  {
+    LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+              "mysql_stmt_reset", stmt);
+    return GNUNET_SYSERR;
+  }
+  return GNUNET_OK;
+}
+
+/**
+ * Test if a member was admitted to the channel at the given message ID.
+ *
+ * @see GNUNET_PSYCSTORE_membership_test()
+ *
+ * @return #GNUNET_YES if the member was admitted, #GNUNET_NO if not,
+ *         #GNUNET_SYSERR if there was en error.
+ */
+static int
+membership_test (void *cls,
+                 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+                 const struct GNUNET_CRYPTO_EcdsaPublicKey *slave_key,
+                 uint64_t message_id)
+{
+  struct Plugin *plugin = cls;
+
+  struct GNUNET_MYSQL_StatementHandle *stmt = plugin->select_membership;
+
+  uint32_t did_join = 0;
+
+  int ret = GNUNET_SYSERR;
+
+  struct GNUNET_MY_QueryParam params_select[] = {
+    GNUNET_MY_query_param_auto_from_type (channel_key),
+    GNUNET_MY_query_param_auto_from_type (slave_key),
+    GNUNET_MY_query_param_uint64 (&message_id),
+    GNUNET_MY_query_param_end
+  };
+
+  if (GNUNET_OK != GNUNET_MY_exec_prepared (plugin->mc, stmt, params_select))
+  {
+    LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "mysql execute prepared", stmt);
+    return GNUNET_SYSERR;
+  }
+
+  struct GNUNET_MY_ResultSpec results_select[] = {
+    GNUNET_MY_result_spec_uint32 (&did_join),
+    GNUNET_MY_result_spec_end
+  };
+
+  switch (GNUNET_MY_extract_result (stmt, results_select))
+  {
+    case GNUNET_NO:
+      ret = GNUNET_NO;
+      break;
+    case GNUNET_OK:
+      ret = GNUNET_YES;
+      break;
+    default:
+      LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "mysql extract_result", stmt);
+      return GNUNET_SYSERR;
+  }
+
+  if (0 != mysql_stmt_reset (GNUNET_MYSQL_statement_get_stmt (stmt)))
+  {
+    LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+              "mysql_stmt_reset", stmt);
+    return GNUNET_SYSERR;
+  }
+
+  return ret;
+}
+
+/**
+ * Store a message fragment sent to a channel.
+ *
+ * @see GNUNET_PSYCSTORE_fragment_store()
+ *
+ * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+ */
+static int
+fragment_store (void *cls,
+                const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+                const struct GNUNET_MULTICAST_MessageHeader *msg,
+                uint32_t psycstore_flags)
+{
+  struct Plugin *plugin = cls;
+
+  struct GNUNET_MYSQL_StatementHandle *stmt = plugin->insert_fragment;
+
+  GNUNET_assert (TRANSACTION_NONE == plugin->transaction);
+
+  uint64_t fragment_id = GNUNET_ntohll (msg->fragment_id);
+
+  uint64_t fragment_offset = GNUNET_ntohll (msg->fragment_offset);
+  uint64_t message_id = GNUNET_ntohll (msg->message_id);
+  uint64_t group_generation = GNUNET_ntohll (msg->group_generation);
+
+  uint64_t hop_counter = ntohl(msg->hop_counter);
+  uint64_t flags = ntohl(msg->flags);
+
+  if (fragment_id > INT64_MAX || fragment_offset > INT64_MAX ||
+      message_id > INT64_MAX || group_generation > INT64_MAX)
+  {
+    LOG(GNUNET_ERROR_TYPE_ERROR,
+         "Tried to store fragment with a field > INT64_MAX: "
+         "%lu, %lu, %lu, %lu\n", fragment_id, fragment_offset,
+         message_id, group_generation);
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+
+  if (GNUNET_OK != channel_key_store (plugin, channel_key))
+    return GNUNET_SYSERR;
+
+  struct GNUNET_MY_QueryParam params_insert[] = {
+    GNUNET_MY_query_param_auto_from_type (channel_key),
+    GNUNET_MY_query_param_uint64 (&hop_counter),
+    GNUNET_MY_query_param_auto_from_type (&msg->signature),
+    GNUNET_MY_query_param_auto_from_type (&msg->purpose),
+    GNUNET_MY_query_param_uint64 (&fragment_id),
+    GNUNET_MY_query_param_uint64 (&fragment_offset),
+    GNUNET_MY_query_param_uint64 (&message_id),
+    GNUNET_MY_query_param_uint64 (&group_generation),
+    GNUNET_MY_query_param_uint64 (&flags),
+    GNUNET_MY_query_param_uint32 (&psycstore_flags),
+    GNUNET_MY_query_param_fixed_size (&msg[1], ntohs (msg->header.size)
+                                                  - sizeof (*msg)),
+    GNUNET_MY_query_param_end
+  };
+
+  if (GNUNET_OK != GNUNET_MY_exec_prepared (plugin->mc, stmt, params_insert))
+  {
+    LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+              "mysql execute prepared", stmt);
+    return GNUNET_SYSERR;
+  }
+
+  if (0 != mysql_stmt_reset (GNUNET_MYSQL_statement_get_stmt (stmt)))
+  {
+    LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+              "mysql_stmt_reset", stmt);
+    return GNUNET_SYSERR;
+  }
+
+  return GNUNET_OK;
+}
+
+/**
+ * Set additional flags for a given message.
+ *
+ * They are OR'd with any existing flags set.
+ *
+ * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+ */
+static int
+message_add_flags (void *cls,
+                   const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+                   uint64_t message_id,
+                   uint32_t psycstore_flags)
+{
+  struct Plugin *plugin = cls;
+  struct GNUNET_MYSQL_StatementHandle *stmt = plugin->update_message_flags;
+
+  int sql_ret;
+  int ret = GNUNET_SYSERR;
+
+  struct GNUNET_MY_QueryParam params_update[] = {
+    GNUNET_MY_query_param_uint32 (&psycstore_flags),
+    GNUNET_MY_query_param_auto_from_type (channel_key),
+    GNUNET_MY_query_param_uint64 (&message_id),
+    GNUNET_MY_query_param_end
+  };
+
+  sql_ret = GNUNET_MY_exec_prepared (plugin->mc, stmt, params_update);
+  switch (sql_ret)
+  {
+    case GNUNET_OK:
+      ret = GNUNET_OK;
+      break;
+
+    default:
+       LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+              "mysql execute prepared", stmt);
+  }
+
+  if (0 != mysql_stmt_reset (GNUNET_MYSQL_statement_get_stmt (stmt)))
+  {
+    LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+              "mysql_stmt_reset", stmt);
+    return GNUNET_SYSERR;
+  }
+
+  return ret;
+}
+
+
+static int
+fragment_row (struct GNUNET_MYSQL_StatementHandle *stmt,
+              GNUNET_PSYCSTORE_FragmentCallback cb,
+              void *cb_cls,
+              uint64_t *returned_fragments)
+{
+
+  uint32_t hop_counter;
+  void *signature = NULL;
+  void *purpose = NULL;
+  size_t signature_size;
+  size_t purpose_size;
+  uint64_t fragment_id;
+  uint64_t fragment_offset;
+  uint64_t message_id;
+  uint64_t group_generation;
+  uint64_t flags;
+  void *buf;
+  size_t buf_size;
+  int ret = GNUNET_SYSERR;
+  int sql_ret;
+  struct GNUNET_MULTICAST_MessageHeader *mp;
+  uint64_t msg_flags;
+  struct GNUNET_MY_ResultSpec results[] = {
+    GNUNET_MY_result_spec_uint32 (&hop_counter),
+    GNUNET_MY_result_spec_variable_size (&signature, &signature_size),
+    GNUNET_MY_result_spec_variable_size (&purpose, &purpose_size),
+    GNUNET_MY_result_spec_uint64 (&fragment_id),
+    GNUNET_MY_result_spec_uint64 (&fragment_offset),
+    GNUNET_MY_result_spec_uint64 (&message_id),
+    GNUNET_MY_result_spec_uint64 (&group_generation),
+    GNUNET_MY_result_spec_uint64 (&msg_flags),
+    GNUNET_MY_result_spec_uint64 (&flags),
+    GNUNET_MY_result_spec_variable_size (&buf,
+                                         &buf_size),
+    GNUNET_MY_result_spec_end
+  };
+
+  do
+  {
+    sql_ret = GNUNET_MY_extract_result (stmt, results);
+    switch (sql_ret)
+    {
+    case GNUNET_NO:
+      if (ret != GNUNET_YES)
+        ret = GNUNET_NO;
+      break;
+
+    case GNUNET_YES:
+      mp = GNUNET_malloc (sizeof (*mp) + buf_size);
+
+      mp->header.size = htons (sizeof (*mp) + buf_size);
+      mp->header.type = htons (GNUNET_MESSAGE_TYPE_MULTICAST_MESSAGE);
+      mp->hop_counter = htonl (hop_counter);
+      GNUNET_memcpy (&mp->signature,
+                     signature,
+                     signature_size);
+      GNUNET_memcpy (&mp->purpose,
+                     purpose,
+                     purpose_size);
+      mp->fragment_id = GNUNET_htonll (fragment_id);
+      mp->fragment_offset = GNUNET_htonll (fragment_offset);
+      mp->message_id = GNUNET_htonll (message_id);
+      mp->group_generation = GNUNET_htonll (group_generation);
+      mp->flags = htonl(msg_flags);
+
+      GNUNET_memcpy (&mp[1],
+                     buf,
+                     buf_size);
+      ret = cb (cb_cls, mp, (enum GNUNET_PSYCSTORE_MessageFlags) flags);
+      if (NULL != returned_fragments)
+        (*returned_fragments)++;
+      GNUNET_MY_cleanup_result (results);
+      break;
+
+    default:
+      LOG_MYSQL (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                 "mysql extract_result", stmt);
+    }
+  }
+  while (GNUNET_YES == sql_ret);
+
+  // for debugging
+  if (GNUNET_NO == ret)
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
+               "Empty result set\n");
+
+  return ret;
+}
+
+
+static int
+fragment_select (struct Plugin *plugin,
+                 struct GNUNET_MYSQL_StatementHandle *stmt,
+                 struct GNUNET_MY_QueryParam *params,
+                 uint64_t *returned_fragments,
+                 GNUNET_PSYCSTORE_FragmentCallback cb,
+                 void *cb_cls)
+{
+  int ret = GNUNET_SYSERR;
+  int sql_ret;
+
+  sql_ret = GNUNET_MY_exec_prepared (plugin->mc, stmt, params);
+  switch (sql_ret)
+  {
+    case GNUNET_NO:
+      if (ret != GNUNET_YES)
+        ret = GNUNET_NO;
+      break;
+
+    case GNUNET_YES:
+      ret = fragment_row (stmt, cb, cb_cls, returned_fragments);
+      break;
+
+    default:
+      LOG_MYSQL (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                 "mysql exec_prepared", stmt);
+  }
+  return ret;
+}
+
+
+/**
+ * Retrieve a message fragment range by fragment ID.
+ *
+ * @see GNUNET_PSYCSTORE_fragment_get()
+ *
+ * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+ */
+static int
+fragment_get (void *cls,
+              const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+              uint64_t first_fragment_id,
+              uint64_t last_fragment_id,
+              uint64_t *returned_fragments,
+              GNUNET_PSYCSTORE_FragmentCallback cb,
+              void *cb_cls)
+{
+  struct Plugin *plugin = cls;
+  struct GNUNET_MYSQL_StatementHandle *stmt = plugin->select_fragments;
+  int ret = GNUNET_SYSERR;
+  struct GNUNET_MY_QueryParam params_select[] = {
+    GNUNET_MY_query_param_auto_from_type (channel_key),
+    GNUNET_MY_query_param_uint64 (&first_fragment_id),
+    GNUNET_MY_query_param_uint64 (&last_fragment_id),
+    GNUNET_MY_query_param_end
+  };
+
+  *returned_fragments = 0;
+  ret = fragment_select (plugin, stmt, params_select, returned_fragments, cb, 
cb_cls);
+
+  if (0 != mysql_stmt_reset (GNUNET_MYSQL_statement_get_stmt (stmt)))
+  {
+    LOG_MYSQL (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+              "mysql_stmt_reset", stmt);
+    return GNUNET_SYSERR;
+  }
+
+  return ret;
+}
+
+
+/**
+ * Retrieve a message fragment range by fragment ID.
+ *
+ * @see GNUNET_PSYCSTORE_fragment_get_latest()
+ *
+ * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+ */
+static int
+fragment_get_latest (void *cls,
+                     const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+                     uint64_t fragment_limit,
+                     uint64_t *returned_fragments,
+                     GNUNET_PSYCSTORE_FragmentCallback cb,
+                     void *cb_cls)
+{
+  struct Plugin *plugin = cls;
+
+  struct GNUNET_MYSQL_StatementHandle *stmt = plugin->select_latest_fragments;
+
+  int ret = GNUNET_SYSERR;
+  *returned_fragments = 0;
+
+  struct GNUNET_MY_QueryParam params_select[] = {
+    GNUNET_MY_query_param_auto_from_type (channel_key),
+    GNUNET_MY_query_param_uint64 (&fragment_limit),
+    GNUNET_MY_query_param_end
+  };
+
+  ret = fragment_select (plugin, stmt, params_select, returned_fragments, cb, 
cb_cls);
+
+  if (0 != mysql_stmt_reset (GNUNET_MYSQL_statement_get_stmt (stmt)))
+  {
+    LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+              "mysql_stmt_reset", stmt);
+    return GNUNET_SYSERR;
+  }
+
+  return ret;
+}
+
+
+/**
+ * Retrieve all fragments of a message ID range.
+ *
+ * @see GNUNET_PSYCSTORE_message_get()
+ *
+ * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+ */
+static int
+message_get (void *cls,
+             const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+             uint64_t first_message_id,
+             uint64_t last_message_id,
+             uint64_t fragment_limit,
+             uint64_t *returned_fragments,
+             GNUNET_PSYCSTORE_FragmentCallback cb,
+             void *cb_cls)
+{
+  struct Plugin *plugin = cls;
+  struct GNUNET_MYSQL_StatementHandle *stmt = plugin->select_messages;
+  int ret;
+
+  if (0 == fragment_limit)
+    fragment_limit = UINT64_MAX;
+
+  struct GNUNET_MY_QueryParam params_select[] = {
+    GNUNET_MY_query_param_auto_from_type (channel_key),
+    GNUNET_MY_query_param_uint64 (&first_message_id),
+    GNUNET_MY_query_param_uint64 (&last_message_id),
+    GNUNET_MY_query_param_uint64 (&fragment_limit),
+    GNUNET_MY_query_param_end
+  };
+
+  *returned_fragments = 0;
+  ret = fragment_select (plugin, stmt, params_select, returned_fragments, cb, 
cb_cls);
+
+  if (0 != mysql_stmt_reset (GNUNET_MYSQL_statement_get_stmt (stmt)))
+  {
+    LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+              "mysql_stmt_reset", stmt);
+    return GNUNET_SYSERR;
+  }
+
+  return ret;
+}
+
+
+/**
+ * Retrieve all fragments of the latest messages.
+ *
+ * @see GNUNET_PSYCSTORE_message_get_latest()
+ *
+ * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+ */
+static int
+message_get_latest (void *cls,
+                    const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+                    uint64_t message_limit,
+                    uint64_t *returned_fragments,
+                    GNUNET_PSYCSTORE_FragmentCallback cb,
+                    void *cb_cls)
+{
+  struct Plugin *plugin = cls;
+
+  struct GNUNET_MYSQL_StatementHandle *stmt = plugin->select_latest_messages;
+
+  int ret = GNUNET_SYSERR;
+  *returned_fragments = 0;
+
+  struct GNUNET_MY_QueryParam params_select[] = {
+    GNUNET_MY_query_param_auto_from_type (channel_key),
+    GNUNET_MY_query_param_auto_from_type (channel_key),
+    GNUNET_MY_query_param_uint64 (&message_limit),
+    GNUNET_MY_query_param_end
+  };
+
+  ret = fragment_select (plugin, stmt, params_select, returned_fragments, cb, 
cb_cls);
+
+  if (0 != mysql_stmt_reset (GNUNET_MYSQL_statement_get_stmt (stmt)))
+  {
+    LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+              "mysql_stmt_reset", stmt);
+    return GNUNET_SYSERR;
+  }
+
+  return ret;
+}
+
+
+/**
+ * Retrieve a fragment of message specified by its message ID and fragment
+ * offset.
+ *
+ * @see GNUNET_PSYCSTORE_message_get_fragment()
+ *
+ * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+ */
+static int
+message_get_fragment (void *cls,
+                      const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+                      uint64_t message_id,
+                      uint64_t fragment_offset,
+                      GNUNET_PSYCSTORE_FragmentCallback cb,
+                      void *cb_cls)
+{
+  struct Plugin *plugin = cls;
+  struct GNUNET_MYSQL_StatementHandle *stmt = plugin->select_message_fragment;
+  int sql_ret;
+  int ret = GNUNET_SYSERR;
+
+  struct GNUNET_MY_QueryParam params_select[] = {
+    GNUNET_MY_query_param_auto_from_type (channel_key),
+    GNUNET_MY_query_param_uint64 (&message_id),
+    GNUNET_MY_query_param_uint64 (&fragment_offset),
+    GNUNET_MY_query_param_end
+  };
+
+  sql_ret = GNUNET_MY_exec_prepared (plugin->mc, stmt, params_select);
+  switch (sql_ret)
+  {
+    case GNUNET_NO:
+      ret = GNUNET_NO;
+      break;
+
+    case GNUNET_OK:
+      ret = fragment_row (stmt, cb, cb_cls, NULL);
+      break;
+
+    default:
+      LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+              "mysql execute prepared", stmt);
+  }
+
+  if (0 != mysql_stmt_reset (GNUNET_MYSQL_statement_get_stmt (stmt)))
+  {
+    LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+              "mysql_stmt_reset", stmt);
+    return GNUNET_SYSERR;
+  }
+
+  return ret;
+}
+
+/**
+ * Retrieve the max. values of message counters for a channel.
+ *
+ * @see GNUNET_PSYCSTORE_counters_get()
+ *
+ * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+ */
+static int
+counters_message_get (void *cls,
+                      const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+                      uint64_t *max_fragment_id,
+                      uint64_t *max_message_id,
+                      uint64_t *max_group_generation)
+{
+  struct Plugin *plugin = cls;
+
+  struct GNUNET_MYSQL_StatementHandle *stmt = plugin->select_counters_message;
+
+  int ret = GNUNET_SYSERR;
+
+  struct GNUNET_MY_QueryParam params_select[] = {
+    GNUNET_MY_query_param_auto_from_type (channel_key),
+    GNUNET_MY_query_param_end
+  };
+
+  if (GNUNET_OK != GNUNET_MY_exec_prepared (plugin->mc, stmt, params_select))
+  {
+    LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+              "mysql execute prepared", stmt);
+    return GNUNET_SYSERR;
+  }
+
+  struct GNUNET_MY_ResultSpec results_select[] = {
+    GNUNET_MY_result_spec_uint64 (max_fragment_id),
+    GNUNET_MY_result_spec_uint64 (max_message_id),
+    GNUNET_MY_result_spec_uint64 (max_group_generation),
+    GNUNET_MY_result_spec_end
+  };
+
+  ret = GNUNET_MY_extract_result (stmt, results_select);
+
+  if (GNUNET_OK != ret)
+  {
+    LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+              "mysql extract_result", stmt);
+    return GNUNET_SYSERR;
+  }
+
+  if (0 != mysql_stmt_reset (GNUNET_MYSQL_statement_get_stmt (stmt)))
+  {
+    LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+              "mysql_stmt_reset", stmt);
+    return GNUNET_SYSERR;
+  }
+
+  return ret;
+}
+
+/**
+ * Retrieve the max. values of state counters for a channel.
+ *
+ * @see GNUNET_PSYCSTORE_counters_get()
+ *
+ * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+ */
+static int
+counters_state_get (void *cls,
+                    const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+                    uint64_t *max_state_message_id)
+{
+  struct Plugin *plugin = cls;
+
+  struct GNUNET_MYSQL_StatementHandle *stmt = plugin->select_counters_state;
+
+  int ret = GNUNET_SYSERR;
+
+  struct GNUNET_MY_QueryParam params_select[] = {
+    GNUNET_MY_query_param_auto_from_type (channel_key),
+    GNUNET_MY_query_param_end
+  };
+
+  if (GNUNET_OK != GNUNET_MY_exec_prepared (plugin->mc, stmt, params_select))
+  {
+    LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+              "mysql execute prepared", stmt);
+    return GNUNET_SYSERR;
+  }
+
+  struct GNUNET_MY_ResultSpec results_select[] = {
+    GNUNET_MY_result_spec_uint64 (max_state_message_id),
+    GNUNET_MY_result_spec_end
+  };
+
+  ret = GNUNET_MY_extract_result (stmt, results_select);
+
+  if (GNUNET_OK != ret)
+  {
+    LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+              "mysql extract_result", stmt);
+    return GNUNET_SYSERR;
+  }
+
+  if (0 != mysql_stmt_reset (GNUNET_MYSQL_statement_get_stmt (stmt)))
+  {
+    LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+              "mysql_stmt_reset", stmt);
+    return GNUNET_SYSERR;
+  }
+
+  return ret;
+}
+
+
+/**
+ * Assign a value to a state variable.
+ *
+ * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+ */
+static int
+state_assign (struct Plugin *plugin, struct GNUNET_MYSQL_StatementHandle *stmt,
+              const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+              const char *name, const void *value, size_t value_size)
+{
+  int ret = GNUNET_SYSERR;
+
+  struct GNUNET_MY_QueryParam params[] = {
+    GNUNET_MY_query_param_auto_from_type (channel_key),
+    GNUNET_MY_query_param_string (name),
+    GNUNET_MY_query_param_fixed_size(value, value_size),
+    GNUNET_MY_query_param_end
+  };
+
+  ret = GNUNET_MY_exec_prepared (plugin->mc, stmt, params);
+  if (GNUNET_OK != ret)
+  {
+    LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+              "mysql exec_prepared", stmt);
+    return GNUNET_SYSERR;
+  }
+
+  if (0 != mysql_stmt_reset (GNUNET_MYSQL_statement_get_stmt (stmt)))
+  {
+    LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+              "mysql_stmt_reset", stmt);
+    return GNUNET_SYSERR;
+  }
+
+  return ret;
+}
+
+
+static int
+update_message_id (struct Plugin *plugin, struct GNUNET_MYSQL_StatementHandle 
*stmt,
+                   const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+                   uint64_t message_id)
+{
+  struct GNUNET_MY_QueryParam params[] = {
+    GNUNET_MY_query_param_uint64 (&message_id),
+    GNUNET_MY_query_param_auto_from_type (channel_key),
+    GNUNET_MY_query_param_end
+  };
+
+  if (GNUNET_OK != GNUNET_MY_exec_prepared (plugin->mc,
+                                            stmt,
+                                            params))
+  {
+    LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+              "mysql execute prepared", stmt);
+    return GNUNET_SYSERR;
+  }
+
+  if (0 != mysql_stmt_reset (GNUNET_MYSQL_statement_get_stmt (stmt)))
+  {
+    LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+              "mysql_stmt_reset", stmt);
+    return GNUNET_SYSERR;
+  }
+
+  return GNUNET_OK;
+}
+
+
+/**
+ * Begin modifying current state.
+ */
+static int
+state_modify_begin (void *cls,
+                    const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+                    uint64_t message_id, uint64_t state_delta)
+{
+  struct Plugin *plugin = cls;
+
+  if (state_delta > 0)
+  {
+    /**
+     * We can only apply state modifiers in the current message if modifiers in
+     * the previous stateful message (message_id - state_delta) were already
+     * applied.
+     */
+
+    uint64_t max_state_message_id = 0;
+    int ret = counters_state_get (plugin, channel_key, &max_state_message_id);
+    switch (ret)
+    {
+    case GNUNET_OK:
+    case GNUNET_NO: // no state yet
+      ret = GNUNET_OK;
+      break;
+    default:
+      return ret;
+    }
+
+    if (max_state_message_id < message_id - state_delta)
+      return GNUNET_NO; /* some stateful messages not yet applied */
+    else if (message_id - state_delta < max_state_message_id)
+      return GNUNET_NO; /* changes already applied */
+  }
+
+  if (TRANSACTION_NONE != plugin->transaction)
+  {
+    /** @todo FIXME: wait for other transaction to finish  */
+    return GNUNET_SYSERR;
+  }
+  return transaction_begin (plugin, TRANSACTION_STATE_MODIFY);
+}
+
+
+/**
+ * Set the current value of state variable.
+ *
+ * @see GNUNET_PSYCSTORE_state_modify()
+ *
+ * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+ */
+static int
+state_modify_op (void *cls,
+                 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+                 enum GNUNET_PSYC_Operator op,
+                 const char *name, const void *value, size_t value_size)
+{
+  struct Plugin *plugin = cls;
+  GNUNET_assert (TRANSACTION_STATE_MODIFY == plugin->transaction);
+
+  switch (op)
+  {
+  case GNUNET_PSYC_OP_ASSIGN:
+    return state_assign (plugin, plugin->insert_state_current,
+                         channel_key, name, value, value_size);
+
+  default: /** @todo implement more state operations */
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+}
+
+
+/**
+ * End modifying current state.
+ */
+static int
+state_modify_end (void *cls,
+                  const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+                  uint64_t message_id)
+{
+  struct Plugin *plugin = cls;
+  GNUNET_assert (TRANSACTION_STATE_MODIFY == plugin->transaction);
+
+  return
+    GNUNET_OK == exec_channel (plugin, plugin->delete_state_empty, channel_key)
+    && GNUNET_OK == update_message_id (plugin,
+                                       plugin->update_max_state_message_id,
+                                       channel_key, message_id)
+    && GNUNET_OK == transaction_commit (plugin)
+    ? GNUNET_OK : GNUNET_SYSERR;
+}
+
+
+/**
+ * Begin state synchronization.
+ */
+static int
+state_sync_begin (void *cls,
+                  const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key)
+{
+  struct Plugin *plugin = cls;
+  return exec_channel (plugin, plugin->delete_state_sync, channel_key);
+}
+
+
+/**
+ * Assign current value of a state variable.
+ *
+ * @see GNUNET_PSYCSTORE_state_modify()
+ *
+ * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+ */
+static int
+state_sync_assign (void *cls,
+                const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+                const char *name, const void *value, size_t value_size)
+{
+  struct Plugin *plugin = cls;
+  return state_assign (cls, plugin->insert_state_sync,
+                       channel_key, name, value, value_size);
+}
+
+
+/**
+ * End modifying current state.
+ */
+static int
+state_sync_end (void *cls,
+                const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+                uint64_t max_state_message_id,
+                uint64_t state_hash_message_id)
+{
+  struct Plugin *plugin = cls;
+  int ret = GNUNET_SYSERR;
+
+  if (TRANSACTION_NONE != plugin->transaction)
+  {
+    /** @todo FIXME: wait for other transaction to finish  */
+    return GNUNET_SYSERR;
+  }
+
+  GNUNET_OK == transaction_begin (plugin, TRANSACTION_STATE_SYNC)
+    && GNUNET_OK == exec_channel (plugin, plugin->delete_state, channel_key)
+    && GNUNET_OK == exec_channel (plugin, plugin->insert_state_from_sync,
+                                  channel_key)
+    && GNUNET_OK == exec_channel (plugin, plugin->delete_state_sync,
+                                  channel_key)
+    && GNUNET_OK == update_message_id (plugin,
+                                       plugin->update_state_hash_message_id,
+                                       channel_key, state_hash_message_id)
+    && GNUNET_OK == update_message_id (plugin,
+                                       plugin->update_max_state_message_id,
+                                       channel_key, max_state_message_id)
+    && GNUNET_OK == transaction_commit (plugin)
+    ? ret = GNUNET_OK
+    : transaction_rollback (plugin);
+  return ret;
+}
+
+
+/**
+ * Delete the whole state.
+ *
+ * @see GNUNET_PSYCSTORE_state_reset()
+ *
+ * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+ */
+static int
+state_reset (void *cls, const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key)
+{
+  struct Plugin *plugin = cls;
+  return exec_channel (plugin, plugin->delete_state, channel_key);
+}
+
+
+/**
+ * Update signed values of state variables in the state store.
+ *
+ * @see GNUNET_PSYCSTORE_state_hash_update()
+ *
+ * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+ */
+static int
+state_update_signed (void *cls,
+                     const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key)
+{
+  struct Plugin *plugin = cls;
+  return exec_channel (plugin, plugin->update_state_signed, channel_key);
+}
+
+
+/**
+ * Retrieve a state variable by name.
+ *
+ * @see GNUNET_PSYCSTORE_state_get()
+ *
+ * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+ */
+static int
+state_get (void *cls, const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+           const char *name, GNUNET_PSYCSTORE_StateCallback cb, void *cb_cls)
+{
+  struct Plugin *plugin = cls;
+  int ret = GNUNET_SYSERR;
+  int sql_ret ;
+
+  struct GNUNET_MYSQL_StatementHandle *stmt = plugin->select_state_one;
+
+  struct GNUNET_MY_QueryParam params_select[] = {
+    GNUNET_MY_query_param_auto_from_type (channel_key),
+    GNUNET_MY_query_param_string (name),
+    GNUNET_MY_query_param_end
+  };
+
+  void *value_current = NULL;
+  size_t value_size = 0;
+
+  struct GNUNET_MY_ResultSpec results[] = {
+    GNUNET_MY_result_spec_variable_size (&value_current, &value_size),
+    GNUNET_MY_result_spec_end
+  };
+
+  if (GNUNET_OK != GNUNET_MY_exec_prepared (plugin->mc, stmt, params_select))
+  {
+    LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+              "mysql exec_prepared", stmt);
+  }
+  else
+  {
+    sql_ret = GNUNET_MY_extract_result (stmt, results);
+    switch (sql_ret)
+    {
+    case GNUNET_NO:
+      ret = GNUNET_NO;
+      break;
+
+    case GNUNET_YES:
+      ret = cb (cb_cls, name, value_current, value_size);
+      break;
+
+    default:
+      LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "mysql extract_result", stmt);
+    }
+  }
+
+  if (0 != mysql_stmt_reset (GNUNET_MYSQL_statement_get_stmt (stmt)))
+  {
+    LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+              "mysql_stmt_reset", stmt);
+    return GNUNET_SYSERR;
+  }
+
+  return ret;
+}
+
+
+/**
+ * Retrieve all state variables for a channel with the given prefix.
+ *
+ * @see GNUNET_PSYCSTORE_state_get_prefix()
+ *
+ * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+ */
+static int
+state_get_prefix (void *cls, const struct GNUNET_CRYPTO_EddsaPublicKey 
*channel_key,
+                  const char *name, GNUNET_PSYCSTORE_StateCallback cb,
+                  void *cb_cls)
+{
+  struct Plugin *plugin = cls;
+  int ret = GNUNET_SYSERR;
+
+  struct GNUNET_MYSQL_StatementHandle *stmt = plugin->select_state_prefix;
+
+  uint32_t name_len = (uint32_t) strlen (name);
+
+  struct GNUNET_MY_QueryParam params_select[] = {
+    GNUNET_MY_query_param_auto_from_type (channel_key),
+    GNUNET_MY_query_param_string (name),
+    GNUNET_MY_query_param_uint32 (&name_len),
+    GNUNET_MY_query_param_string (name),
+    GNUNET_MY_query_param_end
+  };
+
+  char *name2 = "";
+  void *value_current = NULL;
+  size_t value_size = 0;
+
+  struct GNUNET_MY_ResultSpec results[] = {
+    GNUNET_MY_result_spec_string (&name2),
+    GNUNET_MY_result_spec_variable_size (&value_current, &value_size),
+    GNUNET_MY_result_spec_end
+  };;
+
+  int sql_ret;
+
+  if (GNUNET_OK != GNUNET_MY_exec_prepared (plugin->mc, stmt, params_select))
+  {
+    LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+              "mysql exec_prepared", stmt);
+    return GNUNET_SYSERR;
+  }
+
+  do
+  {
+    sql_ret = GNUNET_MY_extract_result (stmt, results);
+    switch (sql_ret)
+    {
+      case GNUNET_NO:
+        if (ret != GNUNET_YES)
+          ret = GNUNET_NO;
+        break;
+
+      case GNUNET_YES:
+        ret = cb (cb_cls, (const char *) name2, value_current, value_size);
+
+        if (ret != GNUNET_YES)
+          sql_ret = GNUNET_NO;
+        break;
+
+      default:
+        LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                  "mysql extract_result", stmt);
+    }
+  }
+  while (sql_ret == GNUNET_YES);
+
+  if (0 != mysql_stmt_reset (GNUNET_MYSQL_statement_get_stmt (stmt)))
+  {
+    LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+              "mysql_stmt_reset", stmt);
+    return GNUNET_SYSERR;
+  }
+
+  return ret;
+}
+
+
+/**
+ * Retrieve all signed state variables for a channel.
+ *
+ * @see GNUNET_PSYCSTORE_state_get_signed()
+ *
+ * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+ */
+static int
+state_get_signed (void *cls,
+                  const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+                  GNUNET_PSYCSTORE_StateCallback cb, void *cb_cls)
+{
+  struct Plugin *plugin = cls;
+  int ret = GNUNET_SYSERR;
+
+  struct GNUNET_MYSQL_StatementHandle *stmt = plugin->select_state_signed;
+
+  struct GNUNET_MY_QueryParam params_select[] = {
+    GNUNET_MY_query_param_auto_from_type (channel_key),
+    GNUNET_MY_query_param_end
+  };
+
+  int sql_ret;
+
+  char *name = "";
+  void *value_signed = NULL;
+  size_t value_size = 0;
+
+  struct GNUNET_MY_ResultSpec results[] = {
+    GNUNET_MY_result_spec_string (&name),
+    GNUNET_MY_result_spec_variable_size (&value_signed, &value_size),
+    GNUNET_MY_result_spec_end
+  };
+
+  if (GNUNET_OK != GNUNET_MY_exec_prepared (plugin->mc, stmt, params_select))
+  {
+    LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+              "mysql exec_prepared", stmt);
+    return GNUNET_SYSERR;
+  }
+
+  do
+  {
+    sql_ret = GNUNET_MY_extract_result (stmt, results);
+    switch (sql_ret)
+    {
+      case GNUNET_NO:
+        if (ret != GNUNET_YES)
+          ret = GNUNET_NO;
+        break;
+
+      case GNUNET_YES:
+        ret = cb (cb_cls, (const char *) name, value_signed, value_size);
+
+        if (ret != GNUNET_YES)
+            sql_ret = GNUNET_NO;
+        break;
+
+      default:
+         LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+              "mysql extract_result", stmt);
+    }
+  }
+  while (sql_ret == GNUNET_YES);
+
+  if (0 != mysql_stmt_reset (GNUNET_MYSQL_statement_get_stmt (stmt)))
+  {
+    LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+              "mysql_stmt_reset", stmt);
+    return GNUNET_SYSERR;
+  }
+
+  return ret;
+}
+
+
+/**
+ * Entry point for the plugin.
+ *
+ * @param cls The struct GNUNET_CONFIGURATION_Handle.
+ * @return NULL on error, otherwise the plugin context
+ */
+void *
+libgnunet_plugin_psycstore_mysql_init (void *cls)
+{
+  static struct Plugin plugin;
+  const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
+  struct GNUNET_PSYCSTORE_PluginFunctions *api;
+
+  if (NULL != plugin.cfg)
+    return NULL;                /* can only initialize once! */
+  memset (&plugin, 0, sizeof (struct Plugin));
+  plugin.cfg = cfg;
+  if (GNUNET_OK != database_setup (&plugin))
+  {
+    database_shutdown (&plugin);
+    return NULL;
+  }
+  api = GNUNET_new (struct GNUNET_PSYCSTORE_PluginFunctions);
+  api->cls = &plugin;
+  api->membership_store = &mysql_membership_store;
+  api->membership_test = &membership_test;
+  api->fragment_store = &fragment_store;
+  api->message_add_flags = &message_add_flags;
+  api->fragment_get = &fragment_get;
+  api->fragment_get_latest = &fragment_get_latest;
+  api->message_get = &message_get;
+  api->message_get_latest = &message_get_latest;
+  api->message_get_fragment = &message_get_fragment;
+  api->counters_message_get = &counters_message_get;
+  api->counters_state_get = &counters_state_get;
+  api->state_modify_begin = &state_modify_begin;
+  api->state_modify_op = &state_modify_op;
+  api->state_modify_end = &state_modify_end;
+  api->state_sync_begin = &state_sync_begin;
+  api->state_sync_assign = &state_sync_assign;
+  api->state_sync_end = &state_sync_end;
+  api->state_reset = &state_reset;
+  api->state_update_signed = &state_update_signed;
+  api->state_get = &state_get;
+  api->state_get_prefix = &state_get_prefix;
+  api->state_get_signed = &state_get_signed;
+
+  LOG (GNUNET_ERROR_TYPE_INFO, _("Mysql database running\n"));
+  return api;
+}
+
+
+/**
+ * Exit point from the plugin.
+ *
+ * @param cls The plugin context (as returned by "init")
+ * @return Always NULL
+ */
+void *
+libgnunet_plugin_psycstore_mysql_done (void *cls)
+{
+  struct GNUNET_PSYCSTORE_PluginFunctions *api = cls;
+  struct Plugin *plugin = api->cls;
+
+  database_shutdown (plugin);
+  plugin->cfg = NULL;
+  GNUNET_free (api);
+  LOG (GNUNET_ERROR_TYPE_DEBUG, "Mysql plugin is finished\n");
+  return NULL;
+}
+
+/* end of plugin_psycstore_mysql.c */
diff --git a/src/psycstore/plugin_psycstore_postgres.c 
b/src/psycstore/plugin_psycstore_postgres.c
new file mode 100644
index 0000000..33c9960
--- /dev/null
+++ b/src/psycstore/plugin_psycstore_postgres.c
@@ -0,0 +1,1530 @@
+/*
+ * This file is part of GNUnet
+ * Copyright (C) 2016 GNUnet e.V.
+ *
+ * GNUnet is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * GNUnet 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
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+     SPDX-License-Identifier: AGPL3.0-or-later
+ */
+
+/**
+ * @file psycstore/plugin_psycstore_postgres.c
+ * @brief PostgresQL-based psycstore backend
+ * @author Daniel Golle
+ * @author Gabor X Toth
+ * @author Christian Grothoff
+ * @author Christophe Genevey
+ * @author Jeffrey Burdges
+ */
+
+#include "platform.h"
+#include "gnunet_psycstore_plugin.h"
+#include "gnunet_psycstore_service.h"
+#include "gnunet_multicast_service.h"
+#include "gnunet_crypto_lib.h"
+#include "gnunet_psyc_util_lib.h"
+#include "psycstore.h"
+#include "gnunet_pq_lib.h"
+
+/**
+ * After how many ms "busy" should a DB operation fail for good?  A
+ * low value makes sure that we are more responsive to requests
+ * (especially PUTs).  A high value guarantees a higher success rate
+ * (SELECTs in iterate can take several seconds despite LIMIT=1).
+ *
+ * The default value of 1s should ensure that users do not experience
+ * huge latencies while at the same time allowing operations to
+ * succeed with reasonable probability.
+ */
+#define BUSY_TIMEOUT_MS 1000
+
+#define DEBUG_PSYCSTORE GNUNET_EXTRA_LOGGING
+
+#define LOG(kind,...) GNUNET_log_from (kind, "psycstore-postgres", __VA_ARGS__)
+
+enum Transactions {
+  TRANSACTION_NONE = 0,
+  TRANSACTION_STATE_MODIFY,
+  TRANSACTION_STATE_SYNC,
+};
+
+/**
+ * Context for all functions in this plugin.
+ */
+struct Plugin
+{
+
+  const struct GNUNET_CONFIGURATION_Handle *cfg;
+
+  /**
+   * Native Postgres database handle.
+   */
+  PGconn *dbh;
+
+  enum Transactions transaction;
+
+  void *cls;
+};
+
+
+/**
+ * Initialize the database connections and associated
+ * data structures (create tables and indices
+ * as needed as well).
+ *
+ * @param plugin the plugin context (state for this module)
+ * @return #GNUNET_OK on success
+ */
+static int
+database_setup (struct Plugin *plugin)
+{
+  struct GNUNET_PQ_ExecuteStatement es[] = {
+    GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS channels (\n"
+                            " id SERIAL,\n"
+                            " pub_key BYTEA NOT NULL CHECK 
(LENGTH(pub_key)=32),\n"
+                            " max_state_message_id BIGINT,\n"
+                            " state_hash_message_id BIGINT,\n"
+                            " PRIMARY KEY(id)\n"
+                            ")"
+                            "WITH OIDS"),
+    GNUNET_PQ_make_execute ("CREATE UNIQUE INDEX IF NOT EXISTS 
channel_pub_key_idx \n"
+                            " ON channels (pub_key)"),
+    GNUNET_PQ_make_execute ("CREATE OR REPLACE FUNCTION get_chan_id(BYTEA) 
RETURNS INTEGER AS \n"
+                            " 'SELECT id FROM channels WHERE pub_key=$1;' 
LANGUAGE SQL STABLE \n"
+                            "RETURNS NULL ON NULL INPUT"),
+    GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS slaves (\n"
+                            " id SERIAL,\n"
+                            " pub_key BYTEA NOT NULL CHECK 
(LENGTH(pub_key)=32),\n"
+                            " PRIMARY KEY(id)\n"
+                            ")"
+                            "WITH OIDS"),
+    GNUNET_PQ_make_execute ("CREATE UNIQUE INDEX IF NOT EXISTS 
slaves_pub_key_idx \n"
+                            " ON slaves (pub_key)"),
+    GNUNET_PQ_make_execute ("CREATE OR REPLACE FUNCTION get_slave_id(BYTEA) 
RETURNS INTEGER AS \n"
+                            " 'SELECT id FROM slaves WHERE pub_key=$1;' 
LANGUAGE SQL STABLE \n"
+                            "RETURNS NULL ON NULL INPUT"),
+    GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS membership (\n"
+                            "  channel_id BIGINT NOT NULL REFERENCES 
channels(id),\n"
+                            "  slave_id BIGINT NOT NULL REFERENCES 
slaves(id),\n"
+                            "  did_join INT NOT NULL,\n"
+                            "  announced_at BIGINT NOT NULL,\n"
+                            "  effective_since BIGINT NOT NULL,\n"
+                            "  group_generation BIGINT NOT NULL\n"
+                            ")"
+                            "WITH OIDS"),
+    GNUNET_PQ_make_execute ("CREATE INDEX IF NOT EXISTS 
idx_membership_channel_id_slave_id "
+                            "ON membership (channel_id, slave_id)"),
+    /** @todo messages table: add method_name column */
+    GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS messages (\n"
+                            "  channel_id BIGINT NOT NULL REFERENCES 
channels(id),\n"
+                            "  hop_counter INT NOT NULL,\n"
+                            "  signature BYTEA CHECK (LENGTH(signature)=64),\n"
+                            "  purpose BYTEA CHECK (LENGTH(purpose)=8),\n"
+                            "  fragment_id BIGINT NOT NULL,\n"
+                            "  fragment_offset BIGINT NOT NULL,\n"
+                            "  message_id BIGINT NOT NULL,\n"
+                            "  group_generation BIGINT NOT NULL,\n"
+                            "  multicast_flags INT NOT NULL,\n"
+                            "  psycstore_flags INT NOT NULL,\n"
+                            "  data BYTEA,\n"
+                            "  PRIMARY KEY (channel_id, fragment_id),\n"
+                            "  UNIQUE (channel_id, message_id, 
fragment_offset)\n"
+                            ")"
+                            "WITH OIDS"),
+    GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS state (\n"
+                            "  channel_id BIGINT NOT NULL REFERENCES 
channels(id),\n"
+                            "  name TEXT NOT NULL,\n"
+                            "  value_current BYTEA,\n"
+                            "  value_signed BYTEA,\n"
+                            "  PRIMARY KEY (channel_id, name)\n"
+                            ")"
+                            "WITH OIDS"),
+    GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS state_sync (\n"
+                            "  channel_id BIGINT NOT NULL REFERENCES 
channels(id),\n"
+                            "  name TEXT NOT NULL,\n"
+                            "  value BYTEA,\n"
+                            "  PRIMARY KEY (channel_id, name)\n"
+                            ")"
+                            "WITH OIDS"),
+    GNUNET_PQ_EXECUTE_STATEMENT_END
+  };
+
+  /* Open database and precompile statements */
+  plugin->dbh = GNUNET_PQ_connect_with_cfg (plugin->cfg,
+                                            "psycstore-postgres");
+  if (NULL == plugin->dbh)
+    return GNUNET_SYSERR;
+  if (GNUNET_OK !=
+      GNUNET_PQ_exec_statements (plugin->dbh,
+                                 es))
+  {
+    PQfinish (plugin->dbh);
+    plugin->dbh = NULL;
+    return GNUNET_SYSERR;
+  }
+
+  /* Prepare statements */
+  {
+    struct GNUNET_PQ_PreparedStatement ps[] = {
+      GNUNET_PQ_make_prepare ("transaction_begin",
+                              "BEGIN", 0),
+      GNUNET_PQ_make_prepare ("transaction_commit",
+                              "COMMIT", 0),
+      GNUNET_PQ_make_prepare ("transaction_rollback",
+                              "ROLLBACK", 0),
+      GNUNET_PQ_make_prepare ("insert_channel_key",
+                              "INSERT INTO channels (pub_key) VALUES ($1)"
+                              " ON CONFLICT DO NOTHING", 1),
+      GNUNET_PQ_make_prepare ("insert_slave_key",
+                              "INSERT INTO slaves (pub_key) VALUES ($1)"
+                              " ON CONFLICT DO NOTHING", 1),
+      GNUNET_PQ_make_prepare ("insert_membership",
+                              "INSERT INTO membership\n"
+                              " (channel_id, slave_id, did_join, 
announced_at,\n"
+                              "  effective_since, group_generation)\n"
+                              "VALUES (get_chan_id($1),\n"
+                              "        get_slave_id($2),\n"
+                              "        $3, $4, $5, $6)", 6),
+      GNUNET_PQ_make_prepare ("select_membership",
+                              "SELECT did_join FROM membership\n"
+                              "WHERE channel_id = get_chan_id($1)\n"
+                              "      AND slave_id = get_slave_id($2)\n"
+                              "      AND effective_since <= $3 AND did_join = 
1\n"
+                              "ORDER BY announced_at DESC LIMIT 1", 3),
+      GNUNET_PQ_make_prepare ("insert_fragment",
+                              "INSERT INTO messages\n"
+                              " (channel_id, hop_counter, signature, 
purpose,\n"
+                              "  fragment_id, fragment_offset, message_id,\n"
+                              "  group_generation, multicast_flags, 
psycstore_flags, data)\n"
+                              "VALUES (get_chan_id($1),\n"
+                              "        $2, $3, $4, $5, $6, $7, $8, $9, $10, 
$11)"
+                              "ON CONFLICT DO NOTHING", 11),
+      GNUNET_PQ_make_prepare ("update_message_flags",
+                              "UPDATE messages\n"
+                              "SET psycstore_flags = psycstore_flags | $1\n"
+                              "WHERE channel_id = get_chan_id($2) \n"
+                              "      AND message_id = $3 AND fragment_offset = 
0", 3),
+      GNUNET_PQ_make_prepare ("select_fragments",
+                              "SELECT hop_counter, signature, purpose, 
fragment_id,\n"
+                              "       fragment_offset, message_id, 
group_generation,\n"
+                              "       multicast_flags, psycstore_flags, data\n"
+                              "FROM messages\n"
+                              "WHERE channel_id = get_chan_id($1) \n"
+                              "      AND $2 <= fragment_id AND fragment_id <= 
$3", 3),
+      /** @todo select_messages: add method_prefix filter */
+      GNUNET_PQ_make_prepare ("select_messages",
+                              "SELECT hop_counter, signature, purpose, 
fragment_id,\n"
+                              "       fragment_offset, message_id, 
group_generation,\n"
+                              "       multicast_flags, psycstore_flags, data\n"
+                              "FROM messages\n"
+                              "WHERE channel_id = get_chan_id($1) \n"
+                              "      AND $2 <= message_id AND message_id <= 
$3\n"
+                              "LIMIT $4;", 4),
+      /** @todo select_latest_messages: add method_prefix filter */
+      GNUNET_PQ_make_prepare ("select_latest_fragments",
+                              "SELECT  rev.hop_counter AS hop_counter,\n"
+                              "        rev.signature AS signature,\n"
+                              "        rev.purpose AS purpose,\n"
+                              "        rev.fragment_id AS fragment_id,\n"
+                              "        rev.fragment_offset AS 
fragment_offset,\n"
+                              "        rev.message_id AS message_id,\n"
+                              "        rev.group_generation AS 
group_generation,\n"
+                              "        rev.multicast_flags AS 
multicast_flags,\n"
+                              "        rev.psycstore_flags AS 
psycstore_flags,\n"
+                              "        rev.data AS data\n"
+                              " FROM\n"
+                              " (SELECT hop_counter, signature, purpose, 
fragment_id,\n"
+                              "        fragment_offset, message_id, 
group_generation,\n"
+                              "        multicast_flags, psycstore_flags, data 
\n"
+                              "  FROM messages\n"
+                              "  WHERE channel_id = get_chan_id($1) \n"
+                              "  ORDER BY fragment_id DESC\n"
+                              "  LIMIT $2) AS rev\n"
+                              " ORDER BY rev.fragment_id;", 2),
+      GNUNET_PQ_make_prepare ("select_latest_messages",
+                              "SELECT hop_counter, signature, purpose, 
fragment_id,\n"
+                              "       fragment_offset, message_id, 
group_generation,\n"
+                              "        multicast_flags, psycstore_flags, 
data\n"
+                              "FROM messages\n"
+                              "WHERE channel_id = get_chan_id($1)\n"
+                              "      AND message_id IN\n"
+                              "      (SELECT message_id\n"
+                              "       FROM messages\n"
+                              "       WHERE channel_id = get_chan_id($2) \n"
+                              "       GROUP BY message_id\n"
+                              "       ORDER BY message_id\n"
+                              "       DESC LIMIT $3)\n"
+                              "ORDER BY fragment_id", 3),
+      GNUNET_PQ_make_prepare ("select_message_fragment",
+                              "SELECT hop_counter, signature, purpose, 
fragment_id,\n"
+                              "       fragment_offset, message_id, 
group_generation,\n"
+                              "       multicast_flags, psycstore_flags, data\n"
+                              "FROM messages\n"
+                              "WHERE channel_id = get_chan_id($1) \n"
+                              "      AND message_id = $2 AND fragment_offset = 
$3", 3),
+      GNUNET_PQ_make_prepare ("select_counters_message",
+                              "SELECT fragment_id, message_id, 
group_generation\n"
+                              "FROM messages\n"
+                              "WHERE channel_id = get_chan_id($1)\n"
+                              "ORDER BY fragment_id DESC LIMIT 1", 1),
+      GNUNET_PQ_make_prepare ("select_counters_state",
+                              "SELECT max_state_message_id\n"
+                              "FROM channels\n"
+                              "WHERE pub_key = $1 AND max_state_message_id IS 
NOT NULL", 1),
+      GNUNET_PQ_make_prepare ("update_max_state_message_id",
+                              "UPDATE channels\n"
+                              "SET max_state_message_id = $1\n"
+                              "WHERE pub_key = $2", 2),
+
+      GNUNET_PQ_make_prepare ("update_state_hash_message_id",
+                              "UPDATE channels\n"
+                              "SET state_hash_message_id = $1\n"
+                              "WHERE pub_key = $2", 2),
+      GNUNET_PQ_make_prepare ("insert_state_current",
+                              "INSERT INTO state\n"
+                              "  (channel_id, name, value_current, 
value_signed)\n"
+                              "SELECT new.channel_id, new.name,\n"
+                              "       new.value_current, old.value_signed\n"
+                              "FROM (SELECT get_chan_id($1) AS channel_id,\n"
+                              "             $2::TEXT AS name, $3::BYTEA AS 
value_current) AS new\n"
+                              "LEFT JOIN (SELECT channel_id, name, 
value_signed\n"
+                              "           FROM state) AS old\n"
+                              "ON new.channel_id = old.channel_id AND new.name 
= old.name\n"
+                              "ON CONFLICT (channel_id, name)\n"
+                              "   DO UPDATE SET value_current = 
EXCLUDED.value_current,\n"
+                              "                 value_signed = 
EXCLUDED.value_signed", 3),
+      GNUNET_PQ_make_prepare ("delete_state_empty",
+                              "DELETE FROM state\n"
+                              "WHERE channel_id = (SELECT id FROM channels 
WHERE pub_key = $1)\n"
+                              "      AND (value_current IS NULL OR 
length(value_current) = 0)\n"
+                              "      AND (value_signed IS NULL OR 
length(value_signed) = 0)", 1),
+      GNUNET_PQ_make_prepare ("update_state_signed",
+                              "UPDATE state\n"
+                              "SET value_signed = value_current\n"
+                              "WHERE channel_id = get_chan_id($1) ", 1),
+      GNUNET_PQ_make_prepare ("delete_state",
+                              "DELETE FROM state\n"
+                              "WHERE channel_id = get_chan_id($1) ", 1),
+      GNUNET_PQ_make_prepare ("insert_state_sync",
+                              "INSERT INTO state_sync (channel_id, name, 
value)\n"
+                              "VALUES (get_chan_id($1), $2, $3)", 3),
+      GNUNET_PQ_make_prepare ("insert_state_from_sync",
+                              "INSERT INTO state\n"
+                              " (channel_id, name, value_current, 
value_signed)\n"
+                              "SELECT channel_id, name, value, value\n"
+                              "FROM state_sync\n"
+                              "WHERE channel_id = get_chan_id($1)", 1),
+      GNUNET_PQ_make_prepare ("delete_state_sync",
+                              "DELETE FROM state_sync\n"
+                              "WHERE channel_id = get_chan_id($1)", 1),
+      GNUNET_PQ_make_prepare ("select_state_one",
+                              "SELECT value_current\n"
+                              "FROM state\n"
+                              "WHERE channel_id = get_chan_id($1)\n"
+                              "      AND name = $2", 2),
+      GNUNET_PQ_make_prepare ("select_state_prefix",
+                              "SELECT name, value_current\n"
+                              "FROM state\n"
+                              "WHERE channel_id = get_chan_id($1)\n"
+                              "      AND (name = $2 OR substr(name, 1, $3) = 
$4)", 4),
+      GNUNET_PQ_make_prepare ("select_state_signed",
+                              "SELECT name, value_signed\n"
+                              "FROM state\n"
+                              "WHERE channel_id = get_chan_id($1)\n"
+                              "      AND value_signed IS NOT NULL", 1),
+      GNUNET_PQ_PREPARED_STATEMENT_END
+    };
+
+    if (GNUNET_OK !=
+        GNUNET_PQ_prepare_statements (plugin->dbh,
+                                      ps))
+    {
+      PQfinish (plugin->dbh);
+      plugin->dbh = NULL;
+      return GNUNET_SYSERR;
+    }
+  }
+
+  return GNUNET_OK;
+}
+
+
+/**
+ * Shutdown database connection and associate data
+ * structures.
+ * @param plugin the plugin context (state for this module)
+ */
+static void
+database_shutdown (struct Plugin *plugin)
+{
+  PQfinish (plugin->dbh);
+  plugin->dbh = NULL;
+}
+
+
+/**
+ * Execute a prepared statement with a @a channel_key argument.
+ *
+ * @param plugin Plugin handle.
+ * @param stmt Statement to execute.
+ * @param channel_key Public key of the channel.
+ *
+ * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+ */
+static int
+exec_channel (struct Plugin *plugin, const char *stmt,
+              const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key)
+{
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_auto_from_type (channel_key),
+    GNUNET_PQ_query_param_end
+  };
+
+  if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
+      GNUNET_PQ_eval_prepared_non_select (plugin->dbh, stmt, params))
+    return GNUNET_SYSERR;
+
+  return GNUNET_OK;
+}
+
+
+/**
+ * Begin a transaction.
+ */
+static int
+transaction_begin (struct Plugin *plugin, enum Transactions transaction)
+{
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_end
+  };
+
+  if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
+      GNUNET_PQ_eval_prepared_non_select (plugin->dbh, "transaction_begin", 
params))
+    return GNUNET_SYSERR;
+
+  plugin->transaction = transaction;
+  return GNUNET_OK;
+}
+
+
+/**
+ * Commit current transaction.
+ */
+static int
+transaction_commit (struct Plugin *plugin)
+{
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_end
+  };
+
+  if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
+      GNUNET_PQ_eval_prepared_non_select (plugin->dbh, "transaction_commit", 
params))
+    return GNUNET_SYSERR;
+
+  plugin->transaction = TRANSACTION_NONE;
+  return GNUNET_OK;
+}
+
+
+/**
+ * Roll back current transaction.
+ */
+static int
+transaction_rollback (struct Plugin *plugin)
+{
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_end
+  };
+
+  if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
+      GNUNET_PQ_eval_prepared_non_select (plugin->dbh, "transaction_rollback", 
params))
+    return GNUNET_SYSERR;
+
+  plugin->transaction = TRANSACTION_NONE;
+  return GNUNET_OK;
+}
+
+
+static int
+channel_key_store (struct Plugin *plugin,
+                   const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key)
+{
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_auto_from_type (channel_key),
+    GNUNET_PQ_query_param_end
+  };
+
+  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+      GNUNET_PQ_eval_prepared_non_select (plugin->dbh,
+                                         "insert_channel_key",
+                                         params))
+    return GNUNET_SYSERR;
+
+  return GNUNET_OK;
+}
+
+
+static int
+slave_key_store (struct Plugin *plugin,
+                 const struct GNUNET_CRYPTO_EcdsaPublicKey *slave_key)
+{
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_auto_from_type (slave_key),
+    GNUNET_PQ_query_param_end
+  };
+
+  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+      GNUNET_PQ_eval_prepared_non_select (plugin->dbh, "insert_slave_key", 
params))
+    return GNUNET_SYSERR;
+
+  return GNUNET_OK;
+}
+
+
+/**
+ * Store join/leave events for a PSYC channel in order to be able to answer
+ * membership test queries later.
+ *
+ * @see GNUNET_PSYCSTORE_membership_store()
+ *
+ * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+ */
+static int
+postgres_membership_store (void *cls,
+                           const struct GNUNET_CRYPTO_EddsaPublicKey 
*channel_key,
+                           const struct GNUNET_CRYPTO_EcdsaPublicKey 
*slave_key,
+                           int did_join,
+                           uint64_t announced_at,
+                           uint64_t effective_since,
+                           uint64_t group_generation)
+{
+  struct Plugin *plugin = cls;
+  uint32_t idid_join = (uint32_t) did_join;
+
+  GNUNET_assert (TRANSACTION_NONE == plugin->transaction);
+
+  if ( (announced_at > INT64_MAX) ||
+       (effective_since > INT64_MAX) ||
+       (group_generation > INT64_MAX) )
+  {
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+
+  if ( (GNUNET_OK !=
+       channel_key_store (plugin, channel_key)) ||
+       (GNUNET_OK !=
+       slave_key_store (plugin, slave_key)) )
+    return GNUNET_SYSERR;
+
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_auto_from_type (channel_key),
+    GNUNET_PQ_query_param_auto_from_type (slave_key),
+    GNUNET_PQ_query_param_uint32 (&idid_join),
+    GNUNET_PQ_query_param_uint64 (&announced_at),
+    GNUNET_PQ_query_param_uint64 (&effective_since),
+    GNUNET_PQ_query_param_uint64 (&group_generation),
+    GNUNET_PQ_query_param_end
+  };
+
+  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+      GNUNET_PQ_eval_prepared_non_select (plugin->dbh,
+                                         "insert_membership",
+                                         params))
+    return GNUNET_SYSERR;
+
+  return GNUNET_OK;
+}
+
+/**
+ * Test if a member was admitted to the channel at the given message ID.
+ *
+ * @see GNUNET_PSYCSTORE_membership_test()
+ *
+ * @return #GNUNET_YES if the member was admitted, #GNUNET_NO if not,
+ *         #GNUNET_SYSERR if there was en error.
+ */
+static int
+membership_test (void *cls,
+                 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+                 const struct GNUNET_CRYPTO_EcdsaPublicKey *slave_key,
+                 uint64_t message_id)
+{
+  struct Plugin *plugin = cls;
+
+  uint32_t did_join = 0;
+
+  struct GNUNET_PQ_QueryParam params_select[] = {
+    GNUNET_PQ_query_param_auto_from_type (channel_key),
+    GNUNET_PQ_query_param_auto_from_type (slave_key),
+    GNUNET_PQ_query_param_uint64 (&message_id),
+    GNUNET_PQ_query_param_end
+  };
+
+  struct GNUNET_PQ_ResultSpec results_select[] = {
+    GNUNET_PQ_result_spec_uint32 ("did_join", &did_join),
+    GNUNET_PQ_result_spec_end
+  };
+
+  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+      GNUNET_PQ_eval_prepared_singleton_select (plugin->dbh, 
"select_membership", 
+                                                params_select, results_select))
+     return GNUNET_SYSERR;
+
+  return GNUNET_OK;
+}
+
+/**
+ * Store a message fragment sent to a channel.
+ *
+ * @see GNUNET_PSYCSTORE_fragment_store()
+ *
+ * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+ */
+static int
+fragment_store (void *cls,
+                const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+                const struct GNUNET_MULTICAST_MessageHeader *msg,
+                uint32_t psycstore_flags)
+{
+  struct Plugin *plugin = cls;
+
+  GNUNET_assert (TRANSACTION_NONE == plugin->transaction);
+
+  uint64_t fragment_id = GNUNET_ntohll (msg->fragment_id);
+
+  uint64_t fragment_offset = GNUNET_ntohll (msg->fragment_offset);
+  uint64_t message_id = GNUNET_ntohll (msg->message_id);
+  uint64_t group_generation = GNUNET_ntohll (msg->group_generation);
+
+  uint32_t hop_counter = ntohl(msg->hop_counter);
+  uint32_t flags = ntohl(msg->flags);
+
+  if (fragment_id > INT64_MAX || fragment_offset > INT64_MAX ||
+      message_id > INT64_MAX || group_generation > INT64_MAX)
+  {
+    LOG(GNUNET_ERROR_TYPE_ERROR,
+         "Tried to store fragment with a field > INT64_MAX: "
+         "%lu, %lu, %lu, %lu\n", fragment_id, fragment_offset,
+         message_id, group_generation);
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+
+  if (GNUNET_OK != channel_key_store (plugin, channel_key))
+    return GNUNET_SYSERR;
+
+  struct GNUNET_PQ_QueryParam params_insert[] = {
+    GNUNET_PQ_query_param_auto_from_type (channel_key),
+    GNUNET_PQ_query_param_uint32 (&hop_counter),
+    GNUNET_PQ_query_param_auto_from_type (&msg->signature),
+    GNUNET_PQ_query_param_auto_from_type (&msg->purpose),
+    GNUNET_PQ_query_param_uint64 (&fragment_id),
+    GNUNET_PQ_query_param_uint64 (&fragment_offset),
+    GNUNET_PQ_query_param_uint64 (&message_id),
+    GNUNET_PQ_query_param_uint64 (&group_generation),
+    GNUNET_PQ_query_param_uint32 (&flags),
+    GNUNET_PQ_query_param_uint32 (&psycstore_flags),
+    GNUNET_PQ_query_param_fixed_size (&msg[1], ntohs (msg->header.size) - 
sizeof (*msg)),
+    GNUNET_PQ_query_param_end
+  };
+
+  if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
+      GNUNET_PQ_eval_prepared_non_select (plugin->dbh, "insert_fragment", 
params_insert))
+    return GNUNET_SYSERR;
+
+  return GNUNET_OK;
+}
+
+/**
+ * Set additional flags for a given message.
+ *
+ * They are OR'd with any existing flags set.
+ *
+ * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+ */
+static int
+message_add_flags (void *cls,
+                   const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+                   uint64_t message_id,
+                   uint32_t psycstore_flags)
+{
+  struct Plugin *plugin = cls;
+
+  struct GNUNET_PQ_QueryParam params_update[] = {
+    GNUNET_PQ_query_param_uint32 (&psycstore_flags),
+    GNUNET_PQ_query_param_auto_from_type (channel_key),
+    GNUNET_PQ_query_param_uint64 (&message_id),
+    GNUNET_PQ_query_param_end
+  };
+
+  if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
+      GNUNET_PQ_eval_prepared_non_select (plugin->dbh, "update_message_flags", 
params_update))
+    return GNUNET_SYSERR;
+
+  return GNUNET_OK;
+}
+
+
+/**
+ * Closure for #fragment_rows.
+ */
+struct FragmentRowsContext {
+  GNUNET_PSYCSTORE_FragmentCallback cb;
+  void *cb_cls;
+
+  uint64_t *returned_fragments;
+
+  /* I preserved this but I do not see the point since
+   * it cannot stop the loop early and gets overwritten ?? */
+  int ret;
+};
+
+
+/**
+ * Callback that retrieves the results of a SELECT statement
+ * reading form the messages table.
+ *
+ * Only passed to GNUNET_PQ_eval_prepared_multi_select and
+ * has type GNUNET_PQ_PostgresResultHandler.
+ *
+ * @param cls closure
+ * @param result the postgres result
+ * @param num_result the number of results in @a result
+ */
+void fragment_rows (void *cls,
+                    PGresult *res,
+                    unsigned int num_results)
+{
+  struct FragmentRowsContext *c = cls;
+
+  for (unsigned int i=0;i<num_results;i++)
+  {
+    uint32_t hop_counter;
+    void *signature = NULL;
+    void *purpose = NULL;
+    size_t signature_size;
+    size_t purpose_size;
+    uint64_t fragment_id;
+    uint64_t fragment_offset;
+    uint64_t message_id;
+    uint64_t group_generation;
+    uint32_t flags;
+    void *buf;
+    size_t buf_size;
+    uint32_t msg_flags;
+    struct GNUNET_PQ_ResultSpec results[] = {
+      GNUNET_PQ_result_spec_uint32 ("hop_counter", &hop_counter),
+      GNUNET_PQ_result_spec_variable_size ("signature", &signature, 
&signature_size),
+      GNUNET_PQ_result_spec_variable_size ("purpose", &purpose, &purpose_size),
+      GNUNET_PQ_result_spec_uint64 ("fragment_id", &fragment_id),
+      GNUNET_PQ_result_spec_uint64 ("fragment_offset", &fragment_offset),
+      GNUNET_PQ_result_spec_uint64 ("message_id", &message_id),
+      GNUNET_PQ_result_spec_uint64 ("group_generation", &group_generation),
+      GNUNET_PQ_result_spec_uint32 ("multicast_flags", &msg_flags),
+      GNUNET_PQ_result_spec_uint32 ("psycstore_flags", &flags),
+      GNUNET_PQ_result_spec_variable_size ("data", &buf, &buf_size),
+      GNUNET_PQ_result_spec_end
+    };
+    struct GNUNET_MULTICAST_MessageHeader *mp;
+
+    if (GNUNET_YES != GNUNET_PQ_extract_result (res, results, i))
+    {
+      GNUNET_PQ_cleanup_result(results);  /* missing previously, a memory 
leak?? */
+      break;  /* nothing more?? */
+    }
+
+    mp = GNUNET_malloc (sizeof (*mp) + buf_size);
+
+    mp->header.size = htons (sizeof (*mp) + buf_size);
+    mp->header.type = htons (GNUNET_MESSAGE_TYPE_MULTICAST_MESSAGE);
+    mp->hop_counter = htonl (hop_counter);
+    GNUNET_memcpy (&mp->signature,
+                   signature, signature_size);
+    GNUNET_memcpy (&mp->purpose,
+                   purpose, purpose_size);
+    mp->fragment_id = GNUNET_htonll (fragment_id);
+    mp->fragment_offset = GNUNET_htonll (fragment_offset);
+    mp->message_id = GNUNET_htonll (message_id);
+    mp->group_generation = GNUNET_htonll (group_generation);
+    mp->flags = htonl(msg_flags);
+
+    GNUNET_memcpy (&mp[1],
+                   buf, buf_size);
+    GNUNET_PQ_cleanup_result(results);
+    c->ret = c->cb (c->cb_cls, mp, (enum GNUNET_PSYCSTORE_MessageFlags) flags);
+    if (NULL != c->returned_fragments)
+      (*c->returned_fragments)++;
+  }
+}
+
+
+static int
+fragment_select (struct Plugin *plugin,
+                 const char *stmt,
+                 struct GNUNET_PQ_QueryParam *params,
+                 uint64_t *returned_fragments,
+                 GNUNET_PSYCSTORE_FragmentCallback cb,
+                 void *cb_cls)
+{
+  /* Stack based closure */
+  struct FragmentRowsContext frc = {
+    .cb = cb,
+    .cb_cls = cb_cls,
+    .returned_fragments = returned_fragments,
+    .ret = GNUNET_SYSERR
+  };
+
+  if (0 > GNUNET_PQ_eval_prepared_multi_select (plugin->dbh,
+                                                stmt, params,
+                                                &fragment_rows, &frc))
+    return GNUNET_SYSERR;
+  return frc.ret;  /* GNUNET_OK ?? */
+}
+
+/**
+ * Retrieve a message fragment range by fragment ID.
+ *
+ * @see GNUNET_PSYCSTORE_fragment_get()
+ *
+ * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+ */
+static int
+fragment_get (void *cls,
+              const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+              uint64_t first_fragment_id,
+              uint64_t last_fragment_id,
+              uint64_t *returned_fragments,
+              GNUNET_PSYCSTORE_FragmentCallback cb,
+              void *cb_cls)
+{
+  struct Plugin *plugin = cls;
+  struct GNUNET_PQ_QueryParam params_select[] = {
+    GNUNET_PQ_query_param_auto_from_type (channel_key),
+    GNUNET_PQ_query_param_uint64 (&first_fragment_id),
+    GNUNET_PQ_query_param_uint64 (&last_fragment_id),
+    GNUNET_PQ_query_param_end
+  };
+
+  *returned_fragments = 0;
+  return fragment_select (plugin,
+                          "select_fragments",
+                          params_select,
+                          returned_fragments,
+                          cb, cb_cls);
+}
+
+
+/**
+ * Retrieve a message fragment range by fragment ID.
+ *
+ * @see GNUNET_PSYCSTORE_fragment_get_latest()
+ *
+ * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+ */
+static int
+fragment_get_latest (void *cls,
+                     const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+                     uint64_t fragment_limit,
+                     uint64_t *returned_fragments,
+                     GNUNET_PSYCSTORE_FragmentCallback cb,
+                     void *cb_cls)
+{
+  struct Plugin *plugin = cls;
+
+  *returned_fragments = 0;
+
+  struct GNUNET_PQ_QueryParam params_select[] = {
+    GNUNET_PQ_query_param_auto_from_type (channel_key),
+    GNUNET_PQ_query_param_uint64 (&fragment_limit),
+    GNUNET_PQ_query_param_end
+  };
+
+  return fragment_select (plugin,
+                          "select_latest_fragments",
+                          params_select,
+                          returned_fragments,
+                          cb, cb_cls);
+}
+
+
+/**
+ * Retrieve all fragments of a message ID range.
+ *
+ * @see GNUNET_PSYCSTORE_message_get()
+ *
+ * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+ */
+static int
+message_get (void *cls,
+             const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+             uint64_t first_message_id,
+             uint64_t last_message_id,
+             uint64_t fragment_limit,
+             uint64_t *returned_fragments,
+             GNUNET_PSYCSTORE_FragmentCallback cb,
+             void *cb_cls)
+{
+  struct Plugin *plugin = cls;
+  struct GNUNET_PQ_QueryParam params_select[] = {
+    GNUNET_PQ_query_param_auto_from_type (channel_key),
+    GNUNET_PQ_query_param_uint64 (&first_message_id),
+    GNUNET_PQ_query_param_uint64 (&last_message_id),
+    GNUNET_PQ_query_param_uint64 (&fragment_limit),
+    GNUNET_PQ_query_param_end
+  };
+
+  if (0 == fragment_limit)
+    fragment_limit = INT64_MAX;
+  *returned_fragments = 0;
+  return fragment_select (plugin,
+                          "select_messages",
+                          params_select,
+                          returned_fragments,
+                          cb, cb_cls);
+}
+
+
+/**
+ * Retrieve all fragments of the latest messages.
+ *
+ * @see GNUNET_PSYCSTORE_message_get_latest()
+ *
+ * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+ */
+static int
+message_get_latest (void *cls,
+                    const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+                    uint64_t message_limit,
+                    uint64_t *returned_fragments,
+                    GNUNET_PSYCSTORE_FragmentCallback cb,
+                    void *cb_cls)
+{
+  struct Plugin *plugin = cls;
+  struct GNUNET_PQ_QueryParam params_select[] = {
+    GNUNET_PQ_query_param_auto_from_type (channel_key),
+    GNUNET_PQ_query_param_auto_from_type (channel_key),
+    GNUNET_PQ_query_param_uint64 (&message_limit),
+    GNUNET_PQ_query_param_end
+  };
+
+  *returned_fragments = 0;
+  return fragment_select (plugin,
+                          "select_latest_messages",
+                          params_select,
+                          returned_fragments,
+                          cb, cb_cls);
+}
+
+
+/**
+ * Retrieve a fragment of message specified by its message ID and fragment
+ * offset.
+ *
+ * @see GNUNET_PSYCSTORE_message_get_fragment()
+ *
+ * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+ */
+static int
+message_get_fragment (void *cls,
+                      const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+                      uint64_t message_id,
+                      uint64_t fragment_offset,
+                      GNUNET_PSYCSTORE_FragmentCallback cb,
+                      void *cb_cls)
+{
+  struct Plugin *plugin = cls;
+  const char *stmt = "select_message_fragment";
+
+  struct GNUNET_PQ_QueryParam params_select[] = {
+    GNUNET_PQ_query_param_auto_from_type (channel_key),
+    GNUNET_PQ_query_param_uint64 (&message_id),
+    GNUNET_PQ_query_param_uint64 (&fragment_offset),
+    GNUNET_PQ_query_param_end
+  };
+
+  /* Stack based closure */
+  struct FragmentRowsContext frc = {
+    .cb = cb,
+    .cb_cls = cb_cls,
+    .returned_fragments = NULL,
+    .ret = GNUNET_SYSERR
+  };
+
+  if (0 > GNUNET_PQ_eval_prepared_multi_select (plugin->dbh,
+                                                stmt, params_select,
+                                                &fragment_rows, &frc))
+    return GNUNET_SYSERR;
+  return frc.ret;  /* GNUNET_OK ?? */
+}
+
+/**
+ * Retrieve the max. values of message counters for a channel.
+ *
+ * @see GNUNET_PSYCSTORE_counters_get()
+ *
+ * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+ */
+static int
+counters_message_get (void *cls,
+                      const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+                      uint64_t *max_fragment_id,
+                      uint64_t *max_message_id,
+                      uint64_t *max_group_generation)
+{
+  struct Plugin *plugin = cls;
+
+  const char *stmt = "select_counters_message";
+
+  struct GNUNET_PQ_QueryParam params_select[] = {
+    GNUNET_PQ_query_param_auto_from_type (channel_key),
+    GNUNET_PQ_query_param_end
+  };
+
+  struct GNUNET_PQ_ResultSpec results_select[] = {
+    GNUNET_PQ_result_spec_uint64 ("fragment_id", max_fragment_id),
+    GNUNET_PQ_result_spec_uint64 ("message_id", max_message_id),
+    GNUNET_PQ_result_spec_uint64 ("group_generation", max_group_generation),
+    GNUNET_PQ_result_spec_end
+  };
+
+  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+      GNUNET_PQ_eval_prepared_singleton_select (plugin->dbh, stmt, 
+                                                params_select, results_select))
+     return GNUNET_SYSERR;
+
+  return GNUNET_OK;
+}
+
+/**
+ * Retrieve the max. values of state counters for a channel.
+ *
+ * @see GNUNET_PSYCSTORE_counters_get()
+ *
+ * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+ */
+static int
+counters_state_get (void *cls,
+                    const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+                    uint64_t *max_state_message_id)
+{
+  struct Plugin *plugin = cls;
+
+  const char *stmt = "select_counters_state";
+
+  struct GNUNET_PQ_QueryParam params_select[] = {
+    GNUNET_PQ_query_param_auto_from_type (channel_key),
+    GNUNET_PQ_query_param_end
+  };
+
+  struct GNUNET_PQ_ResultSpec results_select[] = {
+    GNUNET_PQ_result_spec_uint64 ("max_state_message_id", 
max_state_message_id),
+    GNUNET_PQ_result_spec_end
+  };
+
+  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+      GNUNET_PQ_eval_prepared_singleton_select (plugin->dbh, stmt, 
+                                                params_select, results_select))
+     return GNUNET_SYSERR;
+
+  return GNUNET_OK;
+}
+
+
+/**
+ * Assign a value to a state variable.
+ *
+ * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+ */
+static int
+state_assign (struct Plugin *plugin, const char *stmt,
+              const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+              const char *name, const void *value, size_t value_size)
+{
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_auto_from_type (channel_key),
+    GNUNET_PQ_query_param_string (name),
+    GNUNET_PQ_query_param_fixed_size (value, value_size),
+    GNUNET_PQ_query_param_end
+  };
+
+  if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
+      GNUNET_PQ_eval_prepared_non_select (plugin->dbh, stmt, params))
+    return GNUNET_SYSERR;
+
+  return GNUNET_OK;
+}
+
+
+static int
+update_message_id (struct Plugin *plugin,
+                   const char *stmt,
+                   const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+                   uint64_t message_id)
+{
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_uint64 (&message_id),
+    GNUNET_PQ_query_param_auto_from_type (channel_key),
+    GNUNET_PQ_query_param_end
+  };
+
+  if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
+      GNUNET_PQ_eval_prepared_non_select (plugin->dbh, stmt, params))
+    return GNUNET_SYSERR;
+
+  return GNUNET_OK;
+}
+
+
+/**
+ * Begin modifying current state.
+ */
+static int
+state_modify_begin (void *cls,
+                    const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+                    uint64_t message_id, uint64_t state_delta)
+{
+  struct Plugin *plugin = cls;
+
+  if (state_delta > 0)
+  {
+    /**
+     * We can only apply state modifiers in the current message if modifiers in
+     * the previous stateful message (message_id - state_delta) were already
+     * applied.
+     */
+
+    uint64_t max_state_message_id = 0;
+    int ret = counters_state_get (plugin, channel_key, &max_state_message_id);
+    switch (ret)
+    {
+    case GNUNET_OK:
+    case GNUNET_NO: // no state yet
+      ret = GNUNET_OK;
+      break;
+
+    default:
+      return ret;
+    }
+
+    if (max_state_message_id < message_id - state_delta)
+      return GNUNET_NO; /* some stateful messages not yet applied */
+    else if (message_id - state_delta < max_state_message_id)
+      return GNUNET_NO; /* changes already applied */
+  }
+
+  if (TRANSACTION_NONE != plugin->transaction)
+  {
+    /** @todo FIXME: wait for other transaction to finish  */
+    return GNUNET_SYSERR;
+  }
+  return transaction_begin (plugin, TRANSACTION_STATE_MODIFY);
+}
+
+
+/**
+ * Set the current value of state variable.
+ *
+ * @see GNUNET_PSYCSTORE_state_modify()
+ *
+ * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+ */
+static int
+state_modify_op (void *cls,
+                 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+                 enum GNUNET_PSYC_Operator op,
+                 const char *name, const void *value, size_t value_size)
+{
+  struct Plugin *plugin = cls;
+  GNUNET_assert (TRANSACTION_STATE_MODIFY == plugin->transaction);
+
+  switch (op)
+  {
+  case GNUNET_PSYC_OP_ASSIGN:
+    return state_assign (plugin, "insert_state_current",
+                         channel_key, name, value, value_size);
+
+  default: /** @todo implement more state operations */
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+}
+
+
+/**
+ * End modifying current state.
+ */
+static int
+state_modify_end (void *cls,
+                  const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+                  uint64_t message_id)
+{
+  struct Plugin *plugin = cls;
+  GNUNET_assert (TRANSACTION_STATE_MODIFY == plugin->transaction);
+
+  return
+    GNUNET_OK == exec_channel (plugin, "delete_state_empty", channel_key)
+    && GNUNET_OK == update_message_id (plugin,
+                                       "update_max_state_message_id",
+                                       channel_key, message_id)
+    && GNUNET_OK == transaction_commit (plugin)
+    ? GNUNET_OK : GNUNET_SYSERR;
+}
+
+
+/**
+ * Begin state synchronization.
+ */
+static int
+state_sync_begin (void *cls,
+                  const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key)
+{
+  struct Plugin *plugin = cls;
+  return exec_channel (plugin, "delete_state_sync", channel_key);
+}
+
+
+/**
+ * Assign current value of a state variable.
+ *
+ * @see GNUNET_PSYCSTORE_state_modify()
+ *
+ * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+ */
+static int
+state_sync_assign (void *cls,
+                const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+                const char *name, const void *value, size_t value_size)
+{
+  struct Plugin *plugin = cls;
+  return state_assign (plugin, "insert_state_sync",
+                       channel_key, name, value, value_size);
+}
+
+
+/**
+ * End modifying current state.
+ */
+static int
+state_sync_end (void *cls,
+                const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+                uint64_t max_state_message_id,
+                uint64_t state_hash_message_id)
+{
+  struct Plugin *plugin = cls;
+  int ret = GNUNET_SYSERR;
+
+  if (TRANSACTION_NONE != plugin->transaction)
+  {
+    /** @todo FIXME: wait for other transaction to finish  */
+    return GNUNET_SYSERR;
+  }
+
+  GNUNET_OK == transaction_begin (plugin, TRANSACTION_STATE_SYNC)
+    && GNUNET_OK == exec_channel (plugin, "delete_state", channel_key)
+    && GNUNET_OK == exec_channel (plugin, "insert_state_from_sync",
+                                  channel_key)
+    && GNUNET_OK == exec_channel (plugin, "delete_state_sync",
+                                  channel_key)
+    && GNUNET_OK == update_message_id (plugin,
+                                       "update_state_hash_message_id",
+                                       channel_key, state_hash_message_id)
+    && GNUNET_OK == update_message_id (plugin,
+                                       "update_max_state_message_id",
+                                       channel_key, max_state_message_id)
+    && GNUNET_OK == transaction_commit (plugin)
+    ? ret = GNUNET_OK
+    : transaction_rollback (plugin);
+  return ret;
+}
+
+
+/**
+ * Delete the whole state.
+ *
+ * @see GNUNET_PSYCSTORE_state_reset()
+ *
+ * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+ */
+static int
+state_reset (void *cls, const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key)
+{
+  struct Plugin *plugin = cls;
+  return exec_channel (plugin, "delete_state", channel_key);
+}
+
+
+/**
+ * Update signed values of state variables in the state store.
+ *
+ * @see GNUNET_PSYCSTORE_state_hash_update()
+ *
+ * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+ */
+static int
+state_update_signed (void *cls,
+                     const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key)
+{
+  struct Plugin *plugin = cls;
+  return exec_channel (plugin, "update_state_signed", channel_key);
+}
+
+
+/**
+ * Retrieve a state variable by name.
+ *
+ * @see GNUNET_PSYCSTORE_state_get()
+ *
+ * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+ */
+static int
+state_get (void *cls, const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+           const char *name, GNUNET_PSYCSTORE_StateCallback cb, void *cb_cls)
+{
+  struct Plugin *plugin = cls;
+
+  const char *stmt = "select_state_one";
+
+  struct GNUNET_PQ_QueryParam params_select[] = {
+    GNUNET_PQ_query_param_auto_from_type (channel_key),
+    GNUNET_PQ_query_param_string (name),
+    GNUNET_PQ_query_param_end
+  };
+
+  void *value_current = NULL;
+  size_t value_size = 0;
+
+  struct GNUNET_PQ_ResultSpec results_select[] = {
+    GNUNET_PQ_result_spec_variable_size ("value_current", &value_current, 
&value_size),
+    GNUNET_PQ_result_spec_end
+  };
+
+  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+      GNUNET_PQ_eval_prepared_singleton_select (plugin->dbh, stmt, 
+                                                params_select, results_select))
+     return GNUNET_SYSERR;
+
+  return cb (cb_cls, name, value_current,
+            value_size);
+}
+
+
+
+/**
+ * Closure for #get_state_cb.
+ */
+struct GetStateContext {
+  const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key;
+  // const char *name,
+  GNUNET_PSYCSTORE_StateCallback cb;
+  void *cb_cls;
+
+  const char *value_id;
+
+  /* I preserved this but I do not see the point since
+   * it cannot stop the loop early and gets overwritten ?? */
+  int ret;
+};
+
+
+/**
+ * Callback that retrieves the results of a SELECT statement
+ * reading form the state table.
+ *
+ * Only passed to GNUNET_PQ_eval_prepared_multi_select and
+ * has type GNUNET_PQ_PostgresResultHandler.
+ *
+ * @param cls closure
+ * @param result the postgres result
+ * @param num_result the number of results in @a result
+ */
+static void 
+get_state_cb (void *cls,
+                PGresult *res,
+                unsigned int num_results)
+{
+  struct GetStateContext *c = cls;
+
+  for (unsigned int i=0;i<num_results;i++)
+  {
+    char *name = "";
+    void *value = NULL;
+    size_t value_size = 0;
+
+    struct GNUNET_PQ_ResultSpec results[] = {
+      GNUNET_PQ_result_spec_string ("name", &name),
+      GNUNET_PQ_result_spec_variable_size (c->value_id, &value, &value_size),
+      GNUNET_PQ_result_spec_end
+    };
+
+    if (GNUNET_YES != GNUNET_PQ_extract_result (res, results, i))
+    {
+      GNUNET_PQ_cleanup_result(results);  /* previously invoked via PQclear?? 
*/
+      break;  /* nothing more?? */
+    }
+
+    c->ret = c->cb (c->cb_cls, (const char *) name, value, value_size);
+    GNUNET_PQ_cleanup_result(results);
+  }
+}
+
+/**
+ * Retrieve all state variables for a channel with the given prefix.
+ *
+ * @see GNUNET_PSYCSTORE_state_get_prefix()
+ *
+ * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+ */
+static int
+state_get_prefix (void *cls, const struct GNUNET_CRYPTO_EddsaPublicKey 
*channel_key,
+                  const char *name, GNUNET_PSYCSTORE_StateCallback cb,
+                  void *cb_cls)
+{
+  struct Plugin *plugin = cls;
+
+  const char *stmt = "select_state_prefix";
+
+  uint32_t name_len = (uint32_t) strlen (name);
+
+  struct GNUNET_PQ_QueryParam params_select[] = {
+    GNUNET_PQ_query_param_auto_from_type (channel_key),
+    GNUNET_PQ_query_param_string (name),
+    GNUNET_PQ_query_param_uint32 (&name_len),
+    GNUNET_PQ_query_param_string (name),
+    GNUNET_PQ_query_param_end
+  };
+
+  struct GetStateContext gsc = {
+    .cb = cb,
+    .cb_cls = cb_cls,
+    .value_id = "value_current",
+    .ret = GNUNET_NO
+  };
+
+  if (0 > GNUNET_PQ_eval_prepared_multi_select (plugin->dbh,
+                                                stmt, params_select,
+                                                &get_state_cb, &gsc))
+    return GNUNET_SYSERR;
+  return gsc.ret;  /* GNUNET_OK ?? */
+}
+
+
+/**
+ * Retrieve all signed state variables for a channel.
+ *
+ * @see GNUNET_PSYCSTORE_state_get_signed()
+ *
+ * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+ */
+static int
+state_get_signed (void *cls,
+                  const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+                  GNUNET_PSYCSTORE_StateCallback cb, void *cb_cls)
+{
+  struct Plugin *plugin = cls;
+
+  const char *stmt = "select_state_signed";
+
+  struct GNUNET_PQ_QueryParam params_select[] = {
+    GNUNET_PQ_query_param_auto_from_type (channel_key),
+    GNUNET_PQ_query_param_end
+  };
+
+  struct GetStateContext gsc = {
+    .cb = cb,
+    .cb_cls = cb_cls,
+    .value_id = "value_signed",
+    .ret = GNUNET_NO
+  };
+
+  if (0 > GNUNET_PQ_eval_prepared_multi_select (plugin->dbh,
+                                                stmt, params_select,
+                                                &get_state_cb, &gsc))
+    return GNUNET_SYSERR;
+  return gsc.ret;  /* GNUNET_OK ?? */
+}
+
+
+/**
+ * Entry point for the plugin.
+ *
+ * @param cls The struct GNUNET_CONFIGURATION_Handle.
+ * @return NULL on error, otherwise the plugin context
+ */
+void *
+libgnunet_plugin_psycstore_postgres_init (void *cls)
+{
+  static struct Plugin plugin;
+  const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
+  struct GNUNET_PSYCSTORE_PluginFunctions *api;
+
+  if (NULL != plugin.cfg)
+    return NULL;                /* can only initialize once! */
+  memset (&plugin, 0, sizeof (struct Plugin));
+  plugin.cfg = cfg;
+  if (GNUNET_OK != database_setup (&plugin))
+  {
+    database_shutdown (&plugin);
+    return NULL;
+  }
+  api = GNUNET_new (struct GNUNET_PSYCSTORE_PluginFunctions);
+  api->cls = &plugin;
+  api->membership_store = &postgres_membership_store;
+  api->membership_test = &membership_test;
+  api->fragment_store = &fragment_store;
+  api->message_add_flags = &message_add_flags;
+  api->fragment_get = &fragment_get;
+  api->fragment_get_latest = &fragment_get_latest;
+  api->message_get = &message_get;
+  api->message_get_latest = &message_get_latest;
+  api->message_get_fragment = &message_get_fragment;
+  api->counters_message_get = &counters_message_get;
+  api->counters_state_get = &counters_state_get;
+  api->state_modify_begin = &state_modify_begin;
+  api->state_modify_op = &state_modify_op;
+  api->state_modify_end = &state_modify_end;
+  api->state_sync_begin = &state_sync_begin;
+  api->state_sync_assign = &state_sync_assign;
+  api->state_sync_end = &state_sync_end;
+  api->state_reset = &state_reset;
+  api->state_update_signed = &state_update_signed;
+  api->state_get = &state_get;
+  api->state_get_prefix = &state_get_prefix;
+  api->state_get_signed = &state_get_signed;
+
+  LOG (GNUNET_ERROR_TYPE_INFO, _("Postgres database running\n"));
+  return api;
+}
+
+
+/**
+ * Exit point from the plugin.
+ *
+ * @param cls The plugin context (as returned by "init")
+ * @return Always NULL
+ */
+void *
+libgnunet_plugin_psycstore_postgres_done (void *cls)
+{
+  struct GNUNET_PSYCSTORE_PluginFunctions *api = cls;
+  struct Plugin *plugin = api->cls;
+
+  database_shutdown (plugin);
+  plugin->cfg = NULL;
+  GNUNET_free (api);
+  LOG (GNUNET_ERROR_TYPE_DEBUG, "Postgres plugin has finished\n");
+  return NULL;
+}
+
+/* end of plugin_psycstore_postgres.c */
diff --git a/src/psycstore/plugin_psycstore_sqlite.c 
b/src/psycstore/plugin_psycstore_sqlite.c
new file mode 100644
index 0000000..24de383
--- /dev/null
+++ b/src/psycstore/plugin_psycstore_sqlite.c
@@ -0,0 +1,1948 @@
+/*
+ * This file is part of GNUnet
+ * Copyright (C) 2013 GNUnet e.V.
+ *
+ * GNUnet is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * GNUnet 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
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+     SPDX-License-Identifier: AGPL3.0-or-later
+ */
+
+/**
+ * @file psycstore/plugin_psycstore_sqlite.c
+ * @brief sqlite-based psycstore backend
+ * @author Gabor X Toth
+ * @author Christian Grothoff
+ */
+
+/*
+ * FIXME: SQLite3 only supports signed 64-bit integers natively,
+ *        thus it can only store 63 bits of the uint64_t's.
+ */
+
+#include "platform.h"
+#include "gnunet_psycstore_plugin.h"
+#include "gnunet_psycstore_service.h"
+#include "gnunet_multicast_service.h"
+#include "gnunet_crypto_lib.h"
+#include "gnunet_psyc_util_lib.h"
+#include "psycstore.h"
+#include <sqlite3.h>
+
+/**
+ * After how many ms "busy" should a DB operation fail for good?  A
+ * low value makes sure that we are more responsive to requests
+ * (especially PUTs).  A high value guarantees a higher success rate
+ * (SELECTs in iterate can take several seconds despite LIMIT=1).
+ *
+ * The default value of 1s should ensure that users do not experience
+ * huge latencies while at the same time allowing operations to
+ * succeed with reasonable probability.
+ */
+#define BUSY_TIMEOUT_MS 1000
+
+#define DEBUG_PSYCSTORE GNUNET_EXTRA_LOGGING
+
+/**
+ * Log an error message at log-level 'level' that indicates
+ * a failure of the command 'cmd' on file 'filename'
+ * with the message given by strerror(errno).
+ */
+#define LOG_SQLITE(db, level, cmd) do { GNUNET_log_from (level, 
"psycstore-sqlite", _("`%s' failed at %s:%d with error: %s (%d)\n"), cmd, 
__FILE__, __LINE__, sqlite3_errmsg(db->dbh), sqlite3_errcode(db->dbh)); } 
while(0)
+
+#define LOG(kind,...) GNUNET_log_from (kind, "psycstore-sqlite", __VA_ARGS__)
+
+enum Transactions {
+  TRANSACTION_NONE = 0,
+  TRANSACTION_STATE_MODIFY,
+  TRANSACTION_STATE_SYNC,
+};
+
+/**
+ * Context for all functions in this plugin.
+ */
+struct Plugin
+{
+
+  const struct GNUNET_CONFIGURATION_Handle *cfg;
+
+  /**
+   * Database filename.
+   */
+  char *fn;
+
+  /**
+   * Native SQLite database handle.
+   */
+  sqlite3 *dbh;
+
+  /**
+   * Current transaction.
+   */
+  enum Transactions transaction;
+
+  sqlite3_stmt *transaction_begin;
+
+  sqlite3_stmt *transaction_commit;
+
+  sqlite3_stmt *transaction_rollback;
+
+  /**
+   * Precompiled SQL for channel_key_store()
+   */
+  sqlite3_stmt *insert_channel_key;
+
+  /**
+   * Precompiled SQL for slave_key_store()
+   */
+  sqlite3_stmt *insert_slave_key;
+
+
+  /**
+   * Precompiled SQL for membership_store()
+   */
+  sqlite3_stmt *insert_membership;
+
+  /**
+   * Precompiled SQL for membership_test()
+   */
+  sqlite3_stmt *select_membership;
+
+
+  /**
+   * Precompiled SQL for fragment_store()
+   */
+  sqlite3_stmt *insert_fragment;
+
+  /**
+   * Precompiled SQL for message_add_flags()
+   */
+  sqlite3_stmt *update_message_flags;
+
+  /**
+   * Precompiled SQL for fragment_get()
+   */
+  sqlite3_stmt *select_fragments;
+
+  /**
+   * Precompiled SQL for fragment_get()
+   */
+  sqlite3_stmt *select_latest_fragments;
+
+  /**
+   * Precompiled SQL for message_get()
+   */
+  sqlite3_stmt *select_messages;
+
+  /**
+   * Precompiled SQL for message_get()
+   */
+  sqlite3_stmt *select_latest_messages;
+
+  /**
+   * Precompiled SQL for message_get_fragment()
+   */
+  sqlite3_stmt *select_message_fragment;
+
+  /**
+   * Precompiled SQL for counters_get_message()
+   */
+  sqlite3_stmt *select_counters_message;
+
+  /**
+   * Precompiled SQL for counters_get_state()
+   */
+  sqlite3_stmt *select_counters_state;
+
+  /**
+   * Precompiled SQL for state_modify_end()
+   */
+  sqlite3_stmt *update_state_hash_message_id;
+
+  /**
+   * Precompiled SQL for state_sync_end()
+   */
+  sqlite3_stmt *update_max_state_message_id;
+
+  /**
+   * Precompiled SQL for state_modify_op()
+   */
+  sqlite3_stmt *insert_state_current;
+
+  /**
+   * Precompiled SQL for state_modify_end()
+   */
+  sqlite3_stmt *delete_state_empty;
+
+  /**
+   * Precompiled SQL for state_set_signed()
+   */
+  sqlite3_stmt *update_state_signed;
+
+  /**
+   * Precompiled SQL for state_sync()
+   */
+  sqlite3_stmt *insert_state_sync;
+
+  /**
+   * Precompiled SQL for state_sync()
+   */
+  sqlite3_stmt *delete_state;
+
+  /**
+   * Precompiled SQL for state_sync()
+   */
+  sqlite3_stmt *insert_state_from_sync;
+
+  /**
+   * Precompiled SQL for state_sync()
+   */
+  sqlite3_stmt *delete_state_sync;
+
+  /**
+   * Precompiled SQL for state_get_signed()
+   */
+  sqlite3_stmt *select_state_signed;
+
+  /**
+   * Precompiled SQL for state_get()
+   */
+  sqlite3_stmt *select_state_one;
+
+  /**
+   * Precompiled SQL for state_get_prefix()
+   */
+  sqlite3_stmt *select_state_prefix;
+
+};
+
+#if DEBUG_PSYCSTORE
+
+static void
+sql_trace (void *cls, const char *sql)
+{
+  LOG (GNUNET_ERROR_TYPE_DEBUG, "SQL query:\n%s\n", sql);
+}
+
+#endif
+
+/**
+ * @brief Prepare a SQL statement
+ *
+ * @param dbh handle to the database
+ * @param sql SQL statement, UTF-8 encoded
+ * @param stmt set to the prepared statement
+ * @return 0 on success
+ */
+static int
+sql_prepare (sqlite3 *dbh, const char *sql, sqlite3_stmt **stmt)
+{
+  char *tail;
+  int result;
+
+  result = sqlite3_prepare_v2 (dbh, sql, strlen (sql), stmt,
+                               (const char **) &tail);
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Prepared `%s' / %p: %d\n", sql, *stmt, result);
+  if (result != SQLITE_OK)
+    LOG (GNUNET_ERROR_TYPE_ERROR,
+        _("Error preparing SQL query: %s\n  %s\n"),
+        sqlite3_errmsg (dbh), sql);
+  return result;
+}
+
+
+/**
+ * @brief Prepare a SQL statement
+ *
+ * @param dbh handle to the database
+ * @param sql SQL statement, UTF-8 encoded
+ * @return 0 on success
+ */
+static int
+sql_exec (sqlite3 *dbh, const char *sql)
+{
+  int result;
+
+  result = sqlite3_exec (dbh, sql, NULL, NULL, NULL);
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Executed `%s' / %d\n", sql, result);
+  if (result != SQLITE_OK)
+    LOG (GNUNET_ERROR_TYPE_ERROR,
+        _("Error executing SQL query: %s\n  %s\n"),
+        sqlite3_errmsg (dbh), sql);
+  return result;
+}
+
+
+/**
+ * Initialize the database connections and associated
+ * data structures (create tables and indices
+ * as needed as well).
+ *
+ * @param plugin the plugin context (state for this module)
+ * @return GNUNET_OK on success
+ */
+static int
+database_setup (struct Plugin *plugin)
+{
+  char *filename;
+
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_filename (plugin->cfg, "psycstore-sqlite",
+                                               "FILENAME", &filename))
+  {
+    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+                              "psycstore-sqlite", "FILENAME");
+    return GNUNET_SYSERR;
+  }
+  if (GNUNET_OK != GNUNET_DISK_file_test (filename))
+  {
+    if (GNUNET_OK != GNUNET_DISK_directory_create_for_file (filename))
+    {
+      GNUNET_break (0);
+      GNUNET_free (filename);
+      return GNUNET_SYSERR;
+    }
+  }
+  /* filename should be UTF-8-encoded. If it isn't, it's a bug */
+  plugin->fn = filename;
+
+  /* Open database and precompile statements */
+  if (SQLITE_OK != sqlite3_open (plugin->fn, &plugin->dbh))
+  {
+    LOG (GNUNET_ERROR_TYPE_ERROR,
+        _("Unable to initialize SQLite: %s.\n"),
+        sqlite3_errmsg (plugin->dbh));
+    return GNUNET_SYSERR;
+  }
+
+#if DEBUG_PSYCSTORE
+  sqlite3_trace (plugin->dbh, &sql_trace, NULL);
+#endif
+
+  sql_exec (plugin->dbh, "PRAGMA temp_store=MEMORY");
+  sql_exec (plugin->dbh, "PRAGMA synchronous=NORMAL");
+  sql_exec (plugin->dbh, "PRAGMA legacy_file_format=OFF");
+  sql_exec (plugin->dbh, "PRAGMA auto_vacuum=INCREMENTAL");
+  sql_exec (plugin->dbh, "PRAGMA encoding=\"UTF-8\"");
+#if ! DEBUG_PSYCSTORE
+  sql_exec (plugin->dbh, "PRAGMA locking_mode=EXCLUSIVE");
+#endif
+  sql_exec (plugin->dbh, "PRAGMA page_size=4096");
+
+  sqlite3_busy_timeout (plugin->dbh, BUSY_TIMEOUT_MS);
+
+  /* Create tables */
+
+  sql_exec (plugin->dbh,
+            "CREATE TABLE IF NOT EXISTS channels (\n"
+            "  id INTEGER PRIMARY KEY,\n"
+            "  pub_key BLOB(32) UNIQUE,\n"
+            "  max_state_message_id INTEGER,\n" // last applied state message 
ID
+            "  state_hash_message_id INTEGER\n" // last message ID with a 
state hash
+            ");");
+
+  sql_exec (plugin->dbh,
+            "CREATE TABLE IF NOT EXISTS slaves (\n"
+            "  id INTEGER PRIMARY KEY,\n"
+            "  pub_key BLOB(32) UNIQUE\n"
+            ");");
+
+  sql_exec (plugin->dbh,
+            "CREATE TABLE IF NOT EXISTS membership (\n"
+            "  channel_id INTEGER NOT NULL REFERENCES channels(id),\n"
+            "  slave_id INTEGER NOT NULL REFERENCES slaves(id),\n"
+            "  did_join INTEGER NOT NULL,\n"
+            "  announced_at INTEGER NOT NULL,\n"
+            "  effective_since INTEGER NOT NULL,\n"
+            "  group_generation INTEGER NOT NULL\n"
+            ");");
+  sql_exec (plugin->dbh,
+            "CREATE INDEX IF NOT EXISTS idx_membership_channel_id_slave_id "
+            "ON membership (channel_id, slave_id);");
+
+  /** @todo messages table: add method_name column */
+  sql_exec (plugin->dbh,
+            "CREATE TABLE IF NOT EXISTS messages (\n"
+            "  channel_id INTEGER NOT NULL REFERENCES channels(id),\n"
+            "  hop_counter INTEGER NOT NULL,\n"
+            "  signature BLOB,\n"
+            "  purpose BLOB,\n"
+            "  fragment_id INTEGER NOT NULL,\n"
+            "  fragment_offset INTEGER NOT NULL,\n"
+            "  message_id INTEGER NOT NULL,\n"
+            "  group_generation INTEGER NOT NULL,\n"
+            "  multicast_flags INTEGER NOT NULL,\n"
+            "  psycstore_flags INTEGER NOT NULL,\n"
+            "  data BLOB,\n"
+            "  PRIMARY KEY (channel_id, fragment_id),\n"
+            "  UNIQUE (channel_id, message_id, fragment_offset)\n"
+            ");");
+
+  sql_exec (plugin->dbh,
+            "CREATE TABLE IF NOT EXISTS state (\n"
+            "  channel_id INTEGER NOT NULL REFERENCES channels(id),\n"
+            "  name TEXT NOT NULL,\n"
+            "  value_current BLOB,\n"
+            "  value_signed BLOB,\n"
+            "  PRIMARY KEY (channel_id, name)\n"
+            ");");
+
+  sql_exec (plugin->dbh,
+            "CREATE TABLE IF NOT EXISTS state_sync (\n"
+            "  channel_id INTEGER NOT NULL REFERENCES channels(id),\n"
+            "  name TEXT NOT NULL,\n"
+            "  value BLOB,\n"
+            "  PRIMARY KEY (channel_id, name)\n"
+            ");");
+
+  /* Prepare statements */
+
+  sql_prepare (plugin->dbh, "BEGIN;", &plugin->transaction_begin);
+
+  sql_prepare (plugin->dbh, "COMMIT;", &plugin->transaction_commit);
+
+  sql_prepare (plugin->dbh, "ROLLBACK;", &plugin->transaction_rollback);
+
+  sql_prepare (plugin->dbh,
+               "INSERT OR IGNORE INTO channels (pub_key) VALUES (?);",
+               &plugin->insert_channel_key);
+
+  sql_prepare (plugin->dbh,
+               "INSERT OR IGNORE INTO slaves (pub_key) VALUES (?);",
+               &plugin->insert_slave_key);
+
+  sql_prepare (plugin->dbh,
+               "INSERT INTO membership\n"
+               " (channel_id, slave_id, did_join, announced_at,\n"
+               "  effective_since, group_generation)\n"
+               "VALUES ((SELECT id FROM channels WHERE pub_key = ?),\n"
+               "        (SELECT id FROM slaves WHERE pub_key = ?),\n"
+               "        ?, ?, ?, ?);",
+               &plugin->insert_membership);
+
+  sql_prepare (plugin->dbh,
+               "SELECT did_join FROM membership\n"
+               "WHERE channel_id = (SELECT id FROM channels WHERE pub_key = 
?)\n"
+               "      AND slave_id = (SELECT id FROM slaves WHERE pub_key = 
?)\n"
+               "      AND effective_since <= ? AND did_join = 1\n"
+               "ORDER BY announced_at DESC LIMIT 1;",
+               &plugin->select_membership);
+
+  sql_prepare (plugin->dbh,
+               "INSERT OR IGNORE INTO messages\n"
+               " (channel_id, hop_counter, signature, purpose,\n"
+               "  fragment_id, fragment_offset, message_id,\n"
+               "  group_generation, multicast_flags, psycstore_flags, data)\n"
+               "VALUES ((SELECT id FROM channels WHERE pub_key = ?),\n"
+               "        ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);",
+               &plugin->insert_fragment);
+
+  sql_prepare (plugin->dbh,
+               "UPDATE messages\n"
+               "SET psycstore_flags = psycstore_flags | ?\n"
+               "WHERE channel_id = (SELECT id FROM channels WHERE pub_key = 
?)\n"
+               "      AND message_id = ? AND fragment_offset = 0;",
+               &plugin->update_message_flags);
+
+  sql_prepare (plugin->dbh,
+               "SELECT hop_counter, signature, purpose, fragment_id,\n"
+               "       fragment_offset, message_id, group_generation,\n"
+               "       multicast_flags, psycstore_flags, data\n"
+               "FROM messages\n"
+               "WHERE channel_id = (SELECT id FROM channels WHERE pub_key = 
?)\n"
+               "      AND ? <= fragment_id AND fragment_id <= ?;",
+               &plugin->select_fragments);
+
+  /** @todo select_messages: add method_prefix filter */
+  sql_prepare (plugin->dbh,
+               "SELECT hop_counter, signature, purpose, fragment_id,\n"
+               "       fragment_offset, message_id, group_generation,\n"
+               "       multicast_flags, psycstore_flags, data\n"
+               "FROM messages\n"
+               "WHERE channel_id = (SELECT id FROM channels WHERE pub_key = 
?)\n"
+               "      AND ? <= message_id AND message_id <= ?"
+               "LIMIT ?;",
+               &plugin->select_messages);
+
+  sql_prepare (plugin->dbh,
+               "SELECT * FROM\n"
+               "(SELECT hop_counter, signature, purpose, fragment_id,\n"
+               "        fragment_offset, message_id, group_generation,\n"
+               "        multicast_flags, psycstore_flags, data\n"
+               " FROM messages\n"
+               " WHERE channel_id = (SELECT id FROM channels WHERE pub_key = 
?)\n"
+               " ORDER BY fragment_id DESC\n"
+               " LIMIT ?)\n"
+               "ORDER BY fragment_id;",
+               &plugin->select_latest_fragments);
+
+  /** @todo select_latest_messages: add method_prefix filter */
+  sql_prepare (plugin->dbh,
+               "SELECT hop_counter, signature, purpose, fragment_id,\n"
+               "       fragment_offset, message_id, group_generation,\n"
+               "        multicast_flags, psycstore_flags, data\n"
+               "FROM messages\n"
+               "WHERE channel_id = (SELECT id FROM channels WHERE pub_key = 
?)\n"
+               "      AND message_id IN\n"
+               "      (SELECT message_id\n"
+               "       FROM messages\n"
+               "       WHERE channel_id = (SELECT id FROM channels WHERE 
pub_key = ?)\n"
+               "       GROUP BY message_id\n"
+               "       ORDER BY message_id\n"
+               "       DESC LIMIT ?)\n"
+               "ORDER BY fragment_id;",
+               &plugin->select_latest_messages);
+
+  sql_prepare (plugin->dbh,
+               "SELECT hop_counter, signature, purpose, fragment_id,\n"
+               "       fragment_offset, message_id, group_generation,\n"
+               "       multicast_flags, psycstore_flags, data\n"
+               "FROM messages\n"
+               "WHERE channel_id = (SELECT id FROM channels WHERE pub_key = 
?)\n"
+               "      AND message_id = ? AND fragment_offset = ?;",
+               &plugin->select_message_fragment);
+
+  sql_prepare (plugin->dbh,
+               "SELECT fragment_id, message_id, group_generation\n"
+               "FROM messages\n"
+               "WHERE channel_id = (SELECT id FROM channels WHERE pub_key = 
?)\n"
+               "ORDER BY fragment_id DESC LIMIT 1;",
+               &plugin->select_counters_message);
+
+  sql_prepare (plugin->dbh,
+               "SELECT max_state_message_id\n"
+               "FROM channels\n"
+               "WHERE pub_key = ? AND max_state_message_id IS NOT NULL;",
+               &plugin->select_counters_state);
+
+  sql_prepare (plugin->dbh,
+               "UPDATE channels\n"
+               "SET max_state_message_id = ?\n"
+               "WHERE pub_key = ?;",
+               &plugin->update_max_state_message_id);
+
+  sql_prepare (plugin->dbh,
+               "UPDATE channels\n"
+               "SET state_hash_message_id = ?\n"
+               "WHERE pub_key = ?;",
+               &plugin->update_state_hash_message_id);
+
+  sql_prepare (plugin->dbh,
+               "INSERT OR REPLACE INTO state\n"
+               "  (channel_id, name, value_current, value_signed)\n"
+               "SELECT new.channel_id, new.name,\n"
+               "       new.value_current, old.value_signed\n"
+               "FROM (SELECT (SELECT id FROM channels WHERE pub_key = ?)\n"
+               "             AS channel_id,\n"
+               "             ? AS name, ? AS value_current) AS new\n"
+               "LEFT JOIN (SELECT channel_id, name, value_signed\n"
+               "           FROM state) AS old\n"
+               "ON new.channel_id = old.channel_id AND new.name = old.name;",
+               &plugin->insert_state_current);
+
+  sql_prepare (plugin->dbh,
+               "DELETE FROM state\n"
+               "WHERE channel_id = (SELECT id FROM channels WHERE pub_key = 
?)\n"
+               "      AND (value_current IS NULL OR length(value_current) = 
0)\n"
+               "      AND (value_signed IS NULL OR length(value_signed) = 0);",
+               &plugin->delete_state_empty);
+
+  sql_prepare (plugin->dbh,
+               "UPDATE state\n"
+               "SET value_signed = value_current\n"
+               "WHERE channel_id = (SELECT id FROM channels WHERE pub_key = 
?);",
+               &plugin->update_state_signed);
+
+  sql_prepare (plugin->dbh,
+               "DELETE FROM state\n"
+               "WHERE channel_id = (SELECT id FROM channels WHERE pub_key = 
?);",
+               &plugin->delete_state);
+
+  sql_prepare (plugin->dbh,
+               "INSERT INTO state_sync (channel_id, name, value)\n"
+               "VALUES ((SELECT id FROM channels WHERE pub_key = ?), ?, ?);",
+               &plugin->insert_state_sync);
+
+  sql_prepare (plugin->dbh,
+               "INSERT INTO state\n"
+               " (channel_id, name, value_current, value_signed)\n"
+               "SELECT channel_id, name, value, value\n"
+               "FROM state_sync\n"
+               "WHERE channel_id = (SELECT id FROM channels WHERE pub_key = 
?);",
+               &plugin->insert_state_from_sync);
+
+  sql_prepare (plugin->dbh,
+               "DELETE FROM state_sync\n"
+               "WHERE channel_id = (SELECT id FROM channels WHERE pub_key = 
?);",
+               &plugin->delete_state_sync);
+
+  sql_prepare (plugin->dbh,
+               "SELECT value_current\n"
+               "FROM state\n"
+               "WHERE channel_id = (SELECT id FROM channels WHERE pub_key = 
?)\n"
+               "      AND name = ?;",
+               &plugin->select_state_one);
+
+  sql_prepare (plugin->dbh,
+               "SELECT name, value_current\n"
+               "FROM state\n"
+               "WHERE channel_id = (SELECT id FROM channels WHERE pub_key = 
?)\n"
+               "      AND (name = ? OR substr(name, 1, ?) = ?);",
+               &plugin->select_state_prefix);
+
+  sql_prepare (plugin->dbh,
+               "SELECT name, value_signed\n"
+               "FROM state\n"
+               "WHERE channel_id = (SELECT id FROM channels WHERE pub_key = ?)"
+               "      AND value_signed IS NOT NULL;",
+               &plugin->select_state_signed);
+
+  return GNUNET_OK;
+}
+
+
+/**
+ * Shutdown database connection and associate data
+ * structures.
+ * @param plugin the plugin context (state for this module)
+ */
+static void
+database_shutdown (struct Plugin *plugin)
+{
+  int result;
+  sqlite3_stmt *stmt;
+  while (NULL != (stmt = sqlite3_next_stmt (plugin->dbh, NULL)))
+  {
+    result = sqlite3_finalize (stmt);
+    if (SQLITE_OK != result)
+      LOG (GNUNET_ERROR_TYPE_WARNING,
+           "Failed to close statement %p: %d\n", stmt, result);
+  }
+  if (SQLITE_OK != sqlite3_close (plugin->dbh))
+    LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR, "sqlite3_close");
+
+  GNUNET_free_non_null (plugin->fn);
+}
+
+/**
+ * Execute a prepared statement with a @a channel_key argument.
+ *
+ * @param plugin Plugin handle.
+ * @param stmt Statement to execute.
+ * @param channel_key Public key of the channel.
+ *
+ * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+ */
+static int
+exec_channel (struct Plugin *plugin, sqlite3_stmt *stmt,
+              const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key)
+{
+  if (SQLITE_OK != sqlite3_bind_blob (stmt, 1, channel_key,
+                                      sizeof (*channel_key), SQLITE_STATIC))
+  {
+    LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sqlite3_bind");
+  }
+  else if (SQLITE_DONE != sqlite3_step (stmt))
+  {
+    LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sqlite3_step");
+  }
+
+  if (SQLITE_OK != sqlite3_reset (stmt))
+  {
+    LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sqlite3_reset");
+    return GNUNET_SYSERR;
+  }
+
+  return GNUNET_OK;
+}
+
+/**
+ * Begin a transaction.
+ */
+static int
+transaction_begin (struct Plugin *plugin, enum Transactions transaction)
+{
+  sqlite3_stmt *stmt = plugin->transaction_begin;
+
+  if (SQLITE_DONE != sqlite3_step (stmt))
+  {
+    LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sqlite3_step");
+  }
+  if (SQLITE_OK != sqlite3_reset (stmt))
+  {
+    LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sqlite3_reset");
+    return GNUNET_SYSERR;
+  }
+
+  plugin->transaction = transaction;
+  return GNUNET_OK;
+}
+
+
+/**
+ * Commit current transaction.
+ */
+static int
+transaction_commit (struct Plugin *plugin)
+{
+  sqlite3_stmt *stmt = plugin->transaction_commit;
+
+  if (SQLITE_DONE != sqlite3_step (stmt))
+  {
+    LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sqlite3_step");
+  }
+  if (SQLITE_OK != sqlite3_reset (stmt))
+  {
+    LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sqlite3_reset");
+    return GNUNET_SYSERR;
+  }
+
+  plugin->transaction = TRANSACTION_NONE;
+  return GNUNET_OK;
+}
+
+
+/**
+ * Roll back current transaction.
+ */
+static int
+transaction_rollback (struct Plugin *plugin)
+{
+  sqlite3_stmt *stmt = plugin->transaction_rollback;
+
+  if (SQLITE_DONE != sqlite3_step (stmt))
+  {
+    LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sqlite3_step");
+  }
+  if (SQLITE_OK != sqlite3_reset (stmt))
+  {
+    LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sqlite3_reset");
+    return GNUNET_SYSERR;
+  }
+  plugin->transaction = TRANSACTION_NONE;
+  return GNUNET_OK;
+}
+
+
+static int
+channel_key_store (struct Plugin *plugin,
+                   const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key)
+{
+  sqlite3_stmt *stmt = plugin->insert_channel_key;
+
+  if (SQLITE_OK != sqlite3_bind_blob (stmt, 1, channel_key,
+                                      sizeof (*channel_key), SQLITE_STATIC))
+  {
+    LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sqlite3_bind");
+  }
+  else if (SQLITE_DONE != sqlite3_step (stmt))
+  {
+    LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sqlite3_step");
+  }
+
+  if (SQLITE_OK != sqlite3_reset (stmt))
+  {
+    LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sqlite3_reset");
+    return GNUNET_SYSERR;
+  }
+
+  return GNUNET_OK;
+}
+
+
+static int
+slave_key_store (struct Plugin *plugin,
+                 const struct GNUNET_CRYPTO_EcdsaPublicKey *slave_key)
+{
+  sqlite3_stmt *stmt = plugin->insert_slave_key;
+
+  if (SQLITE_OK != sqlite3_bind_blob (stmt, 1, slave_key,
+                                      sizeof (*slave_key), SQLITE_STATIC))
+  {
+    LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sqlite3_bind");
+  }
+  else if (SQLITE_DONE != sqlite3_step (stmt))
+  {
+    LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sqlite3_step");
+  }
+
+
+  if (SQLITE_OK != sqlite3_reset (stmt))
+  {
+    LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sqlite3_reset");
+    return GNUNET_SYSERR;
+  }
+
+  return GNUNET_OK;
+}
+
+
+/**
+ * Store join/leave events for a PSYC channel in order to be able to answer
+ * membership test queries later.
+ *
+ * @see GNUNET_PSYCSTORE_membership_store()
+ *
+ * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+ */
+static int
+sqlite_membership_store (void *cls,
+                         const struct GNUNET_CRYPTO_EddsaPublicKey 
*channel_key,
+                         const struct GNUNET_CRYPTO_EcdsaPublicKey *slave_key,
+                         int did_join,
+                         uint64_t announced_at,
+                         uint64_t effective_since,
+                         uint64_t group_generation)
+{
+  struct Plugin *plugin = cls;
+  sqlite3_stmt *stmt = plugin->insert_membership;
+
+  GNUNET_assert (TRANSACTION_NONE == plugin->transaction);
+
+  if (announced_at > INT64_MAX ||
+      effective_since > INT64_MAX ||
+      group_generation > INT64_MAX)
+  {
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+
+  if (GNUNET_OK != channel_key_store (plugin, channel_key)
+      || GNUNET_OK != slave_key_store (plugin, slave_key))
+    return GNUNET_SYSERR;
+
+  if (SQLITE_OK != sqlite3_bind_blob (stmt, 1, channel_key,
+                                      sizeof (*channel_key), SQLITE_STATIC)
+      || SQLITE_OK != sqlite3_bind_blob (stmt, 2, slave_key,
+                                         sizeof (*slave_key), SQLITE_STATIC)
+      || SQLITE_OK != sqlite3_bind_int (stmt, 3, did_join)
+      || SQLITE_OK != sqlite3_bind_int64 (stmt, 4, announced_at)
+      || SQLITE_OK != sqlite3_bind_int64 (stmt, 5, effective_since)
+      || SQLITE_OK != sqlite3_bind_int64 (stmt, 6, group_generation))
+  {
+    LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sqlite3_bind");
+  }
+  else if (SQLITE_DONE != sqlite3_step (stmt))
+  {
+    LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sqlite3_step");
+  }
+
+  if (SQLITE_OK != sqlite3_reset (stmt))
+  {
+    LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sqlite3_reset");
+    return GNUNET_SYSERR;
+  }
+
+  return GNUNET_OK;
+}
+
+/**
+ * Test if a member was admitted to the channel at the given message ID.
+ *
+ * @see GNUNET_PSYCSTORE_membership_test()
+ *
+ * @return #GNUNET_YES if the member was admitted, #GNUNET_NO if not,
+ *         #GNUNET_SYSERR if there was en error.
+ */
+static int
+membership_test (void *cls,
+                 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+                 const struct GNUNET_CRYPTO_EcdsaPublicKey *slave_key,
+                 uint64_t message_id)
+{
+  struct Plugin *plugin = cls;
+  sqlite3_stmt *stmt = plugin->select_membership;
+  int ret = GNUNET_SYSERR;
+
+  if (SQLITE_OK != sqlite3_bind_blob (stmt, 1, channel_key,
+                                      sizeof (*channel_key), SQLITE_STATIC)
+      || SQLITE_OK != sqlite3_bind_blob (stmt, 2, slave_key,
+                                         sizeof (*slave_key), SQLITE_STATIC)
+      || SQLITE_OK != sqlite3_bind_int64 (stmt, 3, message_id))
+  {
+    LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sqlite3_bind");
+  }
+  else
+  {
+    switch (sqlite3_step (stmt))
+    {
+    case SQLITE_DONE:
+      ret = GNUNET_NO;
+      break;
+    case SQLITE_ROW:
+      ret = GNUNET_YES;
+    }
+  }
+
+  if (SQLITE_OK != sqlite3_reset (stmt))
+  {
+    LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sqlite3_reset");
+  }
+
+  return ret;
+}
+
+/**
+ * Store a message fragment sent to a channel.
+ *
+ * @see GNUNET_PSYCSTORE_fragment_store()
+ *
+ * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+ */
+static int
+fragment_store (void *cls,
+                const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+                const struct GNUNET_MULTICAST_MessageHeader *msg,
+                uint32_t psycstore_flags)
+{
+  struct Plugin *plugin = cls;
+  sqlite3_stmt *stmt = plugin->insert_fragment;
+
+  GNUNET_assert (TRANSACTION_NONE == plugin->transaction);
+
+  uint64_t fragment_id = GNUNET_ntohll (msg->fragment_id);
+  uint64_t fragment_offset = GNUNET_ntohll (msg->fragment_offset);
+  uint64_t message_id = GNUNET_ntohll (msg->message_id);
+  uint64_t group_generation = GNUNET_ntohll (msg->group_generation);
+
+  if (fragment_id > INT64_MAX || fragment_offset > INT64_MAX ||
+      message_id > INT64_MAX || group_generation > INT64_MAX)
+  {
+    LOG (GNUNET_ERROR_TYPE_ERROR,
+         "Tried to store fragment with a field > INT64_MAX: "
+         "%lu, %lu, %lu, %lu\n", fragment_id, fragment_offset,
+         message_id, group_generation);
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+
+  if (GNUNET_OK != channel_key_store (plugin, channel_key))
+    return GNUNET_SYSERR;
+
+  if (SQLITE_OK != sqlite3_bind_blob (stmt, 1, channel_key,
+                                      sizeof (*channel_key), SQLITE_STATIC)
+      || SQLITE_OK != sqlite3_bind_int64 (stmt, 2, ntohl (msg->hop_counter) )
+      || SQLITE_OK != sqlite3_bind_blob (stmt, 3, (const void *) 
&msg->signature,
+                                         sizeof (msg->signature), 
SQLITE_STATIC)
+      || SQLITE_OK != sqlite3_bind_blob (stmt, 4, (const void *) &msg->purpose,
+                                         sizeof (msg->purpose), SQLITE_STATIC)
+      || SQLITE_OK != sqlite3_bind_int64 (stmt, 5, fragment_id)
+      || SQLITE_OK != sqlite3_bind_int64 (stmt, 6, fragment_offset)
+      || SQLITE_OK != sqlite3_bind_int64 (stmt, 7, message_id)
+      || SQLITE_OK != sqlite3_bind_int64 (stmt, 8, group_generation)
+      || SQLITE_OK != sqlite3_bind_int64 (stmt, 9, ntohl (msg->flags))
+      || SQLITE_OK != sqlite3_bind_int64 (stmt, 10, psycstore_flags)
+      || SQLITE_OK != sqlite3_bind_blob (stmt, 11, (const void *) &msg[1],
+                                         ntohs (msg->header.size)
+                                         - sizeof (*msg), SQLITE_STATIC))
+  {
+    LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sqlite3_bind");
+  }
+  else if (SQLITE_DONE != sqlite3_step (stmt))
+  {
+    LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sqlite3_step");
+  }
+
+  if (SQLITE_OK != sqlite3_reset (stmt))
+  {
+    LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sqlite3_reset");
+    return GNUNET_SYSERR;
+  }
+
+  return GNUNET_OK;
+}
+
+/**
+ * Set additional flags for a given message.
+ *
+ * They are OR'd with any existing flags set.
+ *
+ * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+ */
+static int
+message_add_flags (void *cls,
+                   const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+                   uint64_t message_id,
+                   uint32_t psycstore_flags)
+{
+  struct Plugin *plugin = cls;
+  sqlite3_stmt *stmt = plugin->update_message_flags;
+  int ret = GNUNET_SYSERR;
+
+  if (SQLITE_OK != sqlite3_bind_int64 (stmt, 1, psycstore_flags)
+      || SQLITE_OK != sqlite3_bind_blob (stmt, 2, channel_key,
+                                         sizeof (*channel_key), SQLITE_STATIC)
+      || SQLITE_OK != sqlite3_bind_int64 (stmt, 3, message_id))
+  {
+    LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sqlite3_bind");
+  }
+  else
+  {
+    switch (sqlite3_step (stmt))
+    {
+    case SQLITE_DONE:
+      ret = sqlite3_total_changes (plugin->dbh) > 0 ? GNUNET_OK : GNUNET_NO;
+      break;
+    default:
+      LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                  "sqlite3_step");
+    }
+  }
+
+  if (SQLITE_OK != sqlite3_reset (stmt))
+  {
+    LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sqlite3_reset");
+    return GNUNET_SYSERR;
+  }
+
+  return ret;
+}
+
+static int
+fragment_row (sqlite3_stmt *stmt, GNUNET_PSYCSTORE_FragmentCallback cb,
+              void *cb_cls)
+{
+  int data_size = sqlite3_column_bytes (stmt, 9);
+  struct GNUNET_MULTICAST_MessageHeader *msg
+    = GNUNET_malloc (sizeof (*msg) + data_size);
+
+  msg->header.size = htons (sizeof (*msg) + data_size);
+  msg->header.type = htons (GNUNET_MESSAGE_TYPE_MULTICAST_MESSAGE);
+  msg->hop_counter = htonl ((uint32_t) sqlite3_column_int64 (stmt, 0));
+  GNUNET_memcpy (&msg->signature,
+          sqlite3_column_blob (stmt, 1),
+          sqlite3_column_bytes (stmt, 1));
+  GNUNET_memcpy (&msg->purpose,
+          sqlite3_column_blob (stmt, 2),
+          sqlite3_column_bytes (stmt, 2));
+  msg->fragment_id = GNUNET_htonll (sqlite3_column_int64 (stmt, 3));
+  msg->fragment_offset = GNUNET_htonll (sqlite3_column_int64 (stmt, 4));
+  msg->message_id = GNUNET_htonll (sqlite3_column_int64 (stmt, 5));
+  msg->group_generation = GNUNET_htonll (sqlite3_column_int64 (stmt, 6));
+  msg->flags = htonl (sqlite3_column_int64 (stmt, 7));
+  GNUNET_memcpy (&msg[1], sqlite3_column_blob (stmt, 9), data_size);
+
+  return cb (cb_cls, (void *) msg, sqlite3_column_int64 (stmt, 8));
+}
+
+
+static int
+fragment_select (struct Plugin *plugin, sqlite3_stmt *stmt,
+                 uint64_t *returned_fragments,
+                 GNUNET_PSYCSTORE_FragmentCallback cb, void *cb_cls)
+{
+  int ret = GNUNET_SYSERR;
+  int sql_ret;
+
+  do
+  {
+    sql_ret = sqlite3_step (stmt);
+    switch (sql_ret)
+    {
+    case SQLITE_DONE:
+      if (ret != GNUNET_OK)
+        ret = GNUNET_NO;
+      break;
+    case SQLITE_ROW:
+      ret = fragment_row (stmt, cb, cb_cls);
+      (*returned_fragments)++;
+      if (ret != GNUNET_YES)
+        sql_ret = SQLITE_DONE;
+      break;
+    default:
+      LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                  "sqlite3_step");
+    }
+  }
+  while (sql_ret == SQLITE_ROW);
+
+  return ret;
+}
+
+/**
+ * Retrieve a message fragment range by fragment ID.
+ *
+ * @see GNUNET_PSYCSTORE_fragment_get()
+ *
+ * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+ */
+static int
+fragment_get (void *cls,
+              const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+              uint64_t first_fragment_id,
+              uint64_t last_fragment_id,
+              uint64_t *returned_fragments,
+              GNUNET_PSYCSTORE_FragmentCallback cb,
+              void *cb_cls)
+{
+  struct Plugin *plugin = cls;
+  sqlite3_stmt *stmt = plugin->select_fragments;
+  int ret = GNUNET_SYSERR;
+  *returned_fragments = 0;
+
+  if (SQLITE_OK != sqlite3_bind_blob (stmt, 1, channel_key,
+                                      sizeof (*channel_key),
+                                      SQLITE_STATIC)
+      || SQLITE_OK != sqlite3_bind_int64 (stmt, 2, first_fragment_id)
+      || SQLITE_OK != sqlite3_bind_int64 (stmt, 3, last_fragment_id))
+  {
+    LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sqlite3_bind");
+  }
+  else
+  {
+    ret = fragment_select (plugin, stmt, returned_fragments, cb, cb_cls);
+  }
+
+  if (SQLITE_OK != sqlite3_reset (stmt))
+  {
+    LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sqlite3_reset");
+  }
+
+  return ret;
+}
+
+
+/**
+ * Retrieve a message fragment range by fragment ID.
+ *
+ * @see GNUNET_PSYCSTORE_fragment_get_latest()
+ *
+ * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+ */
+static int
+fragment_get_latest (void *cls,
+                     const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+                     uint64_t fragment_limit,
+                     uint64_t *returned_fragments,
+                     GNUNET_PSYCSTORE_FragmentCallback cb,
+                     void *cb_cls)
+{
+  struct Plugin *plugin = cls;
+  sqlite3_stmt *stmt = plugin->select_latest_fragments;
+  int ret = GNUNET_SYSERR;
+  *returned_fragments = 0;
+
+  if (SQLITE_OK != sqlite3_bind_blob (stmt, 1, channel_key,
+                                      sizeof (*channel_key),
+                                      SQLITE_STATIC)
+      || SQLITE_OK != sqlite3_bind_int64 (stmt, 2, fragment_limit))
+  {
+    LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sqlite3_bind");
+  }
+  else
+  {
+    ret = fragment_select (plugin, stmt, returned_fragments, cb, cb_cls);
+  }
+
+  if (SQLITE_OK != sqlite3_reset (stmt))
+  {
+    LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sqlite3_reset");
+  }
+
+  return ret;
+}
+
+
+/**
+ * Retrieve all fragments of a message ID range.
+ *
+ * @see GNUNET_PSYCSTORE_message_get()
+ *
+ * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+ */
+static int
+message_get (void *cls,
+             const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+             uint64_t first_message_id,
+             uint64_t last_message_id,
+             uint64_t fragment_limit,
+             uint64_t *returned_fragments,
+             GNUNET_PSYCSTORE_FragmentCallback cb,
+             void *cb_cls)
+{
+  struct Plugin *plugin = cls;
+  sqlite3_stmt *stmt = plugin->select_messages;
+  int ret = GNUNET_SYSERR;
+  *returned_fragments = 0;
+
+  if (SQLITE_OK != sqlite3_bind_blob (stmt, 1, channel_key,
+                                      sizeof (*channel_key),
+                                      SQLITE_STATIC)
+      || SQLITE_OK != sqlite3_bind_int64 (stmt, 2, first_message_id)
+      || SQLITE_OK != sqlite3_bind_int64 (stmt, 3, last_message_id)
+      || SQLITE_OK != sqlite3_bind_int64 (stmt, 4,
+                                          (0 != fragment_limit)
+                                          ? fragment_limit
+                                          : INT64_MAX))
+  {
+    LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sqlite3_bind");
+  }
+  else
+  {
+    ret = fragment_select (plugin, stmt, returned_fragments, cb, cb_cls);
+  }
+
+  if (SQLITE_OK != sqlite3_reset (stmt))
+  {
+    LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sqlite3_reset");
+  }
+
+  return ret;
+}
+
+
+/**
+ * Retrieve all fragments of the latest messages.
+ *
+ * @see GNUNET_PSYCSTORE_message_get_latest()
+ *
+ * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+ */
+static int
+message_get_latest (void *cls,
+                    const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+                    uint64_t message_limit,
+                    uint64_t *returned_fragments,
+                    GNUNET_PSYCSTORE_FragmentCallback cb,
+                    void *cb_cls)
+{
+  struct Plugin *plugin = cls;
+  sqlite3_stmt *stmt = plugin->select_latest_messages;
+  int ret = GNUNET_SYSERR;
+  *returned_fragments = 0;
+
+  if (SQLITE_OK != sqlite3_bind_blob (stmt, 1, channel_key,
+                                      sizeof (*channel_key),
+                                      SQLITE_STATIC)
+      || SQLITE_OK != sqlite3_bind_blob (stmt, 2, channel_key,
+                                         sizeof (*channel_key),
+                                         SQLITE_STATIC)
+      || SQLITE_OK != sqlite3_bind_int64 (stmt, 3, message_limit))
+  {
+    LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sqlite3_bind");
+  }
+  else
+  {
+    ret = fragment_select (plugin, stmt, returned_fragments, cb, cb_cls);
+  }
+
+  if (SQLITE_OK != sqlite3_reset (stmt))
+  {
+    LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sqlite3_reset");
+  }
+
+  return ret;
+}
+
+
+/**
+ * Retrieve a fragment of message specified by its message ID and fragment
+ * offset.
+ *
+ * @see GNUNET_PSYCSTORE_message_get_fragment()
+ *
+ * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+ */
+static int
+message_get_fragment (void *cls,
+                      const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+                      uint64_t message_id,
+                      uint64_t fragment_offset,
+                      GNUNET_PSYCSTORE_FragmentCallback cb,
+                      void *cb_cls)
+{
+  struct Plugin *plugin = cls;
+  sqlite3_stmt *stmt = plugin->select_message_fragment;
+  int ret = GNUNET_SYSERR;
+
+  if (SQLITE_OK != sqlite3_bind_blob (stmt, 1, channel_key,
+                                      sizeof (*channel_key),
+                                      SQLITE_STATIC)
+      || SQLITE_OK != sqlite3_bind_int64 (stmt, 2, message_id)
+      || SQLITE_OK != sqlite3_bind_int64 (stmt, 3, fragment_offset))
+  {
+    LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sqlite3_bind");
+  }
+  else
+  {
+    switch (sqlite3_step (stmt))
+    {
+    case SQLITE_DONE:
+      ret = GNUNET_NO;
+      break;
+    case SQLITE_ROW:
+      ret = fragment_row (stmt, cb, cb_cls);
+      break;
+    default:
+      LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                  "sqlite3_step");
+    }
+  }
+
+  if (SQLITE_OK != sqlite3_reset (stmt))
+  {
+    LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sqlite3_reset");
+  }
+
+  return ret;
+}
+
+/**
+ * Retrieve the max. values of message counters for a channel.
+ *
+ * @see GNUNET_PSYCSTORE_counters_get()
+ *
+ * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+ */
+static int
+counters_message_get (void *cls,
+                      const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+                      uint64_t *max_fragment_id,
+                      uint64_t *max_message_id,
+                      uint64_t *max_group_generation)
+{
+  struct Plugin *plugin = cls;
+  sqlite3_stmt *stmt = plugin->select_counters_message;
+  int ret = GNUNET_SYSERR;
+
+  if (SQLITE_OK != sqlite3_bind_blob (stmt, 1, channel_key,
+                                      sizeof (*channel_key),
+                                      SQLITE_STATIC))
+  {
+    LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sqlite3_bind");
+  }
+  else
+  {
+    switch (sqlite3_step (stmt))
+    {
+    case SQLITE_DONE:
+      ret = GNUNET_NO;
+      break;
+    case SQLITE_ROW:
+      *max_fragment_id = sqlite3_column_int64 (stmt, 0);
+      *max_message_id = sqlite3_column_int64 (stmt, 1);
+      *max_group_generation = sqlite3_column_int64 (stmt, 2);
+      ret = GNUNET_OK;
+      break;
+    default:
+      LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                  "sqlite3_step");
+    }
+  }
+
+  if (SQLITE_OK != sqlite3_reset (stmt))
+  {
+    LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sqlite3_reset");
+  }
+
+  return ret;
+}
+
+/**
+ * Retrieve the max. values of state counters for a channel.
+ *
+ * @see GNUNET_PSYCSTORE_counters_get()
+ *
+ * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+ */
+static int
+counters_state_get (void *cls,
+                    const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+                    uint64_t *max_state_message_id)
+{
+  struct Plugin *plugin = cls;
+  sqlite3_stmt *stmt = plugin->select_counters_state;
+  int ret = GNUNET_SYSERR;
+
+  if (SQLITE_OK != sqlite3_bind_blob (stmt, 1, channel_key,
+                                      sizeof (*channel_key),
+                                      SQLITE_STATIC))
+  {
+    LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sqlite3_bind");
+  }
+  else
+  {
+    switch (sqlite3_step (stmt))
+    {
+    case SQLITE_DONE:
+      ret = GNUNET_NO;
+      break;
+    case SQLITE_ROW:
+      *max_state_message_id = sqlite3_column_int64 (stmt, 0);
+      ret = GNUNET_OK;
+      break;
+    default:
+      LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                  "sqlite3_step");
+    }
+  }
+
+  if (SQLITE_OK != sqlite3_reset (stmt))
+  {
+    LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sqlite3_reset");
+  }
+
+  return ret;
+}
+
+
+/**
+ * Assign a value to a state variable.
+ *
+ * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+ */
+static int
+state_assign (struct Plugin *plugin, sqlite3_stmt *stmt,
+              const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+              const char *name, const void *value, size_t value_size)
+{
+  int ret = GNUNET_SYSERR;
+
+  if (SQLITE_OK != sqlite3_bind_blob (stmt, 1, channel_key,
+                                      sizeof (*channel_key), SQLITE_STATIC)
+      || SQLITE_OK != sqlite3_bind_text (stmt, 2, name, -1, SQLITE_STATIC)
+      || SQLITE_OK != sqlite3_bind_blob (stmt, 3, value, value_size,
+                                         SQLITE_STATIC))
+  {
+    LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sqlite3_bind");
+  }
+  else
+  {
+    switch (sqlite3_step (stmt))
+    {
+    case SQLITE_DONE:
+      ret = 0 < sqlite3_total_changes (plugin->dbh) ? GNUNET_OK : GNUNET_NO;
+      break;
+    default:
+      LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                  "sqlite3_step");
+    }
+  }
+
+  if (SQLITE_OK != sqlite3_reset (stmt))
+  {
+    LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sqlite3_reset");
+    return GNUNET_SYSERR;
+  }
+
+  return ret;
+}
+
+
+static int
+update_message_id (struct Plugin *plugin, sqlite3_stmt *stmt,
+                   const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+                   uint64_t message_id)
+{
+  if (SQLITE_OK != sqlite3_bind_int64 (stmt, 1, message_id)
+      || SQLITE_OK != sqlite3_bind_blob (stmt, 2, channel_key,
+                                         sizeof (*channel_key), SQLITE_STATIC))
+  {
+    LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sqlite3_bind");
+  }
+  else if (SQLITE_DONE != sqlite3_step (stmt))
+  {
+    LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sqlite3_step");
+  }
+  if (SQLITE_OK != sqlite3_reset (stmt))
+  {
+    LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sqlite3_reset");
+    return GNUNET_SYSERR;
+  }
+  return GNUNET_OK;
+}
+
+
+/**
+ * Begin modifying current state.
+ */
+static int
+state_modify_begin (void *cls,
+                    const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+                    uint64_t message_id, uint64_t state_delta)
+{
+  struct Plugin *plugin = cls;
+
+  if (state_delta > 0)
+  {
+    /**
+     * We can only apply state modifiers in the current message if modifiers in
+     * the previous stateful message (message_id - state_delta) were already
+     * applied.
+     */
+
+    uint64_t max_state_message_id = 0;
+    int ret = counters_state_get (plugin, channel_key, &max_state_message_id);
+    switch (ret)
+    {
+    case GNUNET_OK:
+    case GNUNET_NO: // no state yet
+      ret = GNUNET_OK;
+      break;
+    default:
+      return ret;
+    }
+
+    if (max_state_message_id < message_id - state_delta)
+      return GNUNET_NO; /* some stateful messages not yet applied */
+    else if (message_id - state_delta < max_state_message_id)
+      return GNUNET_NO; /* changes already applied */
+  }
+
+  if (TRANSACTION_NONE != plugin->transaction)
+  {
+    /** @todo FIXME: wait for other transaction to finish  */
+    return GNUNET_SYSERR;
+  }
+  return transaction_begin (plugin, TRANSACTION_STATE_MODIFY);
+}
+
+
+/**
+ * Set the current value of state variable.
+ *
+ * @see GNUNET_PSYCSTORE_state_modify()
+ *
+ * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+ */
+static int
+state_modify_op (void *cls,
+                 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+                 enum GNUNET_PSYC_Operator op,
+                 const char *name, const void *value, size_t value_size)
+{
+  struct Plugin *plugin = cls;
+  GNUNET_assert (TRANSACTION_STATE_MODIFY == plugin->transaction);
+
+  switch (op)
+  {
+  case GNUNET_PSYC_OP_ASSIGN:
+    return state_assign (plugin, plugin->insert_state_current, channel_key,
+                         name, value, value_size);
+
+  default: /** @todo implement more state operations */
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+}
+
+
+/**
+ * End modifying current state.
+ */
+static int
+state_modify_end (void *cls,
+                  const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+                  uint64_t message_id)
+{
+  struct Plugin *plugin = cls;
+  GNUNET_assert (TRANSACTION_STATE_MODIFY == plugin->transaction);
+
+  return
+    GNUNET_OK == exec_channel (plugin, plugin->delete_state_empty, channel_key)
+    && GNUNET_OK == update_message_id (plugin,
+                                       plugin->update_max_state_message_id,
+                                       channel_key, message_id)
+    && GNUNET_OK == transaction_commit (plugin)
+    ? GNUNET_OK : GNUNET_SYSERR;
+}
+
+
+/**
+ * Begin state synchronization.
+ */
+static int
+state_sync_begin (void *cls,
+                  const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key)
+{
+  struct Plugin *plugin = cls;
+  return exec_channel (plugin, plugin->delete_state_sync, channel_key);
+}
+
+
+/**
+ * Assign current value of a state variable.
+ *
+ * @see GNUNET_PSYCSTORE_state_modify()
+ *
+ * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+ */
+static int
+state_sync_assign (void *cls,
+                const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+                const char *name, const void *value, size_t value_size)
+{
+  struct Plugin *plugin = cls;
+  return state_assign (cls, plugin->insert_state_sync, channel_key,
+                       name, value, value_size);
+}
+
+
+/**
+ * End modifying current state.
+ */
+static int
+state_sync_end (void *cls,
+                const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+                uint64_t max_state_message_id,
+                uint64_t state_hash_message_id)
+{
+  struct Plugin *plugin = cls;
+  int ret = GNUNET_SYSERR;
+
+  if (TRANSACTION_NONE != plugin->transaction)
+  {
+    /** @todo FIXME: wait for other transaction to finish  */
+    return GNUNET_SYSERR;
+  }
+
+  GNUNET_OK == transaction_begin (plugin, TRANSACTION_STATE_SYNC)
+    && GNUNET_OK == exec_channel (plugin, plugin->delete_state, channel_key)
+    && GNUNET_OK == exec_channel (plugin, plugin->insert_state_from_sync,
+                                  channel_key)
+    && GNUNET_OK == exec_channel (plugin, plugin->delete_state_sync,
+                                  channel_key)
+    && GNUNET_OK == update_message_id (plugin,
+                                       plugin->update_state_hash_message_id,
+                                       channel_key, state_hash_message_id)
+    && GNUNET_OK == update_message_id (plugin,
+                                       plugin->update_max_state_message_id,
+                                       channel_key, max_state_message_id)
+    && GNUNET_OK == transaction_commit (plugin)
+    ? ret = GNUNET_OK
+    : transaction_rollback (plugin);
+  return ret;
+}
+
+
+/**
+ * Delete the whole state.
+ *
+ * @see GNUNET_PSYCSTORE_state_reset()
+ *
+ * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+ */
+static int
+state_reset (void *cls, const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key)
+{
+  struct Plugin *plugin = cls;
+  return exec_channel (plugin, plugin->delete_state, channel_key);
+}
+
+
+/**
+ * Update signed values of state variables in the state store.
+ *
+ * @see GNUNET_PSYCSTORE_state_hash_update()
+ *
+ * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+ */
+static int
+state_update_signed (void *cls,
+                     const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key)
+{
+  struct Plugin *plugin = cls;
+  return exec_channel (plugin, plugin->update_state_signed, channel_key);
+}
+
+
+/**
+ * Retrieve a state variable by name.
+ *
+ * @see GNUNET_PSYCSTORE_state_get()
+ *
+ * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+ */
+static int
+state_get (void *cls, const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+           const char *name, GNUNET_PSYCSTORE_StateCallback cb, void *cb_cls)
+{
+  struct Plugin *plugin = cls;
+  int ret = GNUNET_SYSERR;
+
+  sqlite3_stmt *stmt = plugin->select_state_one;
+
+  if (SQLITE_OK != sqlite3_bind_blob (stmt, 1, channel_key,
+                                      sizeof (*channel_key),
+                                      SQLITE_STATIC)
+      || SQLITE_OK != sqlite3_bind_text (stmt, 2, name, -1, SQLITE_STATIC))
+  {
+    LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sqlite3_bind");
+  }
+  else
+  {
+    switch (sqlite3_step (stmt))
+    {
+    case SQLITE_DONE:
+      ret = GNUNET_NO;
+      break;
+    case SQLITE_ROW:
+      ret = cb (cb_cls, name, sqlite3_column_blob (stmt, 0),
+                sqlite3_column_bytes (stmt, 0));
+      break;
+    default:
+      LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                  "sqlite3_step");
+    }
+  }
+
+  if (SQLITE_OK != sqlite3_reset (stmt))
+  {
+    LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sqlite3_reset");
+  }
+
+  return ret;
+}
+
+
+/**
+ * Retrieve all state variables for a channel with the given prefix.
+ *
+ * @see GNUNET_PSYCSTORE_state_get_prefix()
+ *
+ * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+ */
+static int
+state_get_prefix (void *cls, const struct GNUNET_CRYPTO_EddsaPublicKey 
*channel_key,
+                  const char *name, GNUNET_PSYCSTORE_StateCallback cb,
+                  void *cb_cls)
+{
+  struct Plugin *plugin = cls;
+  int ret = GNUNET_SYSERR;
+  sqlite3_stmt *stmt = plugin->select_state_prefix;
+  size_t name_len = strlen (name);
+
+  if (SQLITE_OK != sqlite3_bind_blob (stmt, 1, channel_key,
+                                      sizeof (*channel_key), SQLITE_STATIC)
+      || SQLITE_OK != sqlite3_bind_text (stmt, 2, name, name_len, 
SQLITE_STATIC)
+      || SQLITE_OK != sqlite3_bind_int (stmt, 3, name_len)
+      || SQLITE_OK != sqlite3_bind_text (stmt, 4, name, name_len, 
SQLITE_STATIC))
+  {
+    LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sqlite3_bind");
+  }
+  else
+  {
+    int sql_ret;
+    do
+    {
+      sql_ret = sqlite3_step (stmt);
+      switch (sql_ret)
+      {
+      case SQLITE_DONE:
+        if (ret != GNUNET_OK)
+          ret = GNUNET_NO;
+        break;
+      case SQLITE_ROW:
+        ret = cb (cb_cls, (const char *) sqlite3_column_text (stmt, 0),
+                  sqlite3_column_blob (stmt, 1),
+                  sqlite3_column_bytes (stmt, 1));
+        if (ret != GNUNET_YES)
+          sql_ret = SQLITE_DONE;
+        break;
+      default:
+        LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                    "sqlite3_step");
+      }
+    }
+    while (sql_ret == SQLITE_ROW);
+  }
+  if (SQLITE_OK != sqlite3_reset (stmt))
+  {
+    LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sqlite3_reset");
+  }
+  return ret;
+}
+
+
+/**
+ * Retrieve all signed state variables for a channel.
+ *
+ * @see GNUNET_PSYCSTORE_state_get_signed()
+ *
+ * @return #GNUNET_OK on success, else #GNUNET_SYSERR
+ */
+static int
+state_get_signed (void *cls,
+                  const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+                  GNUNET_PSYCSTORE_StateCallback cb, void *cb_cls)
+{
+  struct Plugin *plugin = cls;
+  int ret = GNUNET_SYSERR;
+
+  sqlite3_stmt *stmt = plugin->select_state_signed;
+
+  if (SQLITE_OK != sqlite3_bind_blob (stmt, 1, channel_key,
+                                      sizeof (*channel_key), SQLITE_STATIC))
+  {
+    LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sqlite3_bind");
+  }
+  else
+  {
+    int sql_ret;
+    do
+    {
+      sql_ret = sqlite3_step (stmt);
+      switch (sql_ret)
+      {
+      case SQLITE_DONE:
+        if (ret != GNUNET_OK)
+          ret = GNUNET_NO;
+        break;
+      case SQLITE_ROW:
+        ret = cb (cb_cls, (const char *) sqlite3_column_text (stmt, 0),
+                  sqlite3_column_blob (stmt, 1),
+                  sqlite3_column_bytes (stmt, 1));
+        if (ret != GNUNET_YES)
+          sql_ret = SQLITE_DONE;
+        break;
+      default:
+        LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                    "sqlite3_step");
+      }
+    }
+    while (sql_ret == SQLITE_ROW);
+  }
+
+  if (SQLITE_OK != sqlite3_reset (stmt))
+  {
+    LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+                "sqlite3_reset");
+  }
+
+  return ret;
+}
+
+
+/**
+ * Entry point for the plugin.
+ *
+ * @param cls The struct GNUNET_CONFIGURATION_Handle.
+ * @return NULL on error, otherwise the plugin context
+ */
+void *
+libgnunet_plugin_psycstore_sqlite_init (void *cls)
+{
+  static struct Plugin plugin;
+  const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
+  struct GNUNET_PSYCSTORE_PluginFunctions *api;
+
+  if (NULL != plugin.cfg)
+    return NULL;                /* can only initialize once! */
+  memset (&plugin, 0, sizeof (struct Plugin));
+  plugin.cfg = cfg;
+  if (GNUNET_OK != database_setup (&plugin))
+  {
+    database_shutdown (&plugin);
+    return NULL;
+  }
+  api = GNUNET_new (struct GNUNET_PSYCSTORE_PluginFunctions);
+  api->cls = &plugin;
+  api->membership_store = &sqlite_membership_store;
+  api->membership_test = &membership_test;
+  api->fragment_store = &fragment_store;
+  api->message_add_flags = &message_add_flags;
+  api->fragment_get = &fragment_get;
+  api->fragment_get_latest = &fragment_get_latest;
+  api->message_get = &message_get;
+  api->message_get_latest = &message_get_latest;
+  api->message_get_fragment = &message_get_fragment;
+  api->counters_message_get = &counters_message_get;
+  api->counters_state_get = &counters_state_get;
+  api->state_modify_begin = &state_modify_begin;
+  api->state_modify_op = &state_modify_op;
+  api->state_modify_end = &state_modify_end;
+  api->state_sync_begin = &state_sync_begin;
+  api->state_sync_assign = &state_sync_assign;
+  api->state_sync_end = &state_sync_end;
+  api->state_reset = &state_reset;
+  api->state_update_signed = &state_update_signed;
+  api->state_get = &state_get;
+  api->state_get_prefix = &state_get_prefix;
+  api->state_get_signed = &state_get_signed;
+
+  LOG (GNUNET_ERROR_TYPE_INFO, _("SQLite database running\n"));
+  return api;
+}
+
+
+/**
+ * Exit point from the plugin.
+ *
+ * @param cls The plugin context (as returned by "init")
+ * @return Always NULL
+ */
+void *
+libgnunet_plugin_psycstore_sqlite_done (void *cls)
+{
+  struct GNUNET_PSYCSTORE_PluginFunctions *api = cls;
+  struct Plugin *plugin = api->cls;
+
+  database_shutdown (plugin);
+  plugin->cfg = NULL;
+  GNUNET_free (api);
+  LOG (GNUNET_ERROR_TYPE_DEBUG, "SQLite plugin is finished\n");
+  return NULL;
+}
+
+/* end of plugin_psycstore_sqlite.c */
diff --git a/src/psycstore/psycstore.conf.in b/src/psycstore/psycstore.conf.in
new file mode 100644
index 0000000..3905db1
--- /dev/null
+++ b/src/psycstore/psycstore.conf.in
@@ -0,0 +1,28 @@
+[psycstore]
+START_ON_DEMAND = @START_ON_DEMAND@
+BINARY = gnunet-service-psycstore
+
+UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-psycstore.sock
+UNIX_MATCH_UID = YES
+UNIX_MATCH_GID = YES
+
address@hidden@PORT = 2111
+HOSTNAME = localhost
+ACCEPT_FROM = 127.0.0.1;
+ACCEPT_FROM6 = ::1;
+
+DATABASE = sqlite
+
+[psycstore-sqlite]
+FILENAME = $GNUNET_DATA_HOME/psycstore/sqlite.db
+
+[psycstore-mysql]
+DATABASE = gnunet
+CONFIG = ~/.my.cnf
+# USER = gnunet
+# PASSWORD =
+# HOST = localhost
+# PORT = 3306
+
+[psycstore-postgres]
+CONFIG = connect_timeout=10; dbname=gnunet
diff --git a/src/psycstore/psycstore.h b/src/psycstore/psycstore.h
new file mode 100644
index 0000000..9a1c06a
--- /dev/null
+++ b/src/psycstore/psycstore.h
@@ -0,0 +1,520 @@
+/*
+ * This file is part of GNUnet
+ * Copyright (C) 2013 GNUnet e.V.
+ *
+ * GNUnet is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * GNUnet 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
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+     SPDX-License-Identifier: AGPL3.0-or-later
+ */
+
+/**
+ * @file psycstore/psycstore.h
+ * @brief Common type definitions for the PSYCstore service and API.
+ * @author Gabor X Toth
+ */
+
+#ifndef GNUNET_PSYCSTORE_H
+#define GNUNET_PSYCSTORE_H
+
+#include "gnunet_common.h"
+
+
+GNUNET_NETWORK_STRUCT_BEGIN
+
+/**
+ * Answer from service to client about last operation.
+ */
+struct OperationResult
+{
+  /**
+   * Type: GNUNET_MESSAGE_TYPE_PSYCSTORE_RESULT_CODE
+   */
+  struct GNUNET_MessageHeader header;
+
+  uint32_t reserved GNUNET_PACKED;
+
+  /**
+   * Operation ID.
+   */
+  uint64_t op_id GNUNET_PACKED;
+
+  /**lowed by
+   * Status code for the operation.
+   */
+  uint64_t result_code GNUNET_PACKED;
+
+  /* followed by 0-terminated error message (on error) */
+
+};
+
+
+/**
+ * Answer from service to client about master counters.
+ *
+ * @see GNUNET_PSYCSTORE_counters_get()
+ */
+struct CountersResult
+{
+  /**
+   * Type: GNUNET_MESSAGE_TYPE_PSYCSTORE_RESULT_COUNTERS
+   */
+  struct GNUNET_MessageHeader header;
+
+  /**
+   * Status code for the operation:
+   * #GNUNET_OK: success, counter values are returned.
+   * #GNUNET_NO: no message has been sent to the channel yet.
+   * #GNUNET_SYSERR: an error occurred.
+   */
+  uint32_t result_code GNUNET_PACKED;
+
+  /**
+   * Operation ID.
+   */
+  uint64_t op_id GNUNET_PACKED;
+
+  uint64_t max_fragment_id GNUNET_PACKED;
+
+  uint64_t max_message_id GNUNET_PACKED;
+
+  uint64_t max_group_generation GNUNET_PACKED;
+
+  uint64_t max_state_message_id GNUNET_PACKED;
+};
+
+
+/**
+ * Answer from service to client containing a message fragment.
+ */
+struct FragmentResult
+{
+  /**
+   * Type: GNUNET_MESSAGE_TYPE_PSYCSTORE_RESULT_CODE
+   */
+  struct GNUNET_MessageHeader header;
+
+  uint32_t psycstore_flags GNUNET_PACKED;
+
+  /**
+   * Operation ID.
+   */
+  uint64_t op_id GNUNET_PACKED;
+
+  /* Followed by GNUNET_MULTICAST_MessageHeader */
+};
+
+
+/**
+ * Answer from service to client containing a state variable.
+ */
+struct StateResult
+{
+  /**
+   * Type: GNUNET_MESSAGE_TYPE_PSYCSTORE_RESULT_CODE
+   */
+  struct GNUNET_MessageHeader header;
+
+  uint16_t name_size GNUNET_PACKED;
+
+  uint16_t reserved GNUNET_PACKED;
+
+  /**
+   * Operation ID.
+   */
+  uint64_t op_id GNUNET_PACKED;
+
+  /* Followed by name and value */
+};
+
+
+/**
+ * Generic operation request.
+ */
+struct OperationRequest
+{
+  struct GNUNET_MessageHeader header;
+
+  uint32_t reserved GNUNET_PACKED;
+
+  /**
+   * Operation ID.
+   */
+  uint64_t op_id GNUNET_PACKED;
+
+  struct GNUNET_CRYPTO_EddsaPublicKey channel_key;
+};
+
+
+/**
+ * @see GNUNET_PSYCSTORE_membership_store()
+ */
+struct MembershipStoreRequest
+{
+  /**
+   * Type: GNUNET_MESSAGE_TYPE_PSYCSTORE_MEMBERSHIP_STORE
+   */
+  struct GNUNET_MessageHeader header;
+
+  uint32_t reserved GNUNET_PACKED;
+
+  /**
+   * Operation ID.
+   */
+  uint64_t op_id GNUNET_PACKED;
+
+  /**
+   * Channel's public key.
+   */
+  struct GNUNET_CRYPTO_EddsaPublicKey channel_key;
+
+  /**
+   * Slave's public key.
+   */
+  struct GNUNET_CRYPTO_EcdsaPublicKey slave_key;
+
+  uint64_t announced_at GNUNET_PACKED;
+  uint64_t effective_since GNUNET_PACKED;
+  uint64_t group_generation GNUNET_PACKED;
+  uint8_t did_join;
+};
+
+
+/**
+ * @see GNUNET_PSYCSTORE_membership_test()
+ */
+struct MembershipTestRequest
+{
+  /**
+   * Type: GNUNET_MESSAGE_TYPE_PSYCSTORE_MEMBERSHIP_TEST
+   */
+  struct GNUNET_MessageHeader header;
+
+  uint32_t reserved GNUNET_PACKED;
+
+  /**
+   * Operation ID.
+   */
+  uint64_t op_id GNUNET_PACKED;
+
+  /**
+   * Channel's public key.
+   */
+  struct GNUNET_CRYPTO_EddsaPublicKey channel_key;
+
+  /**
+   * Slave's public key.
+   */
+  struct GNUNET_CRYPTO_EcdsaPublicKey slave_key;
+
+  uint64_t message_id GNUNET_PACKED;
+
+  uint64_t group_generation GNUNET_PACKED;
+};
+
+
+/**
+ * @see GNUNET_PSYCSTORE_fragment_store()
+ */
+struct FragmentStoreRequest
+{
+  /**
+   * Type: GNUNET_MESSAGE_TYPE_PSYCSTORE_FRAGMENT_STORE
+   */
+  struct GNUNET_MessageHeader header;
+
+  /**
+   * enum GNUNET_PSYCSTORE_MessageFlags
+   */
+  uint32_t psycstore_flags GNUNET_PACKED;
+
+  /**
+   * Channel's public key.
+   */
+  struct GNUNET_CRYPTO_EddsaPublicKey channel_key;
+
+  /**
+   * Operation ID.
+   */
+  uint64_t op_id;
+
+  /* Followed by fragment */
+};
+
+
+/**
+ * @see GNUNET_PSYCSTORE_fragment_get()
+ */
+struct FragmentGetRequest
+{
+  /**
+   * Type: GNUNET_MESSAGE_TYPE_PSYCSTORE_FRAGMENT_GET
+   */
+  struct GNUNET_MessageHeader header;
+
+  uint32_t reserved GNUNET_PACKED;
+
+  /**
+   * Operation ID.
+   */
+  uint64_t op_id GNUNET_PACKED;
+
+  /**
+   * Channel's public key.
+   */
+  struct GNUNET_CRYPTO_EddsaPublicKey channel_key;
+
+  /**
+   * Slave's public key.
+   */
+  struct GNUNET_CRYPTO_EcdsaPublicKey slave_key;
+
+  /**
+   * First fragment ID to request.
+   */
+  uint64_t first_fragment_id GNUNET_PACKED;
+
+  /**
+   * Last fragment ID to request.
+   */
+  uint64_t last_fragment_id GNUNET_PACKED;
+
+  /**
+   * Maximum number of fragments to retrieve.
+   */
+  uint64_t fragment_limit GNUNET_PACKED;
+
+  /**
+   * Do membership test with @a slave_key before returning fragment?
+   * #GNUNET_YES or #GNUNET_NO
+   */
+  uint8_t do_membership_test;
+};
+
+
+/**
+ * @see GNUNET_PSYCSTORE_message_get()
+ */
+struct MessageGetRequest
+{
+  /**
+   * Type: GNUNET_MESSAGE_TYPE_PSYCSTORE_MESSAGE_GET
+   */
+  struct GNUNET_MessageHeader header;
+
+  uint32_t reserved GNUNET_PACKED;
+
+  /**
+   * Operation ID.
+   */
+  uint64_t op_id GNUNET_PACKED;
+
+  /**
+   * Channel's public key.
+   */
+  struct GNUNET_CRYPTO_EddsaPublicKey channel_key;
+
+  /**
+   * Slave's public key.
+   */
+  struct GNUNET_CRYPTO_EcdsaPublicKey slave_key;
+
+  /**
+   * First message ID to request.
+   */
+  uint64_t first_message_id GNUNET_PACKED;
+
+  /**
+   * Last message ID to request.
+   */
+  uint64_t last_message_id GNUNET_PACKED;
+
+  /**
+   * Maximum number of messages to retrieve.
+   */
+  uint64_t message_limit GNUNET_PACKED;
+
+  /**
+   * Maximum number of fragments to retrieve.
+   */
+  uint64_t fragment_limit GNUNET_PACKED;
+
+  /**
+   * Do membership test with @a slave_key before returning fragment?
+   * #GNUNET_YES or #GNUNET_NO
+   */
+  uint8_t do_membership_test;
+
+  /* Followed by method_prefix */
+};
+
+
+/**
+ * @see GNUNET_PSYCSTORE_message_get_fragment()
+ */
+struct MessageGetFragmentRequest
+{
+  /**
+   * Type: GNUNET_MESSAGE_TYPE_PSYCSTORE_MESSAGE_FRAGMENT_GET
+   */
+  struct GNUNET_MessageHeader header;
+
+  uint32_t reserved GNUNET_PACKED;
+
+  /**
+   * Operation ID.
+   */
+  uint64_t op_id GNUNET_PACKED;
+
+  /**
+   * Channel's public key.
+   */
+  struct GNUNET_CRYPTO_EddsaPublicKey channel_key;
+
+  /**
+   * Slave's public key.
+   */
+  struct GNUNET_CRYPTO_EcdsaPublicKey slave_key;
+
+  /**
+   * Requested message ID.
+   */
+  uint64_t message_id GNUNET_PACKED;
+
+  /**
+   * Requested fragment offset.
+   */
+  uint64_t fragment_offset GNUNET_PACKED;
+
+  /**
+   * Do membership test with @a slave_key before returning fragment?
+   * #GNUNET_YES or #GNUNET_NO
+   */
+  uint8_t do_membership_test;
+};
+
+
+/**
+ * @see GNUNET_PSYCSTORE_state_hash_update()
+ */
+struct StateHashUpdateRequest
+{
+  /**
+   * Type: GNUNET_MESSAGE_TYPE_PSYCSTORE_STATE_HASH_UPDATE
+   */
+  struct GNUNET_MessageHeader header;
+
+  uint32_t reserved GNUNET_PACKED;
+
+  /**
+   * Operation ID.
+   */
+  uint64_t op_id GNUNET_PACKED;
+
+  /**
+   * Channel's public key.
+   */
+  struct GNUNET_CRYPTO_EddsaPublicKey channel_key;
+
+  struct GNUNET_HashCode hash;
+};
+
+
+enum StateOpFlags
+{
+  STATE_OP_FIRST = 1 << 0,
+  STATE_OP_LAST = 1 << 1
+};
+
+
+/**
+ * @see GNUNET_PSYCSTORE_state_modify()
+ */
+struct StateModifyRequest
+{
+  /**
+   * Type: GNUNET_MESSAGE_TYPE_PSYCSTORE_STATE_MODIFY
+   */
+  struct GNUNET_MessageHeader header;
+
+  /**
+   * Operation ID.
+   */
+  uint64_t op_id GNUNET_PACKED;
+
+  /**
+   * ID of the message to apply the state changes in.
+   */
+  uint64_t message_id GNUNET_PACKED;
+
+  /**
+   * State delta of the message with ID @a message_id.
+   */
+  uint64_t state_delta GNUNET_PACKED;
+
+  /**
+   * Channel's public key.
+   */
+  struct GNUNET_CRYPTO_EddsaPublicKey channel_key;
+};
+
+
+/**
+ * @see GNUNET_PSYCSTORE_state_sync()
+ */
+struct StateSyncRequest
+{
+  /**
+   * Type: GNUNET_MESSAGE_TYPE_PSYCSTORE_STATE_SYNC
+   */
+  struct GNUNET_MessageHeader header;
+
+  /**
+   * Size of name, including NUL terminator.
+   */
+  uint16_t name_size GNUNET_PACKED;
+
+  /**
+   * OR'd StateOpFlags
+   */
+  uint8_t flags;
+
+  uint8_t reserved;
+
+  /**
+   * Operation ID.
+   */
+  uint64_t op_id GNUNET_PACKED;
+
+  /**
+   * ID of the message that contains the state_hash PSYC header variable.
+   */
+  uint64_t state_hash_message_id GNUNET_PACKED;
+
+  /**
+   * ID of the last stateful message before @a state_hash_message_id.
+   */
+  uint64_t max_state_message_id GNUNET_PACKED;
+
+  /**
+   * Channel's public key.
+   */
+  struct GNUNET_CRYPTO_EddsaPublicKey channel_key;
+
+  /* Followed by NUL-terminated name, then the value. */
+};
+
+
+GNUNET_NETWORK_STRUCT_END
+
+#endif
diff --git a/src/psycstore/psycstore_api.c b/src/psycstore/psycstore_api.c
new file mode 100644
index 0000000..ab4cd0f
--- /dev/null
+++ b/src/psycstore/psycstore_api.c
@@ -0,0 +1,1285 @@
+/*
+ * This file is part of GNUnet
+ * Copyright (C) 2013 GNUnet e.V.
+ *
+ * GNUnet is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * GNUnet 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
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+     SPDX-License-Identifier: AGPL3.0-or-later
+ */
+
+/**
+ * @file psycstore/psycstore_api.c
+ * @brief API to interact with the PSYCstore service
+ * @author Gabor X Toth
+ * @author Christian Grothoff
+ */
+
+#include <inttypes.h>
+
+#include "platform.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_constants.h"
+#include "gnunet_protocols.h"
+#include "gnunet_psycstore_service.h"
+#include "gnunet_multicast_service.h"
+#include "psycstore.h"
+
+#define LOG(kind,...) GNUNET_log_from (kind, "psycstore-api",__VA_ARGS__)
+
+/**
+ * Handle for an operation with the PSYCstore service.
+ */
+struct GNUNET_PSYCSTORE_OperationHandle
+{
+
+  /**
+   * Main PSYCstore handle.
+   */
+  struct GNUNET_PSYCSTORE_Handle *h;
+
+  /**
+   * Data callbacks.
+   */
+  union {
+    GNUNET_PSYCSTORE_FragmentCallback fragment_cb;
+    GNUNET_PSYCSTORE_CountersCallback counters_cb;
+    GNUNET_PSYCSTORE_StateCallback state_cb;
+  };
+
+  /**
+   * Closure for callbacks.
+   */
+  void *cls;
+
+  /**
+   * Message envelope.
+   */
+  struct GNUNET_MQ_Envelope *env;
+
+  /**
+   * Operation ID.
+   */
+  uint64_t op_id;
+};
+
+
+/**
+ * Handle for the service.
+ */
+struct GNUNET_PSYCSTORE_Handle
+{
+  /**
+   * Configuration to use.
+   */
+  const struct GNUNET_CONFIGURATION_Handle *cfg;
+
+  /**
+   * Client connection.
+   */
+  struct GNUNET_MQ_Handle *mq;
+
+  /**
+   * Async operations.
+   */
+  struct GNUNET_OP_Handle *op;
+
+  /**
+   * Task doing exponential back-off trying to reconnect.
+   */
+  struct GNUNET_SCHEDULER_Task *reconnect_task;
+
+  /**
+   * Delay for next connect retry.
+   */
+  struct GNUNET_TIME_Relative reconnect_delay;
+
+
+  GNUNET_PSYCSTORE_FragmentCallback *fragment_cb;
+
+  GNUNET_PSYCSTORE_CountersCallback *counters_cb;
+
+  GNUNET_PSYCSTORE_StateCallback *state_cb;
+  /**
+   * Closure for callbacks.
+   */
+  void *cb_cls;
+};
+
+
+static int
+check_result_code (void *cls, const struct OperationResult *opres)
+{
+  uint16_t size = ntohs (opres->header.size);
+  const char *str = (const char *) &opres[1];
+  if ( (sizeof (*opres) < size) &&
+       ('\0' != str[size - sizeof (*opres) - 1]) )
+  {
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+
+  return GNUNET_OK;
+}
+
+
+static void
+handle_result_code (void *cls, const struct OperationResult *opres)
+{
+  struct GNUNET_PSYCSTORE_Handle *h = cls;
+  struct GNUNET_PSYCSTORE_OperationHandle *op = NULL;
+  uint16_t size = ntohs (opres->header.size);
+
+  const char *
+    str = (sizeof (*opres) < size) ? (const char *) &opres[1] : "";
+
+  if (GNUNET_YES == GNUNET_OP_result (h->op, GNUNET_ntohll (opres->op_id),
+                                      GNUNET_ntohll (opres->result_code) + 
INT64_MIN,
+                                      str, size - sizeof (*opres), (void **) 
&op))
+  {
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "handle_result_code: Received result message with OP ID: %" PRIu64 
"\n",
+         GNUNET_ntohll (opres->op_id));
+    GNUNET_free (op);
+  }
+  else
+  {
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "handle_result_code: No callback registered for OP ID %" PRIu64 ".\n",
+         GNUNET_ntohll (opres->op_id));
+  }
+  h->reconnect_delay = GNUNET_TIME_UNIT_MILLISECONDS;
+}
+
+
+static void
+handle_result_counters (void *cls, const struct CountersResult *cres)
+{
+  struct GNUNET_PSYCSTORE_Handle *h = cls;
+  struct GNUNET_PSYCSTORE_OperationHandle *op = NULL;
+
+  if (GNUNET_YES == GNUNET_OP_get (h->op, GNUNET_ntohll (cres->op_id),
+                                   NULL, NULL, (void **) &op))
+  {
+    GNUNET_assert (NULL != op);
+    if (NULL != op->counters_cb)
+    {
+      op->counters_cb (op->cls,
+                       ntohl (cres->result_code),
+                       GNUNET_ntohll (cres->max_fragment_id),
+                       GNUNET_ntohll (cres->max_message_id),
+                       GNUNET_ntohll (cres->max_group_generation),
+                       GNUNET_ntohll (cres->max_state_message_id));
+    }
+    GNUNET_OP_remove (h->op, GNUNET_ntohll (cres->op_id));
+    GNUNET_free (op);
+  }
+  else
+  {
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "handle_result_counters: No callback registered for OP ID %" PRIu64 
".\n",
+         GNUNET_ntohll (cres->op_id));
+  }
+  h->reconnect_delay = GNUNET_TIME_UNIT_MILLISECONDS;
+}
+
+
+static int
+check_result_fragment (void *cls, const struct FragmentResult *fres)
+{
+  uint16_t size = ntohs (fres->header.size);
+  struct GNUNET_MULTICAST_MessageHeader *mmsg =
+    (struct GNUNET_MULTICAST_MessageHeader *) &fres[1];
+  if (sizeof (*fres) + sizeof (*mmsg) < size
+      && sizeof (*fres) + ntohs (mmsg->header.size) != size)
+  {
+    LOG (GNUNET_ERROR_TYPE_ERROR,
+         "check_result_fragment: Received message with invalid length %lu 
bytes.\n",
+         size, sizeof (*fres));
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+  return GNUNET_OK;
+}
+
+
+static void
+handle_result_fragment (void *cls, const struct FragmentResult *fres)
+{
+  struct GNUNET_PSYCSTORE_Handle *h = cls;
+  struct GNUNET_PSYCSTORE_OperationHandle *op = NULL;
+
+  if (GNUNET_YES == GNUNET_OP_get (h->op, GNUNET_ntohll (fres->op_id),
+                                   NULL, NULL, (void **) &op))
+  {
+    GNUNET_assert (NULL != op);
+    if (NULL != op->fragment_cb)
+      op->fragment_cb (op->cls,
+                       (struct GNUNET_MULTICAST_MessageHeader *) &fres[1],
+                       ntohl (fres->psycstore_flags));
+    //GNUNET_OP_remove (h->op, GNUNET_ntohll (fres->op_id));
+    //GNUNET_free (op);
+  }
+  else
+  {
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "handle_result_fragment: No callback registered for OP ID %" PRIu64 
".\n",
+         GNUNET_ntohll (fres->op_id));
+  }
+  h->reconnect_delay = GNUNET_TIME_UNIT_MILLISECONDS;
+}
+
+
+static int
+check_result_state (void *cls, const struct StateResult *sres)
+{
+  const char *name = (const char *) &sres[1];
+  uint16_t size = ntohs (sres->header.size);
+  uint16_t name_size = ntohs (sres->name_size);
+
+  if (name_size <= 2
+      || size - sizeof (*sres) < name_size
+      || '\0' != name[name_size - 1])
+  {
+    LOG (GNUNET_ERROR_TYPE_ERROR,
+         "check_result_state: Received state result message with invalid 
name.\n");
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+  return GNUNET_OK;
+}
+
+
+static void
+handle_result_state (void *cls, const struct StateResult *sres)
+{
+  struct GNUNET_PSYCSTORE_Handle *h = cls;
+  struct GNUNET_PSYCSTORE_OperationHandle *op = NULL;
+
+  const char *name = (const char *) &sres[1];
+  uint16_t name_size = ntohs (sres->name_size);
+
+  if (GNUNET_YES == GNUNET_OP_get (h->op, GNUNET_ntohll (sres->op_id),
+                                   NULL, NULL, (void **) &op))
+  {
+    GNUNET_assert (NULL != op);
+    if (NULL != op->state_cb)
+       op->state_cb (op->cls, name, (char *) &sres[1] + name_size,
+                     ntohs (sres->header.size) - sizeof (*sres) - name_size);
+    //GNUNET_OP_remove (h->op, GNUNET_ntohll (sres->op_id));
+    //GNUNET_free (op);
+  }
+  else
+  {
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "handle_result_state: No callback registered for OP ID %" PRIu64 
".\n",
+         GNUNET_ntohll (sres->op_id));
+  }
+  h->reconnect_delay = GNUNET_TIME_UNIT_MILLISECONDS;
+}
+
+
+static void
+reconnect (void *cls);
+
+
+/**
+ * Client disconnected from service.
+ *
+ * Reconnect after backoff period.=
+ */
+static void
+disconnected (void *cls, enum GNUNET_MQ_Error error)
+{
+  struct GNUNET_PSYCSTORE_Handle *h = cls;
+
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Origin client disconnected (%d), re-connecting\n",
+       (int) error);
+  if (NULL != h->mq)
+  {
+    GNUNET_MQ_destroy (h->mq);
+    GNUNET_OP_destroy (h->op);
+    h->mq = NULL;
+    h->op = NULL;
+  }
+
+  h->reconnect_task = GNUNET_SCHEDULER_add_delayed (h->reconnect_delay,
+                                                    &reconnect, h);
+  h->reconnect_delay = GNUNET_TIME_STD_BACKOFF (h->reconnect_delay);
+}
+
+
+static void
+do_connect (struct GNUNET_PSYCSTORE_Handle *h)
+{
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Connecting to PSYCstore service.\n");
+
+  struct GNUNET_MQ_MessageHandler handlers[] = {
+    GNUNET_MQ_hd_var_size (result_code,
+                           GNUNET_MESSAGE_TYPE_PSYCSTORE_RESULT_CODE,
+                           struct OperationResult,
+                           h),
+    GNUNET_MQ_hd_fixed_size (result_counters,
+                             GNUNET_MESSAGE_TYPE_PSYCSTORE_RESULT_COUNTERS,
+                             struct CountersResult,
+                             h),
+    GNUNET_MQ_hd_var_size (result_fragment,
+                           GNUNET_MESSAGE_TYPE_PSYCSTORE_RESULT_FRAGMENT,
+                           struct FragmentResult,
+                           h),
+    GNUNET_MQ_hd_var_size (result_state,
+                           GNUNET_MESSAGE_TYPE_PSYCSTORE_RESULT_STATE,
+                           struct StateResult,
+                           h),
+    GNUNET_MQ_handler_end ()
+  };
+
+  h->op = GNUNET_OP_create ();
+  GNUNET_assert (NULL == h->mq);
+  h->mq = GNUNET_CLIENT_connect (h->cfg, "psycstore",
+                                 handlers, disconnected, h);
+  GNUNET_assert (NULL != h->mq);
+}
+
+
+/**
+ * Try again to connect to the PSYCstore service.
+ *
+ * @param cls Handle to the PSYCstore service.
+ */
+static void
+reconnect (void *cls)
+{
+  struct GNUNET_PSYCSTORE_Handle *h = cls;
+
+  h->reconnect_task = NULL;
+  do_connect (cls);
+}
+
+
+/**
+ * Connect to the PSYCstore service.
+ *
+ * @param cfg The configuration to use
+ * @return Handle to use
+ */
+struct GNUNET_PSYCSTORE_Handle *
+GNUNET_PSYCSTORE_connect (const struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+  struct GNUNET_PSYCSTORE_Handle *h
+    = GNUNET_new (struct GNUNET_PSYCSTORE_Handle);
+  h->cfg = cfg;
+  h->reconnect_delay = GNUNET_TIME_UNIT_MILLISECONDS;
+  do_connect (h);
+  return h;
+}
+
+
+/**
+ * Disconnect from PSYCstore service
+ *
+ * @param h Handle to destroy
+ */
+void
+GNUNET_PSYCSTORE_disconnect (struct GNUNET_PSYCSTORE_Handle *h)
+{
+  GNUNET_assert (NULL != h);
+  if (h->reconnect_task != NULL)
+  {
+    GNUNET_SCHEDULER_cancel (h->reconnect_task);
+    h->reconnect_task = NULL;
+  }
+  if (NULL != h->mq)
+  {
+    // FIXME: free data structures for pending operations
+    GNUNET_MQ_destroy (h->mq);
+    h->mq = NULL;
+  }
+  GNUNET_free (h);
+}
+
+
+/**
+ * Message sent notification.
+ *
+ * Remove invalidated envelope pointer.
+ */
+static void
+message_sent (void *cls)
+{
+  struct GNUNET_PSYCSTORE_OperationHandle *op = cls;
+  op->env = NULL;
+}
+
+
+/**
+ * Create a new operation.
+ */
+static struct GNUNET_PSYCSTORE_OperationHandle *
+op_create (struct GNUNET_PSYCSTORE_Handle *h,
+           struct GNUNET_OP_Handle *hop,
+           GNUNET_PSYCSTORE_ResultCallback result_cb,
+           void *cls)
+{
+  struct GNUNET_PSYCSTORE_OperationHandle *
+    op = GNUNET_malloc (sizeof (*op));
+  op->h = h;
+  op->op_id = GNUNET_OP_add (hop,
+                             (GNUNET_ResultCallback) result_cb,
+                             cls, op);
+  return op;
+}
+
+
+/**
+ * Send a message associated with an operation.
+ *
+ * @param h
+ *        PSYCstore handle.
+ * @param op
+ *        Operation handle.
+ * @param env
+ *        Message envelope to send.
+ * @param[out] op_id
+ *        Operation ID to write in network byte order. NULL if not needed.
+ *
+ * @return Operation handle.
+ *
+ */
+static struct GNUNET_PSYCSTORE_OperationHandle *
+op_send (struct GNUNET_PSYCSTORE_Handle *h,
+         struct GNUNET_PSYCSTORE_OperationHandle *op,
+         struct GNUNET_MQ_Envelope *env,
+         uint64_t *op_id)
+{
+  op->env = env;
+  if (NULL != op_id)
+    *op_id = GNUNET_htonll (op->op_id);
+
+  GNUNET_MQ_notify_sent (env, message_sent, op);
+  GNUNET_MQ_send (h->mq, env);
+  return op;
+}
+
+
+/**
+ * Cancel a PSYCstore operation. Note that the operation MAY still
+ * be executed; this merely cancels the continuation; if the request
+ * was already transmitted, the service may still choose to complete
+ * the operation.
+ *
+ * @param op Operation to cancel.
+ *
+ * @return #GNUNET_YES if message was not sent yet and got discarded,
+ *         #GNUNET_NO  if it was already sent, and only the callbacks got 
cancelled.
+ */
+int
+GNUNET_PSYCSTORE_operation_cancel (struct GNUNET_PSYCSTORE_OperationHandle *op)
+{
+  struct GNUNET_PSYCSTORE_Handle *h = op->h;
+  int ret = GNUNET_NO;
+
+  if (NULL != op->env)
+  {
+    GNUNET_MQ_send_cancel (op->env);
+    ret = GNUNET_YES;
+  }
+
+  GNUNET_OP_remove (h->op, op->op_id);
+  GNUNET_free (op);
+
+  return ret;
+}
+
+
+/**
+ * Store join/leave events for a PSYC channel in order to be able to answer
+ * membership test queries later.
+ *
+ * @param h
+ *        Handle for the PSYCstore.
+ * @param channel_key
+ *        The channel where the event happened.
+ * @param slave_key
+ *        Public key of joining/leaving slave.
+ * @param did_join
+ *        #GNUNET_YES on join, #GNUNET_NO on part.
+ * @param announced_at
+ *        ID of the message that announced the membership change.
+ * @param effective_since
+ *        Message ID this membership change is in effect since.
+ *        For joins it is <= announced_at, for parts it is always 0.
+ * @param group_generation
+ *        In case of a part, the last group generation the slave has access to.
+ *        It has relevance when a larger message have fragments with different
+ *        group generations.
+ * @param result_cb
+ *        Callback to call with the result of the storage operation.
+ * @param cls
+ *        Closure for the callback.
+ *
+ * @return Operation handle that can be used to cancel the operation.
+ */
+struct GNUNET_PSYCSTORE_OperationHandle *
+GNUNET_PSYCSTORE_membership_store (struct GNUNET_PSYCSTORE_Handle *h,
+                                   const struct GNUNET_CRYPTO_EddsaPublicKey 
*channel_key,
+                                   const struct GNUNET_CRYPTO_EcdsaPublicKey 
*slave_key,
+                                   int did_join,
+                                   uint64_t announced_at,
+                                   uint64_t effective_since,
+                                   uint64_t group_generation,
+                                   GNUNET_PSYCSTORE_ResultCallback result_cb,
+                                   void *cls)
+{
+  GNUNET_assert (NULL != h);
+  GNUNET_assert (NULL != channel_key);
+  GNUNET_assert (NULL != slave_key);
+  GNUNET_assert (GNUNET_YES == did_join || GNUNET_NO == did_join);
+  GNUNET_assert (did_join
+                 ? effective_since <= announced_at
+                 : effective_since == 0);
+
+  struct MembershipStoreRequest *req;
+  struct GNUNET_MQ_Envelope *
+    env = GNUNET_MQ_msg (req, GNUNET_MESSAGE_TYPE_PSYCSTORE_MEMBERSHIP_STORE);
+  req->channel_key = *channel_key;
+  req->slave_key = *slave_key;
+  req->did_join = did_join;
+  req->announced_at = GNUNET_htonll (announced_at);
+  req->effective_since = GNUNET_htonll (effective_since);
+  req->group_generation = GNUNET_htonll (group_generation);
+
+  return
+    op_send (h, op_create (h, h->op, result_cb, cls),
+             env, &req->op_id);
+}
+
+
+/**
+ * Test if a member was admitted to the channel at the given message ID.
+ *
+ * This is useful when relaying and replaying messages to check if a particular
+ * slave has access to the message fragment with a given group generation.  It
+ * is also used when handling join requests to determine whether the slave is
+ * currently admitted to the channel.
+ *
+ * @param h
+ *        Handle for the PSYCstore.
+ * @param channel_key
+ *        The channel we are interested in.
+ * @param slave_key
+ *        Public key of slave whose membership to check.
+ * @param message_id
+ *        Message ID for which to do the membership test.
+ * @param group_generation
+ *        Group generation of the fragment of the message to test.
+ *        It has relevance if the message consists of multiple fragments with
+ *        different group generations.
+ * @param result_cb
+ *        Callback to call with the test result.
+ * @param cls
+ *        Closure for the callback.
+ *
+ * @return Operation handle that can be used to cancel the operation.
+ */
+struct GNUNET_PSYCSTORE_OperationHandle *
+GNUNET_PSYCSTORE_membership_test (struct GNUNET_PSYCSTORE_Handle *h,
+                                  const struct GNUNET_CRYPTO_EddsaPublicKey 
*channel_key,
+                                  const struct GNUNET_CRYPTO_EcdsaPublicKey 
*slave_key,
+                                  uint64_t message_id,
+                                  uint64_t group_generation,
+                                  GNUNET_PSYCSTORE_ResultCallback result_cb,
+                                  void *cls)
+{
+  struct MembershipTestRequest *req;
+  struct GNUNET_MQ_Envelope *
+    env = GNUNET_MQ_msg (req, GNUNET_MESSAGE_TYPE_PSYCSTORE_MEMBERSHIP_TEST);
+  req->channel_key = *channel_key;
+  req->slave_key = *slave_key;
+  req->message_id = GNUNET_htonll (message_id);
+  req->group_generation = GNUNET_htonll (group_generation);
+
+  return
+    op_send (h, op_create (h, h->op, result_cb, cls),
+             env, &req->op_id);
+}
+
+
+/**
+ * Store a message fragment sent to a channel.
+ *
+ * @param h Handle for the PSYCstore.
+ * @param channel_key The channel the message belongs to.
+ * @param message Message to store.
+ * @param psycstore_flags Flags indicating whether the PSYC message contains
+ *        state modifiers.
+ * @param result_cb Callback to call with the result of the operation.
+ * @param cls Closure for the callback.
+ *
+ * @return Handle that can be used to cancel the operation.
+ */
+struct GNUNET_PSYCSTORE_OperationHandle *
+GNUNET_PSYCSTORE_fragment_store (struct GNUNET_PSYCSTORE_Handle *h,
+                                 const struct GNUNET_CRYPTO_EddsaPublicKey 
*channel_key,
+                                 const struct GNUNET_MULTICAST_MessageHeader 
*msg,
+                                 enum GNUNET_PSYCSTORE_MessageFlags 
psycstore_flags,
+                                 GNUNET_PSYCSTORE_ResultCallback result_cb,
+                                 void *cls)
+{
+  uint16_t size = ntohs (msg->header.size);
+  struct FragmentStoreRequest *req;
+  struct GNUNET_MQ_Envelope *
+    env = GNUNET_MQ_msg_extra (req, size,
+                               GNUNET_MESSAGE_TYPE_PSYCSTORE_FRAGMENT_STORE);
+  req->channel_key = *channel_key;
+  req->psycstore_flags = htonl (psycstore_flags);
+  GNUNET_memcpy (&req[1], msg, size);
+
+  return
+    op_send (h, op_create (h, h->op, result_cb, cls),
+             env, &req->op_id);
+}
+
+
+/**
+ * Retrieve message fragments by fragment ID range.
+ *
+ * @param h
+ *        Handle for the PSYCstore.
+ * @param channel_key
+ *        The channel we are interested in.
+ * @param slave_key
+ *        The slave requesting the fragment.  If not NULL, a membership test is
+ *        performed first and the fragment is only returned if the slave has
+ *        access to it.
+ * @param first_fragment_id
+ *        First fragment ID to retrieve.
+ *        Use 0 to get the latest message fragment.
+ * @param last_fragment_id
+ *        Last consecutive fragment ID to retrieve.
+ *        Use 0 to get the latest message fragment.
+ * @param fragment_limit
+ *        Maximum number of fragments to retrieve.
+ * @param fragment_cb
+ *        Callback to call with the retrieved fragments.
+ * @param result_cb
+ *        Callback to call with the result of the operation.
+ * @param cls
+ *        Closure for the callbacks.
+ *
+ * @return Handle that can be used to cancel the operation.
+ */
+struct GNUNET_PSYCSTORE_OperationHandle *
+GNUNET_PSYCSTORE_fragment_get (struct GNUNET_PSYCSTORE_Handle *h,
+                               const struct GNUNET_CRYPTO_EddsaPublicKey 
*channel_key,
+                               const struct GNUNET_CRYPTO_EcdsaPublicKey 
*slave_key,
+                               uint64_t first_fragment_id,
+                               uint64_t last_fragment_id,
+                               GNUNET_PSYCSTORE_FragmentCallback fragment_cb,
+                               GNUNET_PSYCSTORE_ResultCallback result_cb,
+                               void *cls)
+{
+  struct FragmentGetRequest *req;
+  struct GNUNET_MQ_Envelope *
+    env = GNUNET_MQ_msg (req, GNUNET_MESSAGE_TYPE_PSYCSTORE_FRAGMENT_GET);
+  req->channel_key = *channel_key;
+  req->first_fragment_id = GNUNET_htonll (first_fragment_id);
+  req->last_fragment_id = GNUNET_htonll (last_fragment_id);
+  if (NULL != slave_key)
+  {
+    req->slave_key = *slave_key;
+    req->do_membership_test = GNUNET_YES;
+  }
+
+  struct GNUNET_PSYCSTORE_OperationHandle *
+    op = op_create (h, h->op, result_cb, cls);
+  op->fragment_cb = fragment_cb;
+  op->cls = cls;
+  return op_send (h, op, env, &req->op_id);
+}
+
+
+/**
+ * Retrieve latest message fragments.
+ *
+ * @param h
+ *        Handle for the PSYCstore.
+ * @param channel_key
+ *        The channel we are interested in.
+ * @param slave_key
+ *        The slave requesting the fragment.  If not NULL, a membership test is
+ *        performed first and the fragment is only returned if the slave has
+ *        access to it.
+ * @param first_fragment_id
+ *        First fragment ID to retrieve.
+ *        Use 0 to get the latest message fragment.
+ * @param last_fragment_id
+ *        Last consecutive fragment ID to retrieve.
+ *        Use 0 to get the latest message fragment.
+ * @param fragment_limit
+ *        Maximum number of fragments to retrieve.
+ * @param fragment_cb
+ *        Callback to call with the retrieved fragments.
+ * @param result_cb
+ *        Callback to call with the result of the operation.
+ * @param cls
+ *        Closure for the callbacks.
+ *
+ * @return Handle that can be used to cancel the operation.
+ */
+struct GNUNET_PSYCSTORE_OperationHandle *
+GNUNET_PSYCSTORE_fragment_get_latest (struct GNUNET_PSYCSTORE_Handle *h,
+                                      const struct 
GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+                                      const struct 
GNUNET_CRYPTO_EcdsaPublicKey *slave_key,
+                                      uint64_t fragment_limit,
+                                      GNUNET_PSYCSTORE_FragmentCallback 
fragment_cb,
+                                      GNUNET_PSYCSTORE_ResultCallback 
result_cb,
+                                      void *cls)
+{
+  struct FragmentGetRequest *req;
+  struct GNUNET_MQ_Envelope *
+    env = GNUNET_MQ_msg (req, GNUNET_MESSAGE_TYPE_PSYCSTORE_FRAGMENT_GET);
+  req->channel_key = *channel_key;
+  req->fragment_limit = GNUNET_ntohll (fragment_limit);
+  if (NULL != slave_key)
+  {
+    req->slave_key = *slave_key;
+    req->do_membership_test = GNUNET_YES;
+  }
+
+  struct GNUNET_PSYCSTORE_OperationHandle *
+    op = op_create (h, h->op, result_cb, cls);
+  op->fragment_cb = fragment_cb;
+  op->cls = cls;
+  return op_send (h, op, env, &req->op_id);
+}
+
+
+/**
+ * Retrieve all fragments of messages in a message ID range.
+ *
+ * @param h
+ *        Handle for the PSYCstore.
+ * @param channel_key
+ *        The channel we are interested in.
+ * @param slave_key
+ *        The slave requesting the message.
+ *        If not NULL, a membership test is performed first
+ *        and the message is only returned if the slave has access to it.
+ * @param first_message_id
+ *        First message ID to retrieve.
+ * @param last_message_id
+ *        Last consecutive message ID to retrieve.
+ * @param fragment_limit
+ *        Maximum number of fragments to retrieve.
+ * @param method_prefix
+ *        Retrieve only messages with a matching method prefix.
+ * @todo Implement method_prefix query.
+ * @param fragment_cb
+ *        Callback to call with the retrieved fragments.
+ * @param result_cb
+ *        Callback to call with the result of the operation.
+ * @param cls
+ *        Closure for the callbacks.
+ *
+ * @return Handle that can be used to cancel the operation.
+ */
+struct GNUNET_PSYCSTORE_OperationHandle *
+GNUNET_PSYCSTORE_message_get (struct GNUNET_PSYCSTORE_Handle *h,
+                              const struct GNUNET_CRYPTO_EddsaPublicKey 
*channel_key,
+                              const struct GNUNET_CRYPTO_EcdsaPublicKey 
*slave_key,
+                              uint64_t first_message_id,
+                              uint64_t last_message_id,
+                              uint64_t fragment_limit,
+                              const char *method_prefix,
+                              GNUNET_PSYCSTORE_FragmentCallback fragment_cb,
+                              GNUNET_PSYCSTORE_ResultCallback result_cb,
+                              void *cls)
+{
+  struct MessageGetRequest *req;
+  if (NULL == method_prefix)
+    method_prefix = "";
+  uint16_t method_size = strnlen (method_prefix,
+                                  GNUNET_MAX_MESSAGE_SIZE
+                                  - sizeof (*req)) + 1;
+
+  struct GNUNET_MQ_Envelope *
+    env = GNUNET_MQ_msg_extra (req, method_size,
+                               GNUNET_MESSAGE_TYPE_PSYCSTORE_MESSAGE_GET);
+  req->channel_key = *channel_key;
+  req->first_message_id = GNUNET_htonll (first_message_id);
+  req->last_message_id = GNUNET_htonll (last_message_id);
+  req->fragment_limit = GNUNET_htonll (fragment_limit);
+  if (NULL != slave_key)
+  {
+    req->slave_key = *slave_key;
+    req->do_membership_test = GNUNET_YES;
+  }
+  GNUNET_memcpy (&req[1], method_prefix, method_size);
+  ((char *) &req[1])[method_size - 1] = '\0';
+
+  struct GNUNET_PSYCSTORE_OperationHandle *
+    op = op_create (h, h->op, result_cb, cls);
+  op->fragment_cb = fragment_cb;
+  op->cls = cls;
+  return op_send (h, op, env, &req->op_id);
+}
+
+
+/**
+ * Retrieve all fragments of the latest messages.
+ *
+ * @param h
+ *        Handle for the PSYCstore.
+ * @param channel_key
+ *        The channel we are interested in.
+ * @param slave_key
+ *        The slave requesting the message.
+ *        If not NULL, a membership test is performed first
+ *        and the message is only returned if the slave has access to it.
+ * @param message_limit
+ *        Maximum number of messages to retrieve.
+ * @param method_prefix
+ *        Retrieve only messages with a matching method prefix.
+ * @todo Implement method_prefix query.
+ * @param fragment_cb
+ *        Callback to call with the retrieved fragments.
+ * @param result_cb
+ *        Callback to call with the result of the operation.
+ * @param cls
+ *        Closure for the callbacks.
+ *
+ * @return Handle that can be used to cancel the operation.
+ */
+struct GNUNET_PSYCSTORE_OperationHandle *
+GNUNET_PSYCSTORE_message_get_latest (struct GNUNET_PSYCSTORE_Handle *h,
+                                     const struct GNUNET_CRYPTO_EddsaPublicKey 
*channel_key,
+                                     const struct GNUNET_CRYPTO_EcdsaPublicKey 
*slave_key,
+                                     uint64_t message_limit,
+                                     const char *method_prefix,
+                                     GNUNET_PSYCSTORE_FragmentCallback 
fragment_cb,
+                                     GNUNET_PSYCSTORE_ResultCallback result_cb,
+                                     void *cls)
+{
+  struct MessageGetRequest *req;
+
+  if (NULL == method_prefix)
+    method_prefix = "";
+  uint16_t method_size = strnlen (method_prefix,
+                                  GNUNET_MAX_MESSAGE_SIZE
+                                  - sizeof (*req)) + 1;
+  GNUNET_assert ('\0' == method_prefix[method_size - 1]);
+
+  struct GNUNET_MQ_Envelope *
+    env = GNUNET_MQ_msg_extra (req, method_size,
+                               GNUNET_MESSAGE_TYPE_PSYCSTORE_MESSAGE_GET);
+  req->channel_key = *channel_key;
+  req->message_limit = GNUNET_ntohll (message_limit);
+  if (NULL != slave_key)
+  {
+    req->slave_key = *slave_key;
+    req->do_membership_test = GNUNET_YES;
+  }
+  GNUNET_memcpy (&req[1], method_prefix, method_size);
+
+  struct GNUNET_PSYCSTORE_OperationHandle *
+    op = op_create (h, h->op, result_cb, cls);
+  op->fragment_cb = fragment_cb;
+  op->cls = cls;
+  return op_send (h, op, env, &req->op_id);
+}
+
+
+/**
+ * Retrieve a fragment of message specified by its message ID and fragment
+ * offset.
+ *
+ * @param h
+ *        Handle for the PSYCstore.
+ * @param channel_key
+ *        The channel we are interested in.
+ * @param slave_key
+ *        The slave requesting the message fragment.  If not NULL, a membership
+ *        test is performed first and the message fragment is only returned
+ *        if the slave has access to it.
+ * @param message_id
+ *        Message ID to retrieve.  Use 0 to get the latest message.
+ * @param fragment_offset
+ *        Offset of the fragment to retrieve.
+ * @param fragment_cb
+ *        Callback to call with the retrieved fragments.
+ * @param result_cb
+ *        Callback to call with the result of the operation.
+ * @param cls
+ *        Closure for the callbacks.
+ *
+ * @return Handle that can be used to cancel the operation.
+ */
+struct GNUNET_PSYCSTORE_OperationHandle *
+GNUNET_PSYCSTORE_message_get_fragment (struct GNUNET_PSYCSTORE_Handle *h,
+                                       const struct 
GNUNET_CRYPTO_EddsaPublicKey *channel_key,
+                                       const struct 
GNUNET_CRYPTO_EcdsaPublicKey *slave_key,
+                                       uint64_t message_id,
+                                       uint64_t fragment_offset,
+                                       GNUNET_PSYCSTORE_FragmentCallback 
fragment_cb,
+                                       GNUNET_PSYCSTORE_ResultCallback 
result_cb,
+                                       void *cls)
+{
+  struct MessageGetFragmentRequest *req;
+  struct GNUNET_MQ_Envelope *
+    env = GNUNET_MQ_msg (req, 
GNUNET_MESSAGE_TYPE_PSYCSTORE_MESSAGE_GET_FRAGMENT);
+
+  req->channel_key = *channel_key;
+  req->message_id = GNUNET_htonll (message_id);
+  req->fragment_offset = GNUNET_htonll (fragment_offset);
+  if (NULL != slave_key)
+  {
+    req->slave_key = *slave_key;
+    req->do_membership_test = GNUNET_YES;
+  }
+
+  struct GNUNET_PSYCSTORE_OperationHandle *
+    op = op_create (h, h->op, result_cb, cls);
+  op->fragment_cb = fragment_cb;
+  op->cls = cls;
+  return op_send (h, op, env, &req->op_id);
+}
+
+
+/**
+ * Retrieve latest values of counters for a channel master.
+ *
+ * The current value of counters are needed when a channel master is restarted,
+ * so that it can continue incrementing the counters from their last value.
+ *
+ * @param h
+ *        Handle for the PSYCstore.
+ * @param channel_key
+ *        Public key that identifies the channel.
+ * @param ccb
+ *        Callback to call with the result.
+ * @param ccb_cls
+ *        Closure for the @a ccb callback.
+ *
+ * @return Handle that can be used to cancel the operation.
+ */
+struct GNUNET_PSYCSTORE_OperationHandle *
+GNUNET_PSYCSTORE_counters_get (struct GNUNET_PSYCSTORE_Handle *h,
+                               struct GNUNET_CRYPTO_EddsaPublicKey 
*channel_key,
+                               GNUNET_PSYCSTORE_CountersCallback counters_cb,
+                               void *cls)
+{
+  struct OperationRequest *req;
+  struct GNUNET_MQ_Envelope *
+    env = GNUNET_MQ_msg (req, GNUNET_MESSAGE_TYPE_PSYCSTORE_COUNTERS_GET);
+  req->channel_key = *channel_key;
+
+  struct GNUNET_PSYCSTORE_OperationHandle *
+    op = op_create (h, h->op, NULL, NULL);
+  op->counters_cb = counters_cb;
+  op->cls = cls;
+  return op_send (h, op, env, &req->op_id);
+}
+
+
+/**
+ * Apply modifiers of a message to the current channel state.
+ *
+ * An error is returned if there are missing messages containing state
+ * operations before the current one.
+ *
+ * @param h
+ *        Handle for the PSYCstore.
+ * @param channel_key
+ *        The channel we are interested in.
+ * @param message_id
+ *        ID of the message that contains the @a modifiers.
+ * @param state_delta
+ *        Value of the _state_delta PSYC header variable of the message.
+ * @param result_cb
+ *        Callback to call with the result of the operation.
+ * @param cls
+ *        Closure for @a result_cb.
+ *
+ * @return Handle that can be used to cancel the operation.
+ */
+struct GNUNET_PSYCSTORE_OperationHandle *
+GNUNET_PSYCSTORE_state_modify (struct GNUNET_PSYCSTORE_Handle *h,
+                               const struct GNUNET_CRYPTO_EddsaPublicKey 
*channel_key,
+                               uint64_t message_id,
+                               uint64_t state_delta,
+                               GNUNET_PSYCSTORE_ResultCallback result_cb,
+                               void *cls)
+{
+  struct StateModifyRequest *req;
+  struct GNUNET_MQ_Envelope *
+    env = GNUNET_MQ_msg (req, GNUNET_MESSAGE_TYPE_PSYCSTORE_STATE_MODIFY);
+  req->channel_key = *channel_key;
+  req->message_id = GNUNET_htonll (message_id);
+  req->state_delta = GNUNET_htonll (state_delta);
+
+  return op_send (h, op_create (h, h->op, result_cb, cls),
+                  env, &req->op_id);
+}
+
+
+struct StateSyncClosure
+{
+  GNUNET_PSYCSTORE_ResultCallback result_cb;
+  void *cls;
+  uint8_t last;
+};
+
+
+static void
+state_sync_result (void *cls, int64_t result,
+                   const char *err_msg, uint16_t err_msg_size)
+{
+  struct StateSyncClosure *ssc = cls;
+  if (GNUNET_OK != result || ssc->last)
+    ssc->result_cb (ssc->cls, result, err_msg, err_msg_size);
+  GNUNET_free (ssc);
+}
+
+
+/**
+ * Store synchronized state.
+ *
+ * @param h
+ *        Handle for the PSYCstore.
+ * @param channel_key
+ *        The channel we are interested in.
+ * @param max_state_message_id
+ *        ID of the last stateful message before @a state_hash_message_id.
+ * @param state_hash_message_id
+ *        ID of the message that contains the state_hash PSYC header variable.
+ * @param modifier_count
+ *        Number of elements in the @a modifiers array.
+ * @param modifiers
+ *        Full state to store.
+ * @param result_cb
+ *        Callback to call with the result of the operation.
+ * @param cls
+ *        Closure for the callback.
+ *
+ * @return Handle that can be used to cancel the operation.
+ */
+struct GNUNET_PSYCSTORE_OperationHandle *
+GNUNET_PSYCSTORE_state_sync (struct GNUNET_PSYCSTORE_Handle *h,
+                             const struct GNUNET_CRYPTO_EddsaPublicKey 
*channel_key,
+                             uint64_t max_state_message_id,
+                             uint64_t state_hash_message_id,
+                             size_t modifier_count,
+                             const struct GNUNET_PSYC_Modifier *modifiers,
+                             GNUNET_PSYCSTORE_ResultCallback result_cb,
+                             void *cls)
+{
+  struct GNUNET_PSYCSTORE_OperationHandle *op = NULL;
+  size_t i;
+
+  for (i = 0; i < modifier_count; i++) {
+    struct StateSyncRequest *req;
+    uint16_t name_size = strlen (modifiers[i].name) + 1;
+
+    struct GNUNET_MQ_Envelope *
+      env = GNUNET_MQ_msg_extra (req,
+                                 sizeof (*req) + name_size + 
modifiers[i].value_size,
+                                 GNUNET_MESSAGE_TYPE_PSYCSTORE_STATE_SYNC);
+
+    req->header.type = htons (GNUNET_MESSAGE_TYPE_PSYCSTORE_STATE_SYNC);
+    req->header.size = htons (sizeof (*req) + name_size
+                              + modifiers[i].value_size);
+    req->channel_key = *channel_key;
+    req->max_state_message_id = GNUNET_htonll (max_state_message_id);
+    req->state_hash_message_id = GNUNET_htonll (state_hash_message_id);
+    req->name_size = htons (name_size);
+    req->flags
+      = (0 == i)
+      ? STATE_OP_FIRST
+      : (modifier_count - 1 == i)
+      ? STATE_OP_LAST
+      : 0;
+
+    GNUNET_memcpy (&req[1], modifiers[i].name, name_size);
+    GNUNET_memcpy ((char *) &req[1] + name_size, modifiers[i].value, 
modifiers[i].value_size);
+
+    struct StateSyncClosure *ssc = GNUNET_malloc (sizeof (*ssc));
+    ssc->last = (req->flags & STATE_OP_LAST);
+    ssc->result_cb = result_cb;
+    ssc->cls = cls;
+
+    op_send (h, op_create (h, h->op, state_sync_result, ssc),
+             env, &req->op_id);
+  }
+  // FIXME: only one operation is returned,
+  //        add pointers to other operations and make all cancellable.
+  return op;
+}
+
+
+/**
+ * Reset the state of a channel.
+ *
+ * Delete all state variables stored for the given channel.
+ *
+ * @param h
+ *        Handle for the PSYCstore.
+ * @param channel_key
+ *        The channel we are interested in.
+ * @param result_cb
+ *        Callback to call with the result of the operation.
+ * @param cls
+ *        Closure for the callback.
+ *
+ * @return Handle that can be used to cancel the operation.
+ */
+struct GNUNET_PSYCSTORE_OperationHandle *
+GNUNET_PSYCSTORE_state_reset (struct GNUNET_PSYCSTORE_Handle *h,
+                              const struct GNUNET_CRYPTO_EddsaPublicKey
+                              *channel_key,
+                              GNUNET_PSYCSTORE_ResultCallback result_cb,
+                              void *cls)
+{
+  struct OperationRequest *req;
+  struct GNUNET_MQ_Envelope *
+    env = GNUNET_MQ_msg (req, GNUNET_MESSAGE_TYPE_PSYCSTORE_STATE_RESET);
+  req->channel_key = *channel_key;
+
+  return
+    op_send (h, op_create (h, h->op, result_cb, cls),
+             env, &req->op_id);
+}
+
+
+/**
+ * Update signed values of state variables in the state store.
+ *
+ * @param h
+ *        Handle for the PSYCstore.
+ * @param channel_key
+ *        The channel we are interested in.
+ * @param message_id
+ *        Message ID that contained the state @a hash.
+ * @param hash
+ *        Hash of the serialized full state.
+ * @param result_cb
+ *        Callback to call with the result of the operation.
+ * @param cls
+ *        Closure for the callback.
+ */
+struct GNUNET_PSYCSTORE_OperationHandle *
+GNUNET_PSYCSTORE_state_hash_update (struct GNUNET_PSYCSTORE_Handle *h,
+                                    const struct GNUNET_CRYPTO_EddsaPublicKey 
*channel_key,
+                                    uint64_t message_id,
+                                    const struct GNUNET_HashCode *hash,
+                                    GNUNET_PSYCSTORE_ResultCallback result_cb,
+                                    void *cls)
+{
+  struct StateHashUpdateRequest *req;
+  struct GNUNET_MQ_Envelope *
+    env = GNUNET_MQ_msg (req, GNUNET_MESSAGE_TYPE_PSYCSTORE_STATE_HASH_UPDATE);
+  req->channel_key = *channel_key;
+  req->hash = *hash;
+
+  return
+    op_send (h, op_create (h, h->op, result_cb, cls),
+             env, &req->op_id);
+}
+
+
+/**
+ * Retrieve the best matching state variable.
+ *
+ * @param h
+ *        Handle for the PSYCstore.
+ * @param channel_key
+ *        The channel we are interested in.
+ * @param name
+ *        Name of variable to match, the returned variable might be less 
specific.
+ * @param state_cb
+ *        Callback to return the matching state variable.
+ * @param result_cb
+ *        Callback to call with the result of the operation.
+ * @param cls
+ *        Closure for the callbacks.
+ *
+ * @return Handle that can be used to cancel the operation.
+ */
+struct GNUNET_PSYCSTORE_OperationHandle *
+GNUNET_PSYCSTORE_state_get (struct GNUNET_PSYCSTORE_Handle *h,
+                            const struct GNUNET_CRYPTO_EddsaPublicKey 
*channel_key,
+                            const char *name,
+                            GNUNET_PSYCSTORE_StateCallback state_cb,
+                            GNUNET_PSYCSTORE_ResultCallback result_cb,
+                            void *cls)
+{
+  size_t name_size = strlen (name) + 1;
+  struct OperationRequest *req;
+  struct GNUNET_MQ_Envelope *
+    env = GNUNET_MQ_msg_extra (req, name_size,
+                               GNUNET_MESSAGE_TYPE_PSYCSTORE_STATE_GET);
+  req->channel_key = *channel_key;
+  GNUNET_memcpy (&req[1], name, name_size);
+
+  struct GNUNET_PSYCSTORE_OperationHandle *
+    op = op_create (h, h->op, result_cb, cls);
+  op->state_cb = state_cb;
+  op->cls = cls;
+  return op_send (h, op, env, &req->op_id);
+}
+
+
+/**
+ * Retrieve all state variables for a channel with the given prefix.
+ *
+ * @param h
+ *        Handle for the PSYCstore.
+ * @param channel_key
+ *        The channel we are interested in.
+ * @param name_prefix
+ *        Prefix of state variable names to match.
+ * @param state_cb
+ *        Callback to return matching state variables.
+ * @param result_cb
+ *        Callback to call with the result of the operation.
+ * @param cls
+ *        Closure for the callbacks.
+ *
+ * @return Handle that can be used to cancel the operation.
+ */
+struct GNUNET_PSYCSTORE_OperationHandle *
+GNUNET_PSYCSTORE_state_get_prefix (struct GNUNET_PSYCSTORE_Handle *h,
+                                   const struct GNUNET_CRYPTO_EddsaPublicKey 
*channel_key,
+                                   const char *name_prefix,
+                                   GNUNET_PSYCSTORE_StateCallback state_cb,
+                                   GNUNET_PSYCSTORE_ResultCallback result_cb,
+                                   void *cls)
+{
+  size_t name_size = strlen (name_prefix) + 1;
+  struct OperationRequest *req;
+  struct GNUNET_MQ_Envelope *
+    env = GNUNET_MQ_msg_extra (req, name_size,
+                               GNUNET_MESSAGE_TYPE_PSYCSTORE_STATE_GET_PREFIX);
+  req->channel_key = *channel_key;
+  GNUNET_memcpy (&req[1], name_prefix, name_size);
+
+  struct GNUNET_PSYCSTORE_OperationHandle *
+    op = op_create (h, h->op, result_cb, cls);
+  op->state_cb = state_cb;
+  op->cls = cls;
+  return op_send (h, op, env, &req->op_id);
+}
+
+/* end of psycstore_api.c */
diff --git a/src/psycstore/test_plugin_psycstore.c 
b/src/psycstore/test_plugin_psycstore.c
new file mode 100644
index 0000000..ff4eac8
--- /dev/null
+++ b/src/psycstore/test_plugin_psycstore.c
@@ -0,0 +1,532 @@
+/*
+ * This file is part of GNUnet
+ * Copyright (C) 2013 GNUnet e.V.
+ *
+ * GNUnet is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * GNUnet 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
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+     SPDX-License-Identifier: AGPL3.0-or-later
+ */
+
+/**
+ * @author Gabor X Toth
+ * @author Christian Grothoff
+ *
+ * @file
+ * Test for the PSYCstore plugins.
+ */
+
+#include <inttypes.h>
+
+#include "platform.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_testing_lib.h"
+#include "gnunet_psycstore_plugin.h"
+#include "gnunet_psycstore_service.h"
+#include "gnunet_multicast_service.h"
+
+#define DEBUG_PSYCSTORE GNUNET_EXTRA_LOGGING
+#if DEBUG_PSYCSTORE
+# define LOG_LEVEL "DEBUG"
+#else
+# define LOG_LEVEL "WARNING"
+#endif
+
+#define C2ARG(str) str, (sizeof (str) - 1)
+
+#define LOG(kind,...)                                                          
\
+  GNUNET_log_from (kind, "test-plugin-psycstore", __VA_ARGS__)
+
+static int ok;
+
+/**
+ * Name of plugin under test.
+ */
+static const char *plugin_name;
+
+static struct GNUNET_CRYPTO_EddsaPrivateKey *channel_key;
+static struct GNUNET_CRYPTO_EcdsaPrivateKey *slave_key;
+
+static struct GNUNET_CRYPTO_EddsaPublicKey channel_pub_key;
+static struct GNUNET_CRYPTO_EcdsaPublicKey slave_pub_key;
+
+/**
+ * Function called when the service shuts down.  Unloads our psycstore
+ * plugin.
+ *
+ * @param api api to unload
+ */
+static void
+unload_plugin (struct GNUNET_PSYCSTORE_PluginFunctions *api)
+{
+  char *libname;
+
+  GNUNET_asprintf (&libname, "libgnunet_plugin_psycstore_%s", plugin_name);
+  GNUNET_break (NULL == GNUNET_PLUGIN_unload (libname, api));
+  GNUNET_free (libname);
+}
+
+
+/**
+ * Load the psycstore plugin.
+ *
+ * @param cfg configuration to pass
+ * @return NULL on error
+ */
+static struct GNUNET_PSYCSTORE_PluginFunctions *
+load_plugin (const struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+  struct GNUNET_PSYCSTORE_PluginFunctions *ret;
+  char *libname;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO, _ ("Loading `%s' psycstore plugin\n"),
+              plugin_name);
+  GNUNET_asprintf (&libname, "libgnunet_plugin_psycstore_%s", plugin_name);
+  if (NULL == (ret = GNUNET_PLUGIN_load (libname, (void*) cfg)))
+  {
+    FPRINTF (stderr, "Failed to load plugin `%s'!\n", plugin_name);
+    return NULL;
+  }
+  GNUNET_free (libname);
+  return ret;
+}
+
+
+#define MAX_MSG 16
+
+struct FragmentClosure
+{
+  uint8_t n;
+  uint64_t flags[MAX_MSG];
+  struct GNUNET_MULTICAST_MessageHeader *msg[MAX_MSG];
+};
+
+static int
+fragment_cb (void *cls, struct GNUNET_MULTICAST_MessageHeader *msg2,
+             enum GNUNET_PSYCSTORE_MessageFlags flags)
+{
+  struct FragmentClosure *fcls = cls;
+  struct GNUNET_MULTICAST_MessageHeader *msg1;
+  uint64_t flags1;
+  int ret;
+
+  if (fcls->n >= MAX_MSG)
+    {
+      GNUNET_break (0);
+      return GNUNET_SYSERR;
+    }
+  msg1 = fcls->msg[fcls->n];
+  flags1 = fcls->flags[fcls->n++];
+  if (NULL == msg1)
+    {
+      GNUNET_break (0);
+      return GNUNET_SYSERR;
+    }
+
+  if (flags1 == flags && msg1->header.size == msg2->header.size
+      && 0 == memcmp (msg1, msg2, ntohs (msg1->header.size)))
+  {
+    LOG (GNUNET_ERROR_TYPE_DEBUG, "Fragment %llu matches\n",
+         GNUNET_ntohll (msg1->fragment_id));
+    ret = GNUNET_YES;
+  }
+  else
+  {
+    LOG (GNUNET_ERROR_TYPE_ERROR, "Fragment %llu differs\n",
+         GNUNET_ntohll (msg1->fragment_id));
+    ret = GNUNET_SYSERR;
+  }
+
+  GNUNET_free (msg2);
+  return ret;
+}
+
+
+struct StateClosure {
+  size_t n;
+  char *name[16];
+  void *value[16];
+  size_t value_size[16];
+};
+
+static int
+state_cb (void *cls, const char *name, const void *value, uint32_t value_size)
+{
+  struct StateClosure *scls = cls;
+  const void *val = scls->value[scls->n]; // FIXME: check for n out-of-bounds 
FIRST!
+  size_t val_size = scls->value_size[scls->n++];
+
+  /* FIXME: check name */
+
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "  name = %s, value_size = %u\n",
+       name, value_size);
+
+  return GNUNET_YES;
+  return value_size == val_size && 0 == memcmp (value, val, val_size)
+    ? GNUNET_YES
+    : GNUNET_SYSERR;
+}
+
+
+static void
+run (void *cls, char *const *args, const char *cfgfile,
+     const struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+  struct GNUNET_PSYCSTORE_PluginFunctions *db;
+
+  ok = 1;
+  db = load_plugin (cfg);
+  if (NULL == db)
+  {
+    FPRINTF (stderr,
+             "%s",
+            "Failed to initialize PSYCstore.  "
+             "Database likely not setup, skipping test.\n");
+    ok = 77;
+    return;
+  }
+
+  /* Store & test membership */
+
+  LOG (GNUNET_ERROR_TYPE_INFO, "MEMBERSHIP\n");
+
+  channel_key = GNUNET_CRYPTO_eddsa_key_create ();
+  slave_key = GNUNET_CRYPTO_ecdsa_key_create ();
+
+  GNUNET_CRYPTO_eddsa_key_get_public (channel_key,
+                                                  &channel_pub_key);
+  GNUNET_CRYPTO_ecdsa_key_get_public (slave_key, &slave_pub_key);
+
+  LOG (GNUNET_ERROR_TYPE_INFO, "membership_store()\n");
+
+  GNUNET_assert (GNUNET_OK == db->membership_store (db->cls, &channel_pub_key,
+                                                    &slave_pub_key, GNUNET_YES,
+                                                    4, 2, 1));
+
+  LOG (GNUNET_ERROR_TYPE_INFO, "membership_test()\n");
+
+  GNUNET_assert (GNUNET_YES == db->membership_test (db->cls, &channel_pub_key,
+                                                    &slave_pub_key, 4));
+
+  GNUNET_assert (GNUNET_YES == db->membership_test (db->cls, &channel_pub_key,
+                                                    &slave_pub_key, 2));
+
+  GNUNET_assert (GNUNET_NO == db->membership_test (db->cls, &channel_pub_key,
+                                                   &slave_pub_key, 1));
+
+  /* Store & get messages */
+
+  LOG (GNUNET_ERROR_TYPE_INFO, "MESSAGES\n");
+
+  struct GNUNET_MULTICAST_MessageHeader *msg
+    = GNUNET_malloc (sizeof (*msg) + sizeof (channel_pub_key));
+  GNUNET_assert (msg != NULL);
+
+  msg->header.type = htons (GNUNET_MESSAGE_TYPE_MULTICAST_MESSAGE);
+  msg->header.size = htons (sizeof (*msg) + sizeof (channel_pub_key));
+
+  uint64_t fragment_id = INT64_MAX - 1;
+  msg->fragment_id = GNUNET_htonll (fragment_id);
+
+  uint64_t message_id = INT64_MAX - 10;
+  msg->message_id = GNUNET_htonll (message_id);
+
+  uint64_t group_generation = INT64_MAX - 3;
+  msg->group_generation = GNUNET_htonll (group_generation);
+
+  msg->hop_counter = htonl (9);
+  msg->fragment_offset = GNUNET_htonll (0);
+  msg->flags = htonl (GNUNET_MULTICAST_MESSAGE_LAST_FRAGMENT);
+
+  GNUNET_memcpy (&msg[1], &channel_pub_key, sizeof (channel_pub_key));
+
+  msg->purpose.size = htonl (ntohs (msg->header.size)
+                             - sizeof (msg->header)
+                             - sizeof (msg->hop_counter)
+                             - sizeof (msg->signature));
+  msg->purpose.purpose = htonl (234);
+  GNUNET_assert (GNUNET_OK ==
+                 GNUNET_CRYPTO_eddsa_sign (channel_key, &msg->purpose, 
&msg->signature));
+
+  LOG (GNUNET_ERROR_TYPE_INFO, "fragment_store()\n");
+
+  struct FragmentClosure fcls = { 0 };
+  fcls.n = 0;
+  fcls.msg[0] = msg;
+  fcls.flags[0] = GNUNET_PSYCSTORE_MESSAGE_STATE;
+
+  GNUNET_assert (
+    GNUNET_OK == db->fragment_store (db->cls, &channel_pub_key, msg,
+                                     fcls.flags[0]));
+
+  LOG (GNUNET_ERROR_TYPE_INFO, "fragment_get(%" PRIu64 ")\n", fragment_id);
+
+  uint64_t ret_frags = 0;
+  GNUNET_assert (
+    GNUNET_OK == db->fragment_get (db->cls, &channel_pub_key,
+                                   fragment_id, fragment_id,
+                                   &ret_frags, fragment_cb, &fcls));
+  GNUNET_assert (fcls.n == 1);
+
+  LOG (GNUNET_ERROR_TYPE_INFO, "message_get_fragment()\n");
+
+  fcls.n = 0;
+  GNUNET_assert (
+    GNUNET_OK == db->message_get_fragment (db->cls, &channel_pub_key,
+                                           GNUNET_ntohll (msg->message_id),
+                                           GNUNET_ntohll 
(msg->fragment_offset),
+                                           fragment_cb, &fcls));
+  GNUNET_assert (fcls.n == 1);
+
+  LOG (GNUNET_ERROR_TYPE_INFO, "message_add_flags()\n");
+  GNUNET_assert (
+    GNUNET_OK == db->message_add_flags (db->cls, &channel_pub_key,
+                                        GNUNET_ntohll (msg->message_id),
+                                        
GNUNET_PSYCSTORE_MESSAGE_STATE_APPLIED));
+  LOG (GNUNET_ERROR_TYPE_INFO, "fragment_get(%" PRIu64 ")\n", fragment_id);
+
+  fcls.n = 0;
+  fcls.flags[0] |= GNUNET_PSYCSTORE_MESSAGE_STATE_APPLIED;
+
+  GNUNET_assert (
+    GNUNET_OK == db->fragment_get (db->cls, &channel_pub_key,
+                                   fragment_id, fragment_id,
+                                   &ret_frags, fragment_cb, &fcls));
+
+  GNUNET_assert (fcls.n == 1);
+
+  LOG (GNUNET_ERROR_TYPE_INFO, "fragment_store()\n");
+
+  struct GNUNET_MULTICAST_MessageHeader *msg1
+    = GNUNET_malloc (sizeof (*msg1) + sizeof (channel_pub_key));
+
+  GNUNET_memcpy (msg1, msg, sizeof (*msg1) + sizeof (channel_pub_key));
+
+  msg1->fragment_id = GNUNET_htonll (INT64_MAX);
+  msg1->fragment_offset = GNUNET_htonll (32768);
+
+  fcls.n = 0;
+  fcls.msg[1] = msg1;
+  fcls.flags[1] = GNUNET_PSYCSTORE_MESSAGE_STATE_HASH;
+
+  GNUNET_assert (GNUNET_OK == db->fragment_store (db->cls, &channel_pub_key, 
msg1,
+                                                  fcls.flags[1]));
+
+  LOG (GNUNET_ERROR_TYPE_INFO, "message_get()\n");
+
+  GNUNET_assert (
+    GNUNET_OK == db->message_get (db->cls, &channel_pub_key,
+                                  message_id, message_id, 0,
+                                  &ret_frags, fragment_cb, &fcls));
+  GNUNET_assert (fcls.n == 2 && ret_frags == 2);
+
+  /* Message counters */
+
+  LOG (GNUNET_ERROR_TYPE_INFO, "counters_message_get()\n");
+
+  fragment_id = 0;
+  message_id = 0;
+  group_generation = 0;
+  GNUNET_assert (
+    GNUNET_OK == db->counters_message_get (db->cls, &channel_pub_key,
+                                           &fragment_id, &message_id,
+                                           &group_generation)
+    && fragment_id == GNUNET_ntohll (msg1->fragment_id)
+    && message_id == GNUNET_ntohll (msg1->message_id)
+    && group_generation == GNUNET_ntohll (msg1->group_generation));
+
+  /* Modify state */
+
+  LOG (GNUNET_ERROR_TYPE_INFO, "STATE\n");
+
+  LOG (GNUNET_ERROR_TYPE_INFO, "state_modify_*()\n");
+
+  message_id = GNUNET_ntohll (fcls.msg[0]->message_id) + 1;
+  GNUNET_assert (GNUNET_OK == db->state_modify_begin (db->cls, 
&channel_pub_key,
+                                                      message_id, 0));
+
+  GNUNET_assert (GNUNET_OK == db->state_modify_op (db->cls, &channel_pub_key,
+                                                   GNUNET_PSYC_OP_ASSIGN,
+                                                   "_foo",
+                                                   C2ARG("one two three")));
+
+  GNUNET_assert (GNUNET_OK == db->state_modify_op (db->cls, &channel_pub_key,
+                                                   GNUNET_PSYC_OP_ASSIGN,
+                                                   "_foo_bar", slave_key,
+                                                   sizeof (*slave_key)));
+
+  GNUNET_assert (GNUNET_OK == db->state_modify_end (db->cls, &channel_pub_key,
+                                                    message_id));
+
+  LOG (GNUNET_ERROR_TYPE_INFO, "state_get()\n");
+
+  struct StateClosure scls = { 0 };
+  scls.n = 0;
+  scls.value[0] = "one two three";
+  scls.value_size[0] = strlen ("one two three");
+
+  GNUNET_assert (GNUNET_OK == db->state_get (db->cls, &channel_pub_key, "_foo",
+                                             state_cb, &scls));
+  GNUNET_assert (scls.n == 1);
+
+  LOG (GNUNET_ERROR_TYPE_INFO, "state_get_prefix()\n");
+
+  scls.n = 0;
+  scls.value[1] = slave_key;
+  scls.value_size[1] = sizeof (*slave_key);
+
+  GNUNET_assert (GNUNET_OK == db->state_get_prefix (db->cls, &channel_pub_key,
+                                                    "_foo", state_cb, &scls));
+  GNUNET_assert (scls.n == 2);
+
+  LOG (GNUNET_ERROR_TYPE_INFO, "state_get_signed()\n");
+
+  scls.n = 0;
+  GNUNET_assert (GNUNET_NO == db->state_get_signed (db->cls, &channel_pub_key,
+                                                    state_cb, &scls));
+  GNUNET_assert (scls.n == 0);
+
+  LOG (GNUNET_ERROR_TYPE_INFO, "state_update_signed()\n");
+
+  GNUNET_assert (GNUNET_OK == db->state_update_signed (db->cls,
+                                                       &channel_pub_key));
+
+  LOG (GNUNET_ERROR_TYPE_INFO, "state_get_signed()\n");
+
+  scls.n = 0;
+  GNUNET_assert (GNUNET_YES == db->state_get_signed (db->cls, &channel_pub_key,
+                                                     state_cb, &scls));
+  GNUNET_assert (scls.n == 2);
+
+  /* State counters */
+
+  LOG (GNUNET_ERROR_TYPE_INFO, "counters_state_get()\n");
+
+  uint64_t max_state_msg_id = 0;
+  GNUNET_assert (GNUNET_OK == db->counters_state_get (db->cls, 
&channel_pub_key,
+                                                      &max_state_msg_id)
+                 && max_state_msg_id == message_id);
+
+  /* State sync */
+
+  LOG (GNUNET_ERROR_TYPE_INFO, "state_sync_*()\n");
+
+  scls.n = 0;
+  scls.value[0] = channel_key;
+  scls.value_size[0] = sizeof (*channel_key);
+  scls.value[1] = "three two one";
+  scls.value_size[1] = strlen ("three two one");
+
+  GNUNET_assert (GNUNET_OK == db->state_sync_begin (db->cls, 
&channel_pub_key));
+
+  GNUNET_assert (GNUNET_OK == db->state_sync_assign (db->cls, &channel_pub_key,
+                                                     "_sync_bar", 
scls.value[0],
+                                                     scls.value_size[0]));
+
+  GNUNET_assert (GNUNET_OK == db->state_sync_assign (db->cls, &channel_pub_key,
+                                                     "_sync_foo", 
scls.value[1],
+                                                     scls.value_size[1]));
+
+  GNUNET_assert (GNUNET_OK == db->state_sync_end (db->cls, &channel_pub_key,
+                                                  max_state_msg_id,
+                                                  INT64_MAX - 5));
+
+  GNUNET_assert (GNUNET_NO == db->state_get_prefix (db->cls, &channel_pub_key,
+                                                    "_foo", state_cb, &scls));
+  GNUNET_assert (scls.n == 0);
+
+  GNUNET_assert (GNUNET_OK == db->state_get_prefix (db->cls, &channel_pub_key,
+                                                    "_sync", state_cb, &scls));
+  GNUNET_assert (scls.n == 2);
+
+  scls.n = 0;
+  GNUNET_assert (GNUNET_OK == db->state_get_signed (db->cls, &channel_pub_key,
+                                                    state_cb, &scls));
+  GNUNET_assert (scls.n == 2);
+
+  /* Modify state after sync */
+
+  LOG (GNUNET_ERROR_TYPE_INFO, "state_modify_*()\n");
+
+  message_id = GNUNET_ntohll (fcls.msg[0]->message_id) + 6;
+  GNUNET_assert (GNUNET_OK == db->state_modify_begin (db->cls, 
&channel_pub_key,
+                                                      message_id,
+                                                      message_id - 
max_state_msg_id));
+
+  GNUNET_assert (GNUNET_OK == db->state_modify_op (db->cls, &channel_pub_key,
+                                                   GNUNET_PSYC_OP_ASSIGN,
+                                                   "_sync_foo",
+                                                   C2ARG("five six seven")));
+
+  GNUNET_assert (GNUNET_OK == db->state_modify_end (db->cls, &channel_pub_key,
+                                                    message_id));
+
+  /* Reset state */
+
+  LOG (GNUNET_ERROR_TYPE_INFO, "state_reset()\n");
+
+  scls.n = 0;
+  GNUNET_assert (GNUNET_OK == db->state_reset (db->cls, &channel_pub_key));
+  GNUNET_assert (scls.n == 0);
+
+  ok = 0;
+
+  if (NULL != channel_key)
+  {
+    GNUNET_free (channel_key);
+    channel_key = NULL;
+  }
+  if (NULL != slave_key)
+  {
+    GNUNET_free (slave_key);
+    slave_key = NULL;
+  }
+
+  unload_plugin (db);
+}
+
+
+int
+main (int argc, char *argv[])
+{
+  char cfg_name[128];
+  char *const xargv[] = {
+    "test-plugin-psycstore",
+    "-c", cfg_name,
+    "-L", LOG_LEVEL,
+    NULL
+  };
+  struct GNUNET_GETOPT_CommandLineOption options[] = {
+    GNUNET_GETOPT_OPTION_END
+  };
+  GNUNET_DISK_directory_remove ("/tmp/gnunet-test-plugin-psycstore-sqlite");
+  GNUNET_log_setup ("test-plugin-psycstore", LOG_LEVEL, NULL);
+  plugin_name = GNUNET_TESTING_get_testname_from_underscore (argv[0]);
+  GNUNET_snprintf (cfg_name, sizeof (cfg_name), 
"test_plugin_psycstore_%s.conf",
+                   plugin_name);
+  GNUNET_PROGRAM_run ((sizeof (xargv) / sizeof (char *)) - 1, xargv,
+                      "test-plugin-psycstore", "nohelp", options, &run, NULL);
+
+  if ( (0 != ok) &&
+       (77 != ok) )
+    FPRINTF (stderr, "Missed some testcases: %d\n", ok);
+
+#if ! DEBUG_PSYCSTORE
+  GNUNET_DISK_directory_remove ("/tmp/gnunet-test-plugin-psycstore-sqlite");
+#endif
+
+  return ok;
+}
+
+/* end of test_plugin_psycstore.c */
diff --git a/src/psycstore/test_plugin_psycstore_mysql.conf 
b/src/psycstore/test_plugin_psycstore_mysql.conf
new file mode 100644
index 0000000..e15b3fd
--- /dev/null
+++ b/src/psycstore/test_plugin_psycstore_mysql.conf
@@ -0,0 +1,7 @@
+[psycstore-mysql]
+DATABASE = test
+# CONFIG = ~/.my.cnf
+# USER = gnunet
+# PASSWORD =
+# HOST = localhost
+# PORT = 3306
diff --git a/src/psycstore/test_plugin_psycstore_postgres.conf 
b/src/psycstore/test_plugin_psycstore_postgres.conf
new file mode 100644
index 0000000..4b870dd
--- /dev/null
+++ b/src/psycstore/test_plugin_psycstore_postgres.conf
@@ -0,0 +1,2 @@
+[psycstore-postgres]
+CONFIG = connect_timeout=10; dbname=template1
diff --git a/src/psycstore/test_plugin_psycstore_sqlite.conf 
b/src/psycstore/test_plugin_psycstore_sqlite.conf
new file mode 100644
index 0000000..498b1d0
--- /dev/null
+++ b/src/psycstore/test_plugin_psycstore_sqlite.conf
@@ -0,0 +1,2 @@
+[psycstore-sqlite]
+FILENAME = $GNUNET_TMP/gnunet-test-plugin-psycstore-sqlite/sqlite.db
diff --git a/src/psycstore/test_psycstore.c b/src/psycstore/test_psycstore.c
new file mode 100644
index 0000000..ca50904
--- /dev/null
+++ b/src/psycstore/test_psycstore.c
@@ -0,0 +1,586 @@
+/*
+ * This file is part of GNUnet
+ * Copyright (C) 2013 GNUnet e.V.
+ *
+ * GNUnet is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * GNUnet 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
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+     SPDX-License-Identifier: AGPL3.0-or-later
+ */
+
+/**
+ * @file psycstore/test_psycstore.c
+ * @brief Test for the PSYCstore service.
+ * @author Gabor X Toth
+ * @author Christian Grothoff
+ */
+
+#include <inttypes.h>
+
+#include "platform.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_common.h"
+#include "gnunet_testing_lib.h"
+#include "gnunet_psycstore_service.h"
+
+#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
+
+
+/**
+ * Return value from 'main'.
+ */
+static int res;
+
+/**
+ * Handle to PSYCstore service.
+ */
+static struct GNUNET_PSYCSTORE_Handle *h;
+
+/**
+ * Handle to PSYCstore operation.
+ */
+static struct GNUNET_PSYCSTORE_OperationHandle *op;
+
+/**
+ * Handle for task for timeout termination.
+ */
+static struct GNUNET_SCHEDULER_Task *end_badly_task;
+
+static struct GNUNET_CRYPTO_EddsaPrivateKey *channel_key;
+static struct GNUNET_CRYPTO_EcdsaPrivateKey *slave_key;
+
+static struct GNUNET_CRYPTO_EddsaPublicKey channel_pub_key;
+static struct GNUNET_CRYPTO_EcdsaPublicKey slave_pub_key;
+
+static struct FragmentClosure
+{
+  uint8_t n;
+  uint8_t n_expected;
+  uint64_t flags[16];
+  struct GNUNET_MULTICAST_MessageHeader *msg[16];
+} fcls;
+
+struct StateClosure {
+  size_t n;
+  char *name[16];
+  void *value[16];
+  size_t value_size[16];
+} scls;
+
+static struct GNUNET_PSYC_Modifier modifiers[16];
+
+/**
+ * Clean up all resources used.
+ */
+static void
+cleanup ()
+{
+  if (NULL != op)
+  {
+    GNUNET_PSYCSTORE_operation_cancel (op);
+    op = NULL;
+  }
+  if (NULL != h)
+  {
+    GNUNET_PSYCSTORE_disconnect (h);
+    h = NULL;
+  }
+  if (NULL != channel_key)
+  {
+    GNUNET_free (channel_key);
+    channel_key = NULL;
+  }
+  if (NULL != slave_key)
+  {
+    GNUNET_free (slave_key);
+    slave_key = NULL;
+  }
+  GNUNET_SCHEDULER_shutdown ();
+}
+
+
+/**
+ * Terminate the testcase (failure).
+ *
+ * @param cls NULL
+ */
+static void
+end_badly (void *cls)
+{
+  res = 1;
+  cleanup ();
+}
+
+
+/**
+ * Terminate the testcase (success).
+ *
+ * @param cls NULL
+ */
+static void
+end_normally (void *cls)
+{
+  res = 0;
+  cleanup ();
+}
+
+
+/**
+ * Finish the testcase (successfully).
+ */
+static void
+end ()
+{
+  if (NULL != end_badly_task)
+  {
+    GNUNET_SCHEDULER_cancel (end_badly_task);
+    end_badly_task = NULL;
+  }
+  GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS,
+                               &end_normally, NULL);
+}
+
+
+static void
+state_reset_result (void *cls,
+                    int64_t result,
+                    const char *err_msg,
+                    uint16_t err_msg_size)
+{
+  op = NULL;
+  GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+              "state_reset_result:\t%d\n",
+              (int) result);
+  GNUNET_assert (GNUNET_OK == result);
+
+  op = GNUNET_PSYCSTORE_state_reset (h, &channel_pub_key,
+                                     &state_reset_result, cls);
+  GNUNET_PSYCSTORE_operation_cancel (op);
+  op = NULL;
+  end ();
+}
+
+
+static int
+state_result (void *cls,
+              const char *name,
+              const void *value,
+              uint32_t value_size)
+{
+  struct StateClosure *scls = cls;
+  const char *nam = scls->name[scls->n];
+  const void *val = scls->value[scls->n];
+  size_t val_size = scls->value_size[scls->n++];
+
+  if (value_size == val_size
+      && 0 == memcmp (value, val, val_size)
+      && 0 == strcmp (name, nam))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "  variable %s matches\n",
+                name);
+    return GNUNET_YES;
+  }
+  else
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "  variable %s differs\nReceived: %.*s\nExpected: %.*s\n",
+                name, (int) value_size, (char*) value, (int) val_size, (char*) 
val);
+    GNUNET_assert (0);
+    return GNUNET_SYSERR;
+  }
+}
+
+
+static void
+state_get_prefix_result (void *cls, int64_t result,
+                         const char *err_msg, uint16_t err_msg_size)
+{
+  struct StateClosure *scls = cls;
+  op = NULL;
+  GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "state_get_prefix_result:\t%ld\n", 
(long int) result);
+  GNUNET_assert (GNUNET_OK == result && 2 == scls->n);
+
+  op = GNUNET_PSYCSTORE_state_reset (h, &channel_pub_key,
+                                     &state_reset_result, cls);
+}
+
+
+static void
+state_get_result (void *cls, int64_t result,
+                  const char *err_msg, uint16_t err_msg_size)
+{
+  op = NULL;
+  GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "state_get_result:\t%ld\n", (long 
int) result);
+  GNUNET_assert (GNUNET_OK == result);
+
+  scls.n = 0;
+
+  scls.name[0] = "_sync_bar";
+  scls.value[0] = "ten eleven twelve";
+  scls.value_size[0] = sizeof ("ten eleven twelve") - 1;
+
+  scls.name[1] = "_sync_foo";
+  scls.value[1] = "three two one";
+  scls.value_size[1] = sizeof ("three two one") - 1;
+
+  op = GNUNET_PSYCSTORE_state_get_prefix (h, &channel_pub_key, "_sync",
+                                          &state_result,
+                                          &state_get_prefix_result, &scls);
+}
+
+
+static void
+counters_result (void *cls, int status, uint64_t max_fragment_id,
+                 uint64_t max_message_id, uint64_t max_group_generation,
+                 uint64_t max_state_message_id)
+{
+  struct FragmentClosure *fcls = cls;
+  int result = 0;
+  op = NULL;
+
+  if (GNUNET_OK == status
+      && max_fragment_id == GNUNET_ntohll (fcls->msg[2]->fragment_id)
+      && max_message_id == GNUNET_ntohll (fcls->msg[2]->message_id)
+      && max_group_generation == GNUNET_ntohll (fcls->msg[2]->group_generation)
+      && max_state_message_id == GNUNET_ntohll (fcls->msg[0]->message_id))
+    result = 1;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "counters_get:\t%d\n", result);
+  GNUNET_assert (result == 1);
+
+  scls.n = 0;
+  scls.name[0] = "_sync_bar";
+  scls.value[0] = "ten eleven twelve";
+  scls.value_size[0] = sizeof ("ten eleven twelve") - 1;
+
+  op = GNUNET_PSYCSTORE_state_get (h, &channel_pub_key, "_sync_bar_x_yy_zzz",
+                                   &state_result, &state_get_result, &scls);
+}
+
+
+static void
+state_modify_result (void *cls, int64_t result,
+                     const char *err_msg, uint16_t err_msg_size)
+{
+  op = NULL;
+  GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "state_modify_result:\t%ld\n", (long 
int) result);
+  GNUNET_assert (GNUNET_OK == result);
+
+  op = GNUNET_PSYCSTORE_counters_get (h, &channel_pub_key,
+                                      &counters_result, cls);
+}
+
+
+static void
+state_sync_result (void *cls, int64_t result,
+                   const char *err_msg, uint16_t err_msg_size)
+{
+  struct FragmentClosure *fcls = cls;
+  op = NULL;
+  GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "state_sync_result:\t%ld\n", (long 
int) result);
+  GNUNET_assert (GNUNET_OK == result);
+
+  op = GNUNET_PSYCSTORE_state_modify (h, &channel_pub_key,
+                                      GNUNET_ntohll (fcls->msg[0]->message_id),
+                                      0, state_modify_result, fcls);
+}
+
+
+static int
+fragment_result (void *cls,
+                 struct GNUNET_MULTICAST_MessageHeader *msg,
+                 enum GNUNET_PSYCSTORE_MessageFlags flags)
+{
+  struct FragmentClosure *fcls = cls;
+  GNUNET_assert (fcls->n < fcls->n_expected);
+  struct GNUNET_MULTICAST_MessageHeader *msg0 = fcls->msg[fcls->n];
+  uint64_t flags0 = fcls->flags[fcls->n++];
+
+  if (flags == flags0 && msg->header.size == msg0->header.size
+      && 0 == memcmp (msg, msg0, ntohs (msg->header.size)))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "  fragment %" PRIu64 " matches\n",
+                GNUNET_ntohll (msg->fragment_id));
+    return GNUNET_YES;
+  }
+  else
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "  fragment differs: expected %" PRIu64 ", got %" PRIu64 "\n",
+                GNUNET_ntohll (msg0->fragment_id),
+                GNUNET_ntohll (msg->fragment_id));
+    GNUNET_assert (0);
+    return GNUNET_SYSERR;
+  }
+}
+
+
+static void
+message_get_latest_result (void *cls, int64_t result,
+                           const char *err_msg, uint16_t err_msg_size)
+{
+  struct FragmentClosure *fcls = cls;
+  op = NULL;
+  GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "message_get_latest:\t%ld\n", (long 
int) result);
+  GNUNET_assert (0 < result && fcls->n == fcls->n_expected);
+
+  modifiers[0] = (struct GNUNET_PSYC_Modifier) {
+    .oper = '=',
+    .name = "_sync_foo",
+    .value = "three two one",
+    .value_size = sizeof ("three two one") - 1
+  };
+  modifiers[1] = (struct GNUNET_PSYC_Modifier) {
+    .oper = '=',
+    .name = "_sync_bar",
+    .value = "ten eleven twelve",
+    .value_size = sizeof ("ten eleven twelve") - 1
+  };
+
+  op = GNUNET_PSYCSTORE_state_sync (h, &channel_pub_key,
+                                    GNUNET_ntohll (fcls->msg[0]->message_id) + 
1,
+                                    GNUNET_ntohll (fcls->msg[0]->message_id) + 
2,
+                                    2, modifiers, state_sync_result, fcls);
+}
+
+
+static void
+message_get_result (void *cls, int64_t result,
+                    const char *err_msg, uint16_t err_msg_size)
+{
+  struct FragmentClosure *fcls = cls;
+  op = NULL;
+  GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "message_get:\t%ld\n", (long int) 
result);
+  GNUNET_assert (0 < result && fcls->n == fcls->n_expected);
+
+  fcls->n = 0;
+  fcls->n_expected = 3;
+  op = GNUNET_PSYCSTORE_message_get_latest (h, &channel_pub_key, 
&slave_pub_key,
+                                            1, "", &fragment_result,
+                                            &message_get_latest_result, fcls);
+}
+
+
+static void
+message_get_fragment_result (void *cls, int64_t result,
+                             const char *err_msg, uint16_t err_msg_size)
+{
+  struct FragmentClosure *fcls = cls;
+  op = NULL;
+  GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "message_get_fragment:\t%ld\n", (long 
int) result);
+  GNUNET_assert (0 < result && fcls->n == fcls->n_expected);
+
+  fcls->n = 0;
+  fcls->n_expected = 3;
+  uint64_t message_id = GNUNET_ntohll (fcls->msg[0]->message_id);
+  op = GNUNET_PSYCSTORE_message_get (h, &channel_pub_key, &slave_pub_key,
+                                     message_id, message_id, 0, "",
+                                     &fragment_result,
+                                     &message_get_result, fcls);
+}
+
+
+static void
+fragment_get_latest_result (void *cls, int64_t result,
+                            const char *err_msg, uint16_t err_msg_size)
+{
+  struct FragmentClosure *fcls = cls;
+  op = NULL;
+  GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "fragment_get_latest:\t%ld\n", (long 
int) result);
+  GNUNET_assert (0 < result && fcls->n == fcls->n_expected);
+
+  fcls->n = 1;
+  fcls->n_expected = 2;
+  op = GNUNET_PSYCSTORE_message_get_fragment (h, &channel_pub_key, 
&slave_pub_key,
+                                              GNUNET_ntohll 
(fcls->msg[1]->message_id),
+                                              GNUNET_ntohll 
(fcls->msg[1]->fragment_offset),
+                                              &fragment_result,
+                                              &message_get_fragment_result, 
fcls);
+}
+
+
+static void
+fragment_get_result (void *cls, int64_t result,
+                     const char *err_msg, uint16_t err_msg_size)
+{
+  struct FragmentClosure *fcls = cls;
+  op = NULL;
+  GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+              "fragment_get:\t%d\n",
+              (int) result);
+  GNUNET_assert (0 < result && fcls->n == fcls->n_expected);
+
+  fcls->n = 0;
+  fcls->n_expected = 3;
+  op = GNUNET_PSYCSTORE_fragment_get_latest (h, &channel_pub_key,
+                                             &slave_pub_key, fcls->n_expected,
+                                             &fragment_result,
+                                             &fragment_get_latest_result, 
fcls);
+}
+
+
+static void
+fragment_store_result (void *cls, int64_t result,
+                       const char *err_msg, uint16_t err_msg_size)
+{
+  op = NULL;
+  GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "fragment_store:\t%ld\n", (long int) 
result);
+  GNUNET_assert (GNUNET_OK == result);
+
+  if ((intptr_t) cls == GNUNET_YES)
+  { /* last fragment */
+    fcls.n = 0;
+    fcls.n_expected = 1;
+    uint64_t fragment_id = GNUNET_ntohll (fcls.msg[0]->fragment_id);
+    op = GNUNET_PSYCSTORE_fragment_get (h, &channel_pub_key, &slave_pub_key,
+                                        fragment_id, fragment_id,
+                                        &fragment_result,
+                                        &fragment_get_result, &fcls);
+  }
+}
+
+
+static void
+fragment_store ()
+{
+  struct GNUNET_MULTICAST_MessageHeader *msg;
+  fcls.flags[0] = GNUNET_PSYCSTORE_MESSAGE_STATE;
+  fcls.msg[0] = msg = GNUNET_malloc (sizeof (*msg) + sizeof (channel_pub_key));
+  GNUNET_assert (msg != NULL);
+
+  msg->header.type = htons (GNUNET_MESSAGE_TYPE_MULTICAST_MESSAGE);
+  msg->header.size = htons (sizeof (*msg) + sizeof (channel_pub_key));
+
+  msg->hop_counter = htonl (9);
+  msg->fragment_id = GNUNET_htonll (INT64_MAX - 8);
+  msg->fragment_offset = GNUNET_htonll (0);
+  msg->message_id = GNUNET_htonll (INT64_MAX - 10);
+  msg->group_generation = GNUNET_htonll (INT64_MAX - 3);
+  msg->flags = htonl (GNUNET_MULTICAST_MESSAGE_LAST_FRAGMENT);
+
+  GNUNET_memcpy (&msg[1], &channel_pub_key, sizeof (channel_pub_key));
+
+  msg->purpose.size = htonl (ntohs (msg->header.size)
+                             - sizeof (msg->header)
+                             - sizeof (msg->hop_counter)
+                             - sizeof (msg->signature));
+  msg->purpose.purpose = htonl (234);
+  GNUNET_assert (GNUNET_OK == GNUNET_CRYPTO_eddsa_sign (channel_key, 
&msg->purpose,
+                                                        &msg->signature));
+
+  op = GNUNET_PSYCSTORE_fragment_store (h, &channel_pub_key, msg, 
fcls.flags[0],
+                                        &fragment_store_result, GNUNET_NO);
+
+  fcls.flags[1] = GNUNET_PSYCSTORE_MESSAGE_STATE_APPLIED;
+  fcls.msg[1] = msg = GNUNET_malloc (sizeof (*msg) + sizeof (channel_pub_key));
+  GNUNET_memcpy (msg, fcls.msg[0], sizeof (*msg) + sizeof (channel_pub_key));
+  msg->fragment_id = GNUNET_htonll (INT64_MAX - 4);
+  msg->fragment_offset = GNUNET_htonll (1024);
+
+  op = GNUNET_PSYCSTORE_fragment_store (h, &channel_pub_key, msg, 
fcls.flags[1],
+                                        &fragment_store_result, GNUNET_NO);
+
+  fcls.flags[2] = GNUNET_PSYCSTORE_MESSAGE_STATE_HASH;
+  fcls.msg[2] = msg = GNUNET_malloc (sizeof (*msg) + sizeof (channel_pub_key));
+  GNUNET_memcpy (msg, fcls.msg[1], sizeof (*msg) + sizeof (channel_pub_key));
+  msg->fragment_id = GNUNET_htonll (INT64_MAX);
+  msg->fragment_offset = GNUNET_htonll (16384);
+
+  op = GNUNET_PSYCSTORE_fragment_store (h, &channel_pub_key, msg, 
fcls.flags[2],
+                                        &fragment_store_result, (void *) 
GNUNET_YES);
+}
+
+
+static void
+membership_test_result (void *cls, int64_t result,
+                        const char *err_msg, uint16_t err_msg_size)
+{
+  op = NULL;
+  GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "membership_test:\t%ld\n", (long int) 
result);
+  GNUNET_assert (GNUNET_OK == result);
+
+  fragment_store ();
+}
+
+
+static void
+membership_store_result (void *cls, int64_t result,
+                         const char *err_msg, uint16_t err_msg_size)
+{
+  op = NULL;
+  GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "membership_store:\t%ld\n", (long 
int) result);
+  GNUNET_assert (GNUNET_OK == result);
+
+  op = GNUNET_PSYCSTORE_membership_test (h, &channel_pub_key, &slave_pub_key,
+                                         INT64_MAX - 10, 2,
+                                         &membership_test_result, NULL);
+}
+
+
+/**
+ * Main function of the test, run from scheduler.
+ *
+ * @param cls NULL
+ * @param cfg configuration we use (also to connect to PSYCstore service)
+ * @param peer handle to access more of the peer (not used)
+ */
+static void
+#if DEBUG_TEST_PSYCSTORE
+run (void *cls, char *const *args, const char *cfgfile,
+     const struct GNUNET_CONFIGURATION_Handle *cfg)
+#else
+run (void *cls,
+     const struct GNUNET_CONFIGURATION_Handle *cfg,
+     struct GNUNET_TESTING_Peer *peer)
+#endif
+{
+  end_badly_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, &end_badly, NULL);
+
+  h = GNUNET_PSYCSTORE_connect (cfg);
+  GNUNET_assert (NULL != h);
+
+  channel_key = GNUNET_CRYPTO_eddsa_key_create ();
+  slave_key = GNUNET_CRYPTO_ecdsa_key_create ();
+
+  GNUNET_CRYPTO_eddsa_key_get_public (channel_key, &channel_pub_key);
+  GNUNET_CRYPTO_ecdsa_key_get_public (slave_key, &slave_pub_key);
+
+  op = GNUNET_PSYCSTORE_membership_store (h, &channel_pub_key, &slave_pub_key,
+                                          GNUNET_YES, INT64_MAX - 5,
+                                          INT64_MAX - 10, 2,
+                                          &membership_store_result, NULL);
+}
+
+
+int
+main (int argc, char *argv[])
+{
+  res = 1;
+#if DEBUG_TEST_PSYCSTORE
+  const struct GNUNET_GETOPT_CommandLineOption opts[] = {
+    GNUNET_GETOPT_OPTION_END
+  };
+  if (GNUNET_OK != GNUNET_PROGRAM_run (argc, argv, "test-psycstore",
+                                       "test-psycstore [options]",
+                                       opts, &run, NULL))
+    return 1;
+#else
+  if (0 != GNUNET_TESTING_service_run ("test-psycstore", "psycstore",
+                                       "test_psycstore.conf", &run, NULL))
+    return 1;
+#endif
+  return res;
+}
+
+/* end of test_psycstore.c */
diff --git a/src/psycstore/test_psycstore.conf 
b/src/psycstore/test_psycstore.conf
new file mode 100644
index 0000000..fa7c2d0
--- /dev/null
+++ b/src/psycstore/test_psycstore.conf
@@ -0,0 +1,8 @@
+[PATHS]
+GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-psycstore/
+
+[psycstore]
+DATABASE = sqlite
+
+[psycstore-sqlite]
+FILENAME = $GNUNET_TEST_HOME/psycstore/sqlite.db
diff --git a/src/psycutil/.gitignore b/src/psycutil/.gitignore
new file mode 100644
index 0000000..03d8197
--- /dev/null
+++ b/src/psycutil/.gitignore
@@ -0,0 +1 @@
+test_psyc_env
diff --git a/src/psycutil/Makefile.am b/src/psycutil/Makefile.am
new file mode 100644
index 0000000..2732c3a
--- /dev/null
+++ b/src/psycutil/Makefile.am
@@ -0,0 +1,45 @@
+# This Makefile.am is in the public domain
+AM_CPPFLAGS = -I$(top_srcdir)/src/include
+
+pkgcfgdir= $(pkgdatadir)/config.d/
+
+libexecdir= $(pkglibdir)/libexec/
+
+if MINGW
+ WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols
+endif
+
+if USE_COVERAGE
+  AM_CFLAGS = --coverage -O0
+  XLIB = -lgcov
+endif
+
+lib_LTLIBRARIES = libgnunetpsycutil.la
+
+libgnunetpsycutil_la_SOURCES = \
+  psyc_env.c \
+  psyc_message.c \
+  psyc_slicer.c
+libgnunetpsycutil_la_LIBADD = \
+  $(top_builddir)/src/util/libgnunetutil.la \
+  $(GN_LIBINTL) $(XLIB)
+libgnunetpsycutil_la_LDFLAGS = \
+  $(GN_LIB_LDFLAGS)  $(WINFLAGS) \
+  -version-info 0:0:0
+
+if HAVE_TESTING
+check_PROGRAMS = \
+ test_psyc_env
+endif
+
+if ENABLE_TEST_RUN
+AM_TESTS_ENVIRONMENT=export 
GNUNET_PREFIX=$${GNUNET_PREFIX:address@hidden@};export 
PATH=$${GNUNET_PREFIX:address@hidden@}/bin:$$PATH;unset XDG_DATA_HOME;unset 
XDG_CONFIG_HOME;
+TESTS = $(check_PROGRAMS)
+endif
+
+test_psyc_env_SOURCES = \
+ test_psyc_env.c
+test_psyc_env_LDADD = \
+  libgnunetpsycutil.la \
+  $(top_builddir)/src/testing/libgnunettesting.la \
+  $(top_builddir)/src/util/libgnunetutil.la
diff --git a/src/psycutil/psyc_env.c b/src/psycutil/psyc_env.c
new file mode 100644
index 0000000..fc4b8eb
--- /dev/null
+++ b/src/psycutil/psyc_env.c
@@ -0,0 +1,196 @@
+/*
+ * This file is part of GNUnet.
+ * Copyright (C) 2013 GNUnet e.V.
+ *
+ * GNUnet is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * GNUnet 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
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+     SPDX-License-Identifier: AGPL3.0-or-later
+ */
+
+/**
+ * @author Gabor X Toth
+ *
+ * @file
+ * Library providing operations for the @e environment of
+ * PSYC and Social messages.
+ */
+
+#include "platform.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_psyc_env.h"
+
+/**
+ * Environment for a message.
+ *
+ * Contains modifiers.
+ */
+struct GNUNET_PSYC_Environment
+{
+  struct GNUNET_PSYC_Modifier *mod_head;
+  struct GNUNET_PSYC_Modifier *mod_tail;
+  size_t mod_count;
+};
+
+
+/**
+ * Create an environment.
+ *
+ * @return A newly allocated environment.
+ */
+struct GNUNET_PSYC_Environment *
+GNUNET_PSYC_env_create ()
+{
+  return GNUNET_new (struct GNUNET_PSYC_Environment);
+}
+
+
+/**
+ * Add a modifier to the environment.
+ *
+ * @param env The environment.
+ * @param oper Operation to perform.
+ * @param name Name of the variable.
+ * @param value Value of the variable.
+ * @param value_size Size of @a value.
+ */
+void
+GNUNET_PSYC_env_add (struct GNUNET_PSYC_Environment *env,
+                     enum GNUNET_PSYC_Operator oper, const char *name,
+                     const void *value, size_t value_size)
+{
+  struct GNUNET_PSYC_Modifier *mod = GNUNET_new (struct GNUNET_PSYC_Modifier);
+  mod->oper = oper;
+  mod->name = name;
+  mod->value = value;
+  mod->value_size = value_size;
+  GNUNET_CONTAINER_DLL_insert_tail (env->mod_head, env->mod_tail, mod);
+  env->mod_count++;
+}
+
+
+/**
+ * Get the first modifier of the environment.
+ */
+struct GNUNET_PSYC_Modifier *
+GNUNET_PSYC_env_head (const struct GNUNET_PSYC_Environment *env)
+{
+  return env->mod_head;
+}
+
+
+/**
+ * Get the last modifier of the environment.
+ */
+struct GNUNET_PSYC_Modifier *
+GNUNET_PSYC_env_tail (const struct GNUNET_PSYC_Environment *env)
+{
+  return env->mod_tail;
+}
+
+
+/**
+ * Remove a modifier from the environment.
+ */
+void
+GNUNET_PSYC_env_remove (struct GNUNET_PSYC_Environment *env,
+                        struct GNUNET_PSYC_Modifier *mod)
+{
+  GNUNET_CONTAINER_DLL_remove (env->mod_head, env->mod_tail, mod);
+}
+
+
+/**
+ * Get the modifier at the beginning of an environment and remove it.
+ *
+ * @param env
+ * @param oper
+ * @param name
+ * @param value
+ * @param value_size
+ *
+ * @return
+ */
+int
+GNUNET_PSYC_env_shift (struct GNUNET_PSYC_Environment *env,
+                       enum GNUNET_PSYC_Operator *oper, const char **name,
+                       const void **value, size_t *value_size)
+{
+  if (NULL == env->mod_head)
+    return GNUNET_NO;
+
+  struct GNUNET_PSYC_Modifier *mod = env->mod_head;
+  *oper = mod->oper;
+  *name = mod->name;
+  *value = mod->value;
+  *value_size = mod->value_size;
+
+  GNUNET_CONTAINER_DLL_remove (env->mod_head, env->mod_tail, mod);
+  GNUNET_free (mod);
+  env->mod_count--;
+
+  return GNUNET_YES;
+}
+
+
+/**
+ * Iterate through all modifiers in the environment.
+ *
+ * @param env The environment.
+ * @param it Iterator.
+ * @param it_cls Closure for iterator.
+ */
+void
+GNUNET_PSYC_env_iterate (const struct GNUNET_PSYC_Environment *env,
+                         GNUNET_PSYC_Iterator it, void *it_cls)
+{
+  struct GNUNET_PSYC_Modifier *mod;
+  for (mod = env->mod_head; NULL != mod; mod = mod->next)
+    it (it_cls, mod->oper, mod->name, mod->value, mod->value_size);
+}
+
+
+/**
+ * Get the number of modifiers in the environment.
+ *
+ * @param env The environment.
+ *
+ * @return Number of modifiers.
+ */
+size_t
+GNUNET_PSYC_env_get_count (const struct GNUNET_PSYC_Environment *env)
+{
+  return env->mod_count;
+}
+
+
+/**
+ * Destroy an environment.
+ *
+ * @param env The environment to destroy.
+ */
+void
+GNUNET_PSYC_env_destroy (struct GNUNET_PSYC_Environment *env)
+{
+  struct GNUNET_PSYC_Modifier *mod, *prev = NULL;
+  for (mod = env->mod_head; NULL != mod; mod = mod->next)
+  {
+    if (NULL != prev)
+      GNUNET_free (prev);
+    prev = mod;
+  }
+  if (NULL != prev)
+    GNUNET_free (prev);
+
+  GNUNET_free (env);
+}
diff --git a/src/psycutil/psyc_message.c b/src/psycutil/psyc_message.c
new file mode 100644
index 0000000..a03eff4
--- /dev/null
+++ b/src/psycutil/psyc_message.c
@@ -0,0 +1,1355 @@
+/*
+ * This file is part of GNUnet
+ * Copyright (C) 2013 GNUnet e.V.
+ *
+ * GNUnet is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * GNUnet 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
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+     SPDX-License-Identifier: AGPL3.0-or-later
+ */
+
+/**
+ * @file psycutil/psyc_message.c
+ * @brief PSYC utilities; receiving/transmitting/logging PSYC messages.
+ * @author Gabor X Toth
+ */
+
+#include <inttypes.h>
+
+#include "platform.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_psyc_util_lib.h"
+#include "gnunet_psyc_service.h"
+
+#define LOG(kind,...) GNUNET_log_from (kind, "psyc-util",__VA_ARGS__)
+
+
+struct GNUNET_PSYC_TransmitHandle
+{
+  /**
+   * Client connection to service.
+   */
+  struct GNUNET_MQ_Handle *mq;
+
+  /**
+   * Message currently being received from the client.
+   */
+  struct GNUNET_MessageHeader *msg;
+
+  /**
+   * Envelope for @a msg
+   */
+  struct GNUNET_MQ_Envelope *env;
+
+  /**
+   * Callback to request next modifier from client.
+   */
+  GNUNET_PSYC_TransmitNotifyModifier notify_mod;
+
+  /**
+   * Closure for the notify callbacks.
+   */
+  void *notify_mod_cls;
+
+  /**
+   * Callback to request next data fragment from client.
+   */
+  GNUNET_PSYC_TransmitNotifyData notify_data;
+
+  /**
+   * Closure for the notify callbacks.
+   */
+  void *notify_data_cls;
+
+  /**
+   * Modifier of the environment that is currently being transmitted.
+   */
+  struct GNUNET_PSYC_Modifier *mod;
+
+  /**
+   *
+   */
+  const char *mod_value;
+
+  /**
+   * Number of bytes remaining to be transmitted from the current modifier 
value.
+   */
+  uint32_t mod_value_remaining;
+
+  /**
+   * State of the current message being received from client.
+   */
+  enum GNUNET_PSYC_MessageState state;
+
+  /**
+   * Number of PSYC_TRANSMIT_ACK messages we are still waiting for.
+   */
+  uint8_t acks_pending;
+
+  /**
+   * Is transmission paused?
+   */
+  uint8_t paused;
+
+  /**
+   * Are we currently transmitting a message?
+   */
+  uint8_t in_transmit;
+
+  /**
+   * Notify callback is currently being called.
+   */
+  uint8_t in_notify;
+
+};
+
+
+
+struct GNUNET_PSYC_ReceiveHandle
+{
+  /**
+   * Message callback.
+   */
+  GNUNET_PSYC_MessageCallback message_cb;
+
+  /**
+   * Message part callback.
+   */
+  GNUNET_PSYC_MessagePartCallback message_part_cb;
+
+  /**
+   * Closure for the callbacks.
+   */
+  void *cb_cls;
+
+  /**
+   * ID of the message being received from the PSYC service.
+   */
+  uint64_t message_id;
+
+  /**
+   * Public key of the slave from which a message is being received.
+   */
+  struct GNUNET_CRYPTO_EcdsaPublicKey slave_pub_key;
+
+  /**
+   * State of the currently being received message from the PSYC service.
+   */
+  enum GNUNET_PSYC_MessageState state;
+
+  /**
+   * Flags for the currently being received message from the PSYC service.
+   */
+  enum GNUNET_PSYC_MessageFlags flags;
+
+  /**
+   * Expected value size for the modifier being received from the PSYC service.
+   */
+  uint32_t mod_value_size_expected;
+
+  /**
+   * Actual value size for the modifier being received from the PSYC service.
+   */
+  uint32_t mod_value_size;
+};
+
+
+/**** Messages ****/
+
+
+/**
+ * Create a PSYC message.
+ *
+ * @param method_name
+ *        PSYC method for the message.
+ * @param env
+ *        Environment for the message.
+ * @param data
+ *        Data payload for the message.
+ * @param data_size
+ *        Size of @a data.
+ *
+ * @return Message header with size information,
+ *         followed by the message parts.
+ */
+struct GNUNET_PSYC_Message *
+GNUNET_PSYC_message_create (const char *method_name,
+                            const struct GNUNET_PSYC_Environment *env,
+                            const void *data,
+                            size_t data_size)
+{
+  struct GNUNET_PSYC_Modifier *mod = NULL;
+  struct GNUNET_PSYC_MessageMethod *pmeth = NULL;
+  struct GNUNET_PSYC_MessageModifier *pmod = NULL;
+  struct GNUNET_MessageHeader *pmsg = NULL;
+  uint16_t env_size = 0;
+  if (NULL != env)
+  {
+    mod = GNUNET_PSYC_env_head (env);
+    while (NULL != mod)
+    {
+      env_size += sizeof (*pmod) + strlen (mod->name) + 1 + mod->value_size;
+      mod = mod->next;
+    }
+  }
+
+  struct GNUNET_PSYC_Message *msg;
+  uint16_t method_name_size = strlen (method_name) + 1;
+  if (method_name_size == 1)
+    return NULL;
+
+  uint16_t msg_size = sizeof (*msg)                      /* header */
+    + sizeof (*pmeth) + method_name_size                 /* method */
+    + env_size                                           /* modifiers */
+    + ((0 < data_size) ? sizeof (*pmsg) + data_size : 0) /* data */
+    + sizeof (*pmsg);                                    /* end of message */
+  msg = GNUNET_malloc (msg_size);
+  msg->header.size = htons (msg_size);
+  msg->header.type = htons (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE); /* FIXME */
+
+  pmeth = (struct GNUNET_PSYC_MessageMethod *) &msg[1];
+  pmeth->header.type = htons (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_METHOD);
+  pmeth->header.size = htons (sizeof (*pmeth) + method_name_size);
+  GNUNET_memcpy (&pmeth[1], method_name, method_name_size);
+
+  uint16_t p = sizeof (*msg) + sizeof (*pmeth) + method_name_size;
+  if (NULL != env)
+  {
+    mod = GNUNET_PSYC_env_head (env);
+    while (NULL != mod)
+    {
+      uint16_t mod_name_size = strlen (mod->name) + 1;
+      pmod = (struct GNUNET_PSYC_MessageModifier *) ((char *) msg + p);
+      pmod->header.type = htons (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MODIFIER);
+      pmod->header.size = sizeof (*pmod) + mod_name_size + mod->value_size;
+      p += pmod->header.size;
+      pmod->header.size = htons (pmod->header.size);
+
+      pmod->oper = mod->oper;
+      pmod->name_size = htons (mod_name_size);
+      pmod->value_size = htonl (mod->value_size);
+
+      GNUNET_memcpy (&pmod[1], mod->name, mod_name_size);
+      if (0 < mod->value_size)
+        GNUNET_memcpy ((char *) &pmod[1] + mod_name_size, mod->value, 
mod->value_size);
+
+      mod = mod->next;
+    }
+  }
+
+  if (0 < data_size)
+  {
+    pmsg = (struct GNUNET_MessageHeader *) ((char *) msg + p);
+    pmsg->size = sizeof (*pmsg) + data_size;
+    p += pmsg->size;
+    pmsg->size = htons (pmsg->size);
+    pmsg->type = htons (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_DATA);
+    GNUNET_memcpy (&pmsg[1], data, data_size);
+  }
+
+  pmsg = (struct GNUNET_MessageHeader *) ((char *) msg + p);
+  pmsg->size = htons (sizeof (*pmsg));
+  pmsg->type = htons (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END);
+
+  GNUNET_assert (p + sizeof (*pmsg) == msg_size);
+  return msg;
+}
+
+
+void
+GNUNET_PSYC_log_message (enum GNUNET_ErrorType kind,
+                         const struct GNUNET_MessageHeader *msg)
+{
+  uint16_t size = ntohs (msg->size);
+  uint16_t type = ntohs (msg->type);
+
+  GNUNET_log (kind,
+              "Message of type %d and size %u:\n",
+              type,
+              size);
+  switch (type)
+  {
+  case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE:
+  {
+    const struct GNUNET_PSYC_MessageHeader *pmsg
+      = (const struct GNUNET_PSYC_MessageHeader *) msg;
+    GNUNET_log (kind,
+                "\tID: %" PRIu64 "\tflags: %x" PRIu32 "\n",
+                GNUNET_ntohll (pmsg->message_id),
+                ntohl (pmsg->flags));
+    break;
+  }
+  case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_METHOD:
+  {
+    const struct GNUNET_PSYC_MessageMethod *meth
+      = (const struct GNUNET_PSYC_MessageMethod *) msg;
+    GNUNET_log (kind,
+                "\t%.*s\n",
+                (int) (size - sizeof (*meth)),
+                (const char *) &meth[1]);
+    break;
+  }
+  case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MODIFIER:
+  {
+    const struct GNUNET_PSYC_MessageModifier *mod
+      = (const struct GNUNET_PSYC_MessageModifier *) msg;
+    uint16_t name_size = ntohs (mod->name_size);
+    char oper = ' ' < mod->oper ? mod->oper : ' ';
+    GNUNET_log (kind,
+                "\t%c%.*s\t%.*s\n",
+                oper,
+                (int) name_size,
+                (const char *) &mod[1],
+                (int) (size - sizeof (*mod) - name_size),
+                ((const char *) &mod[1]) + name_size);
+    break;
+  }
+  case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MOD_CONT:
+  case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_DATA:
+    GNUNET_log (kind,
+                "\t%.*s\n",
+                (int) (size - sizeof (*msg)),
+                (const char *) &msg[1]);
+    break;
+  }
+}
+
+
+/**** Transmitting messages ****/
+
+
+/**
+ * Create a transmission handle.
+ */
+struct GNUNET_PSYC_TransmitHandle *
+GNUNET_PSYC_transmit_create (struct GNUNET_MQ_Handle *mq)
+{
+  struct GNUNET_PSYC_TransmitHandle *tmit = GNUNET_new (struct 
GNUNET_PSYC_TransmitHandle);
+
+  tmit->mq = mq;
+  return tmit;
+}
+
+
+/**
+ * Destroy a transmission handle.
+ */
+void
+GNUNET_PSYC_transmit_destroy (struct GNUNET_PSYC_TransmitHandle *tmit)
+{
+  GNUNET_free (tmit);
+}
+
+
+/**
+ * Queue a message part for transmission.
+ *
+ * The message part is added to the current message buffer.
+ * When this buffer is full, it is added to the transmission queue.
+ *
+ * @param tmit
+ *        Transmission handle.
+ * @param msg
+ *        Message part, or NULL.
+ * @param tmit_now
+ *        Transmit message now, or wait for buffer to fill up?
+ *        #GNUNET_YES or #GNUNET_NO.
+ */
+static void
+transmit_queue_insert (struct GNUNET_PSYC_TransmitHandle *tmit,
+                       const struct GNUNET_MessageHeader *msg,
+                       uint8_t tmit_now)
+{
+  uint16_t size = (NULL != msg) ? ntohs (msg->size) : 0;
+
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Queueing message part of type %u and size %u (tmit_now: %u)).\n",
+       NULL != msg ? ntohs (msg->type) : 0, size, tmit_now);
+
+  if (NULL != tmit->msg)
+  {
+    if (NULL == msg
+        || GNUNET_MULTICAST_FRAGMENT_MAX_PAYLOAD < tmit->msg->size + size)
+    {
+      /* End of message or buffer is full, add it to transmission queue
+       * and start with empty buffer */
+      tmit->msg->size = htons (tmit->msg->size);
+      GNUNET_MQ_send (tmit->mq, tmit->env);
+      tmit->env = NULL;
+      tmit->msg = NULL;
+      tmit->acks_pending++;
+    }
+    else
+    {
+      /* Message fits in current buffer, append */
+      GNUNET_memcpy ((char *) tmit->msg + tmit->msg->size, msg, size);
+      tmit->msg->size += size;
+    }
+  }
+
+  if (NULL == tmit->msg && NULL != msg)
+  {
+    /* Empty buffer, copy over message. */
+    tmit->env = GNUNET_MQ_msg_extra (tmit->msg,
+                                     GNUNET_MULTICAST_FRAGMENT_MAX_PAYLOAD,
+                                     GNUNET_MESSAGE_TYPE_PSYC_MESSAGE);
+    /* store current message size in host byte order
+     * then later switch it to network byte order before sending */
+    tmit->msg->size = sizeof (*tmit->msg) + size;
+
+    GNUNET_memcpy (&tmit->msg[1], msg, size);
+  }
+
+  if (NULL != tmit->msg
+      && (GNUNET_YES == tmit_now
+          || (GNUNET_MULTICAST_FRAGMENT_MAX_PAYLOAD
+              < tmit->msg->size + sizeof (struct GNUNET_MessageHeader))))
+  {
+    /* End of message or buffer is full, add it to transmission queue. */
+    tmit->msg->size = htons (tmit->msg->size);
+    GNUNET_MQ_send (tmit->mq, tmit->env);
+    tmit->env = NULL;
+    tmit->msg = NULL;
+    tmit->acks_pending++;
+  }
+}
+
+
+/**
+ * Request data from client to transmit.
+ *
+ * @param tmit  Transmission handle.
+ */
+static void
+transmit_data (struct GNUNET_PSYC_TransmitHandle *tmit)
+{
+  int notify_ret = GNUNET_YES;
+  uint16_t data_size = 0;
+  char data[GNUNET_MULTICAST_FRAGMENT_MAX_PAYLOAD] = "";
+  struct GNUNET_MessageHeader *msg = (struct GNUNET_MessageHeader *) data;
+  msg->type = htons (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_DATA);
+
+  if (NULL != tmit->notify_data)
+  {
+    data_size = GNUNET_PSYC_DATA_MAX_PAYLOAD;
+    tmit->in_notify = GNUNET_YES;
+    notify_ret = tmit->notify_data (tmit->notify_data_cls, &data_size, 
&msg[1]);
+    tmit->in_notify = GNUNET_NO;
+  }
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "transmit_data (ret: %d, size: %u): %.*s\n",
+       notify_ret, data_size, data_size, &msg[1]);
+  switch (notify_ret)
+  {
+  case GNUNET_NO:
+    if (0 == data_size)
+    {
+      /* Transmission paused, nothing to send. */
+      tmit->paused = GNUNET_YES;
+      return;
+    }
+    break;
+
+  case GNUNET_YES:
+    tmit->state = GNUNET_PSYC_MESSAGE_STATE_END;
+    break;
+
+  default:
+    LOG (GNUNET_ERROR_TYPE_ERROR,
+         "TransmitNotifyData callback returned error when requesting data.\n");
+
+    tmit->state = GNUNET_PSYC_MESSAGE_STATE_CANCEL;
+    msg->type = htons (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL);
+    msg->size = htons (sizeof (*msg));
+    transmit_queue_insert (tmit, msg, GNUNET_YES);
+    tmit->in_transmit = GNUNET_NO;
+    return;
+  }
+
+  if (0 < data_size)
+  {
+    GNUNET_assert (data_size <= GNUNET_PSYC_DATA_MAX_PAYLOAD);
+    msg->size = htons (sizeof (*msg) + data_size);
+    transmit_queue_insert (tmit, msg, !notify_ret);
+  }
+
+  /* End of message. */
+  if (GNUNET_YES == notify_ret)
+  {
+    msg->type = htons (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END);
+    msg->size = htons (sizeof (*msg));
+    transmit_queue_insert (tmit, msg, GNUNET_YES);
+    /* FIXME: wait for ACK before setting in_transmit to no */
+    tmit->in_transmit = GNUNET_NO;
+  }
+}
+
+
+/**
+ * Request a modifier from a client to transmit.
+ *
+ * @param tmit  Transmission handle.
+ */
+static void
+transmit_mod (struct GNUNET_PSYC_TransmitHandle *tmit)
+{
+  uint16_t max_data_size = 0;
+  uint16_t data_size = 0;
+  char data[GNUNET_MULTICAST_FRAGMENT_MAX_PAYLOAD] = "";
+  struct GNUNET_MessageHeader *msg = (struct GNUNET_MessageHeader *) data;
+  int notify_ret = GNUNET_YES;
+
+  switch (tmit->state)
+  {
+  case GNUNET_PSYC_MESSAGE_STATE_MODIFIER:
+  {
+    struct GNUNET_PSYC_MessageModifier *mod
+      = (struct GNUNET_PSYC_MessageModifier *) msg;
+    msg->type = htons (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MODIFIER);
+    msg->size = sizeof (struct GNUNET_PSYC_MessageModifier);
+
+    if (NULL != tmit->notify_mod)
+    {
+      max_data_size = GNUNET_PSYC_MODIFIER_MAX_PAYLOAD;
+      data_size = max_data_size;
+      tmit->in_notify = GNUNET_YES;
+      notify_ret = tmit->notify_mod (tmit->notify_mod_cls, &data_size, &mod[1],
+                                     &mod->oper, &mod->value_size);
+      tmit->in_notify = GNUNET_NO;
+    }
+
+    mod->name_size = strnlen ((char *) &mod[1], data_size) + 1;
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "transmit_mod (ret: %d, size: %u + %u): %.*s\n",
+         notify_ret, mod->name_size, mod->value_size, data_size, &mod[1]);
+    if (mod->name_size < data_size)
+    {
+      tmit->mod_value_remaining
+        = mod->value_size - (data_size - mod->name_size);
+      mod->value_size = htonl (mod->value_size);
+      mod->name_size = htons (mod->name_size);
+    }
+    else if (0 < data_size)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got invalid modifier name.\n");
+      notify_ret = GNUNET_SYSERR;
+    }
+    break;
+  }
+  case GNUNET_PSYC_MESSAGE_STATE_MOD_CONT:
+  {
+    msg->type = htons (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MOD_CONT);
+    msg->size = sizeof (struct GNUNET_MessageHeader);
+
+    if (NULL != tmit->notify_mod)
+    {
+      max_data_size = GNUNET_PSYC_MOD_CONT_MAX_PAYLOAD;
+      data_size = max_data_size;
+      tmit->in_notify = GNUNET_YES;
+      notify_ret = tmit->notify_mod (tmit->notify_mod_cls,
+                                     &data_size, &msg[1], NULL, NULL);
+      tmit->in_notify = GNUNET_NO;
+    }
+    tmit->mod_value_remaining -= data_size;
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "transmit_mod (ret: %d, size: %u): %.*s\n",
+         notify_ret, data_size, data_size, &msg[1]);
+    break;
+  }
+  default:
+    GNUNET_assert (0);
+  }
+
+  switch (notify_ret)
+  {
+  case GNUNET_NO:
+    if (0 == data_size)
+    { /* Transmission paused, nothing to send. */
+      tmit->paused = GNUNET_YES;
+      return;
+    }
+    tmit->state
+      = (0 == tmit->mod_value_remaining)
+      ? GNUNET_PSYC_MESSAGE_STATE_MODIFIER
+      : GNUNET_PSYC_MESSAGE_STATE_MOD_CONT;
+    break;
+
+  case GNUNET_YES: /* End of modifiers. */
+    GNUNET_assert (0 == tmit->mod_value_remaining);
+    break;
+
+  default:
+    LOG (GNUNET_ERROR_TYPE_ERROR,
+         "TransmitNotifyModifier callback returned with error.\n");
+
+    tmit->state = GNUNET_PSYC_MESSAGE_STATE_CANCEL;
+    msg->type = htons (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL);
+    msg->size = htons (sizeof (*msg));
+    transmit_queue_insert (tmit, msg, GNUNET_YES);
+    tmit->in_transmit = GNUNET_NO;
+    return;
+  }
+
+  if (0 < data_size)
+  {
+    GNUNET_assert (data_size <= max_data_size);
+    msg->size = htons (msg->size + data_size);
+    transmit_queue_insert (tmit, msg, GNUNET_NO);
+  }
+
+  if (GNUNET_YES == notify_ret)
+  {
+    tmit->state = GNUNET_PSYC_MESSAGE_STATE_DATA;
+    if (0 == tmit->acks_pending)
+      transmit_data (tmit);
+  }
+  else
+  {
+    transmit_mod (tmit);
+  }
+}
+
+
+int
+transmit_notify_env (void *cls, uint16_t *data_size, void *data, uint8_t *oper,
+                     uint32_t *full_value_size)
+
+{
+  struct GNUNET_PSYC_TransmitHandle *tmit = cls;
+  uint16_t name_size = 0;
+  uint32_t value_size = 0;
+  const char *value = NULL;
+
+  if (NULL != oper)
+  { /* New modifier */
+    if (NULL != tmit->mod)
+      tmit->mod = tmit->mod->next;
+    if (NULL == tmit->mod)
+    { /* No more modifiers, continue with data */
+      *data_size = 0;
+      return GNUNET_YES;
+    }
+
+    GNUNET_assert (tmit->mod->value_size < UINT32_MAX);
+    *full_value_size = tmit->mod->value_size;
+    *oper = tmit->mod->oper;
+    name_size = strlen (tmit->mod->name) + 1;
+
+    if (name_size + tmit->mod->value_size <= *data_size)
+    {
+      value_size = tmit->mod->value_size;
+      *data_size = name_size + value_size;
+    }
+    else /* full modifier does not fit in data, continuation needed */
+    {
+      value_size = *data_size - name_size;
+      tmit->mod_value = tmit->mod->value + value_size;
+    }
+
+    GNUNET_memcpy (data, tmit->mod->name, name_size);
+    GNUNET_memcpy ((char *)data + name_size, tmit->mod->value, value_size);
+    return GNUNET_NO;
+  }
+  else
+  { /* Modifier continuation */
+    GNUNET_assert (NULL != tmit->mod_value && 0 < tmit->mod_value_remaining);
+    value = tmit->mod_value;
+    if (tmit->mod_value_remaining <= *data_size)
+    {
+      value_size = tmit->mod_value_remaining;
+      tmit->mod_value = NULL;
+    }
+    else
+    {
+      value_size = *data_size;
+      tmit->mod_value += value_size;
+    }
+
+    if (*data_size < value_size)
+    {
+      LOG (GNUNET_ERROR_TYPE_DEBUG,
+           "Value in environment larger than buffer: %u < %zu\n",
+           *data_size, value_size);
+      *data_size = 0;
+      return GNUNET_NO;
+    }
+
+    *data_size = value_size;
+    GNUNET_memcpy (data, value, value_size);
+    return (NULL == tmit->mod_value) ? GNUNET_YES : GNUNET_NO;
+  }
+}
+
+
+/**
+ * Transmit a message.
+ *
+ * @param tmit
+ *        Transmission handle.
+ * @param method_name
+ *        Which method should be invoked.
+ * @param env
+ *        Environment for the message.
+ *        Should stay available until the first call to notify_data.
+ *        Can be NULL if there are no modifiers or @a notify_mod is
+ *        provided instead.
+ * @param notify_mod
+ *        Function to call to obtain modifiers.
+ *        Can be NULL if there are no modifiers or @a env is provided instead.
+ * @param notify_data
+ *        Function to call to obtain fragments of the data.
+ * @param notify_cls
+ *        Closure for @a notify_mod and @a notify_data.
+ * @param flags
+ *        Flags for the message being transmitted.
+ *
+ * @return #GNUNET_OK if the transmission was started.
+ *         #GNUNET_SYSERR if another transmission is already going on.
+ */
+int
+GNUNET_PSYC_transmit_message (struct GNUNET_PSYC_TransmitHandle *tmit,
+                              const char *method_name,
+                              const struct GNUNET_PSYC_Environment *env,
+                              GNUNET_PSYC_TransmitNotifyModifier notify_mod,
+                              GNUNET_PSYC_TransmitNotifyData notify_data,
+                              void *notify_cls,
+                              uint32_t flags)
+{
+  if (GNUNET_NO != tmit->in_transmit)
+    return GNUNET_SYSERR;
+  tmit->in_transmit = GNUNET_YES;
+
+  size_t size = strlen (method_name) + 1;
+  struct GNUNET_PSYC_MessageMethod *pmeth;
+
+  tmit->env = GNUNET_MQ_msg_extra (tmit->msg,
+                                   GNUNET_MULTICAST_FRAGMENT_MAX_PAYLOAD,
+                                   GNUNET_MESSAGE_TYPE_PSYC_MESSAGE);
+  /* store current message size in host byte order
+   * then later switch it to network byte order before sending */
+  tmit->msg->size = sizeof (*tmit->msg) + sizeof (*pmeth) + size;
+
+  if (NULL != notify_mod)
+  {
+    tmit->notify_mod = notify_mod;
+    tmit->notify_mod_cls = notify_cls;
+  }
+  else
+  {
+    tmit->notify_mod = &transmit_notify_env;
+    tmit->notify_mod_cls = tmit;
+    if (NULL != env)
+    {
+      struct GNUNET_PSYC_Modifier mod = {};
+      mod.next = GNUNET_PSYC_env_head (env);
+      tmit->mod = &mod;
+
+      struct GNUNET_PSYC_Modifier *m = tmit->mod;
+      while (NULL != (m = m->next))
+      {
+        if (m->oper != GNUNET_PSYC_OP_SET)
+          flags |= GNUNET_PSYC_MASTER_TRANSMIT_STATE_MODIFY;
+      }
+    }
+    else
+    {
+      tmit->mod = NULL;
+    }
+  }
+
+  pmeth = (struct GNUNET_PSYC_MessageMethod *) &tmit->msg[1];
+  pmeth->header.type = htons (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_METHOD);
+  pmeth->header.size = htons (sizeof (*pmeth) + size);
+  pmeth->flags = htonl (flags);
+  GNUNET_memcpy (&pmeth[1], method_name, size);
+
+  tmit->state = GNUNET_PSYC_MESSAGE_STATE_MODIFIER;
+  tmit->notify_data = notify_data;
+  tmit->notify_data_cls = notify_cls;
+
+  transmit_mod (tmit);
+  return GNUNET_OK;
+}
+
+
+/**
+ * Resume transmission.
+ *
+ * @param tmit  Transmission handle.
+ */
+void
+GNUNET_PSYC_transmit_resume (struct GNUNET_PSYC_TransmitHandle *tmit)
+{
+  if (GNUNET_YES != tmit->in_transmit || GNUNET_NO != tmit->in_notify)
+    return;
+
+  if (0 == tmit->acks_pending)
+  {
+    tmit->paused = GNUNET_NO;
+    transmit_data (tmit);
+  }
+}
+
+
+/**
+ * Abort transmission request.
+ *
+ * @param tmit  Transmission handle.
+ */
+void
+GNUNET_PSYC_transmit_cancel (struct GNUNET_PSYC_TransmitHandle *tmit)
+{
+  if (GNUNET_NO == tmit->in_transmit)
+    return;
+
+  tmit->state = GNUNET_PSYC_MESSAGE_STATE_CANCEL;
+  tmit->in_transmit = GNUNET_NO;
+  tmit->paused = GNUNET_NO;
+
+  /* FIXME */
+  struct GNUNET_MessageHeader msg;
+  msg.type = htons (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL);
+  msg.size = htons (sizeof (msg));
+  transmit_queue_insert (tmit, &msg, GNUNET_YES);
+}
+
+
+/**
+ * Got acknowledgement of a transmitted message part, continue transmission.
+ *
+ * @param tmit  Transmission handle.
+ */
+void
+GNUNET_PSYC_transmit_got_ack (struct GNUNET_PSYC_TransmitHandle *tmit)
+{
+  if (0 == tmit->acks_pending)
+  {
+    LOG (GNUNET_ERROR_TYPE_WARNING, "Ignoring extraneous message ACK\n");
+    GNUNET_break (0);
+    return;
+  }
+  tmit->acks_pending--;
+
+  if (GNUNET_YES == tmit->paused)
+    return;
+
+  switch (tmit->state)
+  {
+  case GNUNET_PSYC_MESSAGE_STATE_MODIFIER:
+  case GNUNET_PSYC_MESSAGE_STATE_MOD_CONT:
+    transmit_mod (tmit);
+    break;
+
+  case GNUNET_PSYC_MESSAGE_STATE_DATA:
+    transmit_data (tmit);
+    break;
+
+  case GNUNET_PSYC_MESSAGE_STATE_END:
+  case GNUNET_PSYC_MESSAGE_STATE_CANCEL:
+    break;
+
+  default:
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "Ignoring message ACK in state %u.\n", tmit->state);
+  }
+}
+
+
+/**** Receiving messages ****/
+
+
+/**
+ * Create handle for receiving messages.
+ */
+struct GNUNET_PSYC_ReceiveHandle *
+GNUNET_PSYC_receive_create (GNUNET_PSYC_MessageCallback message_cb,
+                            GNUNET_PSYC_MessagePartCallback message_part_cb,
+                            void *cb_cls)
+{
+  struct GNUNET_PSYC_ReceiveHandle *recv = GNUNET_malloc (sizeof (*recv));
+  recv->message_cb = message_cb;
+  recv->message_part_cb = message_part_cb;
+  recv->cb_cls = cb_cls;
+  return recv;
+}
+
+
+/**
+ * Destroy handle for receiving messages.
+ */
+void
+GNUNET_PSYC_receive_destroy (struct GNUNET_PSYC_ReceiveHandle *recv)
+{
+  GNUNET_free (recv);
+}
+
+
+/**
+ * Reset stored data related to the last received message.
+ */
+void
+GNUNET_PSYC_receive_reset (struct GNUNET_PSYC_ReceiveHandle *recv)
+{
+  recv->state = GNUNET_PSYC_MESSAGE_STATE_START;
+  recv->flags = 0;
+  recv->message_id = 0;
+  recv->mod_value_size = 0;
+  recv->mod_value_size_expected = 0;
+}
+
+
+static void
+recv_error (struct GNUNET_PSYC_ReceiveHandle *recv)
+{
+  if (NULL != recv->message_part_cb)
+    recv->message_part_cb (recv->cb_cls, NULL, NULL);
+
+  if (NULL != recv->message_cb)
+    recv->message_cb (recv->cb_cls, NULL);
+
+  GNUNET_PSYC_receive_reset (recv);
+}
+
+
+/**
+ * Handle incoming PSYC message.
+ *
+ * @param recv  Receive handle.
+ * @param msg   The message.
+ *
+ * @return #GNUNET_OK on success,
+ *         #GNUNET_SYSERR on receive error.
+ */
+int
+GNUNET_PSYC_receive_message (struct GNUNET_PSYC_ReceiveHandle *recv,
+                             const struct GNUNET_PSYC_MessageHeader *msg)
+{
+  uint16_t size = ntohs (msg->header.size);
+  uint32_t flags = ntohl (msg->flags);
+
+  GNUNET_PSYC_log_message (GNUNET_ERROR_TYPE_DEBUG,
+                           (struct GNUNET_MessageHeader *) msg);
+
+  if (GNUNET_PSYC_MESSAGE_STATE_START == recv->state)
+  {
+    recv->message_id = GNUNET_ntohll (msg->message_id);
+    recv->flags = flags;
+    recv->slave_pub_key = msg->slave_pub_key;
+    recv->mod_value_size = 0;
+    recv->mod_value_size_expected = 0;
+  }
+  else if (GNUNET_ntohll (msg->message_id) != recv->message_id)
+  {
+    // FIXME
+    LOG (GNUNET_ERROR_TYPE_WARNING,
+         "Unexpected message ID. Got: %" PRIu64 ", expected: %" PRIu64 "\n",
+         GNUNET_ntohll (msg->message_id), recv->message_id);
+    GNUNET_break_op (0);
+    recv_error (recv);
+    return GNUNET_SYSERR;
+  }
+  else if (flags != recv->flags)
+  {
+    LOG (GNUNET_ERROR_TYPE_WARNING,
+         "Unexpected message flags. Got: %lu, expected: %lu\n",
+         flags, recv->flags);
+    GNUNET_break_op (0);
+    recv_error (recv);
+    return GNUNET_SYSERR;
+  }
+
+  uint16_t pos = 0, psize = 0, ptype, size_eq, size_min;
+
+  for (pos = 0; sizeof (*msg) + pos < size; pos += psize)
+  {
+    const struct GNUNET_MessageHeader *pmsg
+      = (const struct GNUNET_MessageHeader *) ((char *) &msg[1] + pos);
+    psize = ntohs (pmsg->size);
+    ptype = ntohs (pmsg->type);
+    size_eq = size_min = 0;
+
+    if (psize < sizeof (*pmsg) || sizeof (*msg) + pos + psize > size)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                  "Dropping message of type %u with invalid size %u.\n",
+                  ptype, psize);
+      recv_error (recv);
+      return GNUNET_SYSERR;
+    }
+
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "Received message part of type %u and size %u from PSYC.\n",
+                ptype, psize);
+    GNUNET_PSYC_log_message (GNUNET_ERROR_TYPE_DEBUG, pmsg);
+
+    switch (ptype)
+    {
+    case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_METHOD:
+      size_min = sizeof (struct GNUNET_PSYC_MessageMethod);
+      break;
+    case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MODIFIER:
+      size_min = sizeof (struct GNUNET_PSYC_MessageModifier);
+      break;
+    case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MOD_CONT:
+    case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_DATA:
+      size_min = sizeof (struct GNUNET_MessageHeader);
+      break;
+    case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END:
+    case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL:
+      size_eq = sizeof (struct GNUNET_MessageHeader);
+      break;
+    default:
+      GNUNET_break_op (0);
+      recv_error (recv);
+      return GNUNET_SYSERR;
+    }
+
+    if (! ((0 < size_eq && psize == size_eq)
+           || (0 < size_min && size_min <= psize)))
+    {
+      GNUNET_break_op (0);
+      recv_error (recv);
+      return GNUNET_SYSERR;
+    }
+
+    switch (ptype)
+    {
+    case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_METHOD:
+    {
+      struct GNUNET_PSYC_MessageMethod *meth
+        = (struct GNUNET_PSYC_MessageMethod *) pmsg;
+
+      if (GNUNET_PSYC_MESSAGE_STATE_START != recv->state)
+      {
+        LOG (GNUNET_ERROR_TYPE_WARNING,
+             "Dropping out of order message method (%u).\n",
+             recv->state);
+        /* It is normal to receive an incomplete message right after 
connecting,
+         * but should not happen later.
+         * FIXME: add a check for this condition.
+         */
+        GNUNET_break_op (0);
+        recv_error (recv);
+        return GNUNET_SYSERR;
+      }
+
+      if ('\0' != *((char *) meth + psize - 1))
+      {
+        LOG (GNUNET_ERROR_TYPE_WARNING,
+             "Dropping message with malformed method. "
+             "Message ID: %" PRIu64 "\n", recv->message_id);
+        GNUNET_break_op (0);
+        recv_error (recv);
+        return GNUNET_SYSERR;
+      }
+      recv->state = GNUNET_PSYC_MESSAGE_STATE_METHOD;
+      break;
+    }
+    case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MODIFIER:
+    {
+      if (!(GNUNET_PSYC_MESSAGE_STATE_METHOD == recv->state
+            || GNUNET_PSYC_MESSAGE_STATE_MODIFIER == recv->state
+            || GNUNET_PSYC_MESSAGE_STATE_MOD_CONT == recv->state))
+      {
+        LOG (GNUNET_ERROR_TYPE_WARNING,
+             "Dropping out of order message modifier (%u).\n",
+             recv->state);
+        GNUNET_break_op (0);
+        recv_error (recv);
+        return GNUNET_SYSERR;
+      }
+
+      struct GNUNET_PSYC_MessageModifier *mod
+        = (struct GNUNET_PSYC_MessageModifier *) pmsg;
+
+      uint16_t name_size = ntohs (mod->name_size);
+      recv->mod_value_size_expected = ntohl (mod->value_size);
+      recv->mod_value_size = psize - sizeof (*mod) - name_size;
+
+      if (psize < sizeof (*mod) + name_size
+          || '\0' != *((char *) &mod[1] + name_size - 1)
+          || recv->mod_value_size_expected < recv->mod_value_size)
+      {
+        LOG (GNUNET_ERROR_TYPE_WARNING, "Dropping malformed modifier.\n");
+        GNUNET_break_op (0);
+        recv_error (recv);
+        return GNUNET_SYSERR;
+      }
+      recv->state = GNUNET_PSYC_MESSAGE_STATE_MODIFIER;
+      break;
+    }
+    case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MOD_CONT:
+    {
+      recv->mod_value_size += psize - sizeof (*pmsg);
+
+      if (!(GNUNET_PSYC_MESSAGE_STATE_MODIFIER == recv->state
+            || GNUNET_PSYC_MESSAGE_STATE_MOD_CONT == recv->state)
+          || recv->mod_value_size_expected < recv->mod_value_size)
+      {
+        LOG (GNUNET_ERROR_TYPE_WARNING,
+             "Dropping out of order message modifier continuation "
+             "!(%u == %u || %u == %u) || %lu < %lu.\n",
+             GNUNET_PSYC_MESSAGE_STATE_MODIFIER, recv->state,
+             GNUNET_PSYC_MESSAGE_STATE_MOD_CONT, recv->state,
+             recv->mod_value_size_expected, recv->mod_value_size);
+        GNUNET_break_op (0);
+        recv_error (recv);
+        return GNUNET_SYSERR;
+      }
+      break;
+    }
+    case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_DATA:
+    {
+      if (recv->state < GNUNET_PSYC_MESSAGE_STATE_METHOD
+          || recv->mod_value_size_expected != recv->mod_value_size)
+      {
+        LOG (GNUNET_ERROR_TYPE_WARNING,
+             "Dropping out of order message data fragment "
+             "(%u < %u || %lu != %lu).\n",
+             recv->state, GNUNET_PSYC_MESSAGE_STATE_METHOD,
+             recv->mod_value_size_expected, recv->mod_value_size);
+
+        GNUNET_break_op (0);
+        recv_error (recv);
+        return GNUNET_SYSERR;
+      }
+      recv->state = GNUNET_PSYC_MESSAGE_STATE_DATA;
+      break;
+    }
+    }
+
+    if (NULL != recv->message_part_cb)
+      recv->message_part_cb (recv->cb_cls, msg, pmsg);
+
+    switch (ptype)
+    {
+    case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END:
+    case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL:
+      GNUNET_PSYC_receive_reset (recv);
+      break;
+    }
+  }
+
+  if (NULL != recv->message_cb)
+    recv->message_cb (recv->cb_cls, msg);
+  return GNUNET_OK;
+}
+
+
+/**
+ * Check if @a data contains a series of valid message parts.
+ *
+ * @param      data_size    Size of @a data.
+ * @param      data        Data.
+ * @param[out] first_ptype  Type of first message part.
+ * @param[out] last_ptype   Type of last message part.
+ *
+ * @return Number of message parts found in @a data.
+ *         or GNUNET_SYSERR if the message contains invalid parts.
+ */
+int
+GNUNET_PSYC_receive_check_parts (uint16_t data_size, const char *data,
+                                 uint16_t *first_ptype, uint16_t *last_ptype)
+{
+  const struct GNUNET_MessageHeader *pmsg;
+  uint16_t parts = 0, ptype = 0, psize = 0, pos = 0;
+  if (NULL != first_ptype)
+    *first_ptype = 0;
+  if (NULL != last_ptype)
+    *last_ptype = 0;
+
+  for (pos = 0; pos < data_size; pos += psize, parts++)
+  {
+    pmsg = (const struct GNUNET_MessageHeader *) (data + pos);
+    GNUNET_PSYC_log_message (GNUNET_ERROR_TYPE_DEBUG, pmsg);
+    psize = ntohs (pmsg->size);
+    ptype = ntohs (pmsg->type);
+    if (0 == parts && NULL != first_ptype)
+      *first_ptype = ptype;
+    if (NULL != last_ptype
+        && *last_ptype < GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END)
+      *last_ptype = ptype;
+    if (psize < sizeof (*pmsg)
+        || pos + psize > data_size
+        || ptype < GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_METHOD
+        || GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL < ptype)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                  "Invalid message part of type %u and size %u.\n",
+                  ptype, psize);
+      return GNUNET_SYSERR;
+    }
+    /** @todo FIXME: check message part order */
+  }
+  return parts;
+}
+
+
+struct ParseMessageClosure
+{
+  struct GNUNET_PSYC_Environment *env;
+  const char **method_name;
+  const void **data;
+  uint16_t *data_size;
+  enum GNUNET_PSYC_MessageState msg_state;
+};
+
+
+static void
+parse_message_part_cb (void *cls,
+                       const struct GNUNET_PSYC_MessageHeader *msg,
+                       const struct GNUNET_MessageHeader *pmsg)
+{
+  struct ParseMessageClosure *pmc = cls;
+  if (NULL == pmsg)
+  {
+    pmc->msg_state = GNUNET_PSYC_MESSAGE_STATE_ERROR;
+    return;
+  }
+
+  switch (ntohs (pmsg->type))
+  {
+  case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_METHOD:
+  {
+    struct GNUNET_PSYC_MessageMethod *
+      pmeth = (struct GNUNET_PSYC_MessageMethod *) pmsg;
+    *pmc->method_name = (const char *) &pmeth[1];
+    pmc->msg_state = GNUNET_PSYC_MESSAGE_STATE_METHOD;
+    break;
+  }
+
+  case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MODIFIER:
+  {
+    struct GNUNET_PSYC_MessageModifier *
+      pmod = (struct GNUNET_PSYC_MessageModifier *) pmsg;
+
+    const char *name = (const char *) &pmod[1];
+    const void *value = name + ntohs (pmod->name_size);
+    GNUNET_PSYC_env_add (pmc->env, pmod->oper, name, value,
+                         ntohl (pmod->value_size));
+    pmc->msg_state = GNUNET_PSYC_MESSAGE_STATE_MODIFIER;
+    break;
+  }
+
+  case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_DATA:
+    *pmc->data = &pmsg[1];
+    *pmc->data_size = ntohs (pmsg->size) - sizeof (*pmsg);
+    pmc->msg_state = GNUNET_PSYC_MESSAGE_STATE_DATA;
+    break;
+
+  case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END:
+    pmc->msg_state = GNUNET_PSYC_MESSAGE_STATE_END;
+    break;
+
+  default:
+    pmc->msg_state = GNUNET_PSYC_MESSAGE_STATE_ERROR;
+  }
+}
+
+
+/**
+ * Parse PSYC message.
+ *
+ * @param msg
+ *        The PSYC message to parse.
+ * @param[out] method_name
+ *        Pointer to the method name inside @a pmsg.
+ * @param env
+ *        The environment for the message with a list of modifiers.
+ * @param[out] data
+ *        Pointer to data inside @a msg.
+ * @param[out] data_size
+ *        Size of @data is written here.
+ *
+ * @return #GNUNET_OK on success,
+ *         #GNUNET_SYSERR on parse error.
+ */
+int
+GNUNET_PSYC_message_parse (const struct GNUNET_PSYC_MessageHeader *msg,
+                           const char **method_name,
+                           struct GNUNET_PSYC_Environment *env,
+                           const void **data,
+                           uint16_t *data_size)
+{
+  struct ParseMessageClosure cls;
+  cls.env = env;
+  cls.method_name = method_name;
+  cls.data = data;
+  cls.data_size = data_size;
+
+  struct GNUNET_PSYC_ReceiveHandle *
+    recv = GNUNET_PSYC_receive_create (NULL, parse_message_part_cb, &cls);
+  int ret = GNUNET_PSYC_receive_message (recv, msg);
+  GNUNET_PSYC_receive_destroy (recv);
+
+  if (GNUNET_OK != ret)
+    return GNUNET_SYSERR;
+
+  return (GNUNET_PSYC_MESSAGE_STATE_END == cls.msg_state)
+    ? GNUNET_OK
+    : GNUNET_NO;
+}
+
+
+/**
+ * Initialize PSYC message header.
+ */
+void
+GNUNET_PSYC_message_header_init (struct GNUNET_PSYC_MessageHeader *pmsg,
+                                 const struct GNUNET_MULTICAST_MessageHeader 
*mmsg,
+                                 uint32_t flags)
+{
+  uint16_t size = ntohs (mmsg->header.size);
+  uint16_t psize = sizeof (*pmsg) + size - sizeof (*mmsg);
+
+  pmsg->header.size = htons (psize);
+  pmsg->header.type = htons (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE);
+  pmsg->message_id = mmsg->message_id;
+  pmsg->fragment_offset = mmsg->fragment_offset;
+  pmsg->flags = htonl (flags);
+
+  GNUNET_memcpy (&pmsg[1], &mmsg[1], size - sizeof (*mmsg));
+}
+
+
+/**
+ * Create a new PSYC message header from a multicast message.
+ */
+struct GNUNET_PSYC_MessageHeader *
+GNUNET_PSYC_message_header_create (const struct GNUNET_MULTICAST_MessageHeader 
*mmsg,
+                                   uint32_t flags)
+{
+  struct GNUNET_PSYC_MessageHeader *pmsg;
+  uint16_t size = ntohs (mmsg->header.size);
+  uint16_t psize = sizeof (*pmsg) + size - sizeof (*mmsg);
+
+  pmsg = GNUNET_malloc (psize);
+  GNUNET_PSYC_message_header_init (pmsg, mmsg, flags);
+  return pmsg;
+}
+
+
+/**
+ * Create a new PSYC message header from a PSYC message.
+ */
+struct GNUNET_PSYC_MessageHeader *
+GNUNET_PSYC_message_header_create_from_psyc (const struct GNUNET_PSYC_Message 
*msg)
+{
+  uint16_t msg_size = ntohs (msg->header.size);
+  struct GNUNET_PSYC_MessageHeader *
+    pmsg = GNUNET_malloc (sizeof (*pmsg) + msg_size - sizeof (*msg));
+  pmsg->header.type = htons (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE);
+  pmsg->header.size = htons (sizeof (*pmsg) + msg_size - sizeof (*msg));
+  GNUNET_memcpy (&pmsg[1], &msg[1], msg_size - sizeof (*msg));
+  return pmsg;
+}
diff --git a/src/psycutil/psyc_slicer.c b/src/psycutil/psyc_slicer.c
new file mode 100644
index 0000000..9b25d8a
--- /dev/null
+++ b/src/psycutil/psyc_slicer.c
@@ -0,0 +1,711 @@
+/*
+ * This file is part of GNUnet
+ * Copyright (C) 2013 GNUnet e.V.
+ *
+ * GNUnet is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * GNUnet 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
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+     SPDX-License-Identifier: AGPL3.0-or-later
+ */
+
+/**
+ * @author Gabor X Toth
+ *
+ * @file
+ * PSYC Slicer API
+ */
+
+#include <inttypes.h>
+
+#include "platform.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_psyc_util_lib.h"
+
+#define LOG(kind,...) GNUNET_log_from (kind, "psyc-util-slicer",__VA_ARGS__)
+
+
+/**
+ * Handle for a try-and-slice instance.
+ */
+struct GNUNET_PSYC_Slicer
+{
+  /**
+   * Method handlers: H(method_name) -> SlicerMethodCallbacks
+   */
+  struct GNUNET_CONTAINER_MultiHashMap *method_handlers;
+
+  /**
+   * Modifier handlers: H(modifier_name) -> SlicerModifierCallbacks
+   */
+  struct GNUNET_CONTAINER_MultiHashMap *modifier_handlers;
+
+  /**
+   * Receive handle for incoming messages.
+   */
+  struct GNUNET_PSYC_ReceiveHandle *recv;
+
+  /**
+   * Currently being processed message.
+   */
+  const struct GNUNET_PSYC_MessageHeader *msg;
+
+  /**
+   * Currently being processed message part.
+   */
+  const struct GNUNET_MessageHeader *pmsg;
+
+  /**
+   * ID of currently being received message.
+   */
+  uint64_t message_id;
+
+  /**
+   * Fragment offset of currently being received message.
+   */
+  uint64_t fragment_offset;
+
+  /**
+   * Flags of currently being received message.
+   */
+  uint32_t flags;
+
+  /**
+   * Method name of currently being received message.
+   */
+  char *method_name;
+
+  /**
+   * Name of currently processed modifier.
+   */
+  char *mod_name;
+
+  /**
+   * Value of currently processed modifier.
+   */
+  char *mod_value;
+
+  /**
+   * Public key of the nym the current message originates from.
+   */
+  struct GNUNET_CRYPTO_EcdsaPublicKey nym_pub_key;
+
+  /**
+   * Size of @a method_name (including terminating \0).
+   */
+  uint16_t method_name_size;
+
+  /**
+   * Size of @a modifier_name (including terminating \0).
+   */
+  uint16_t mod_name_size;
+
+  /**
+   * Size of modifier value fragment.
+   */
+  uint16_t mod_value_size;
+
+  /**
+   * Full size of modifier value.
+   */
+  uint16_t mod_full_value_size;
+
+  /**
+   * Remaining bytes from the value of the current modifier.
+   */
+  uint16_t mod_value_remaining;
+
+  /**
+   * Operator of currently processed modifier.
+   */
+  uint8_t mod_oper;
+};
+
+
+/**
+ * Callbacks for a slicer method handler.
+ */
+struct SlicerMethodCallbacks
+{
+  GNUNET_PSYC_MessageCallback msg_cb;
+  GNUNET_PSYC_MethodCallback method_cb;
+  GNUNET_PSYC_ModifierCallback modifier_cb;
+  GNUNET_PSYC_DataCallback data_cb;
+  GNUNET_PSYC_EndOfMessageCallback eom_cb;
+  void *cls;
+};
+
+
+struct SlicerMethodRemoveClosure
+{
+  struct GNUNET_PSYC_Slicer *slicer;
+  struct SlicerMethodCallbacks rm_cbs;
+};
+
+
+/**
+ * Callbacks for a slicer method handler.
+ */
+struct SlicerModifierCallbacks
+{
+  GNUNET_PSYC_ModifierCallback modifier_cb;
+  void *cls;
+};
+
+
+struct SlicerModifierRemoveClosure
+{
+  struct GNUNET_PSYC_Slicer *slicer;
+  struct SlicerModifierCallbacks rm_cbs;
+};
+
+
+/**
+ * Call a method handler for an incoming message part.
+ */
+static int
+slicer_method_handler_notify (void *cls, const struct GNUNET_HashCode *key,
+                              void *value)
+{
+  struct GNUNET_PSYC_Slicer *slicer = cls;
+  const struct GNUNET_MessageHeader *pmsg = slicer->pmsg;
+  struct SlicerMethodCallbacks *cbs = value;
+
+  uint16_t ptype = ntohs (pmsg->type);
+  switch (ptype)
+  {
+  case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_METHOD:
+  {
+    if (NULL != cbs->msg_cb)
+      cbs->msg_cb (cbs->cls, slicer->msg);
+    if (NULL == cbs->method_cb)
+      break;
+    struct GNUNET_PSYC_MessageMethod *
+      meth = (struct GNUNET_PSYC_MessageMethod *) pmsg;
+    cbs->method_cb (cbs->cls, slicer->msg, meth, slicer->message_id,
+                    slicer->method_name);
+    break;
+  }
+
+  case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MODIFIER:
+  {
+    if (NULL == cbs->modifier_cb)
+      break;
+    struct GNUNET_PSYC_MessageModifier *
+      mod = (struct GNUNET_PSYC_MessageModifier *) pmsg;
+    cbs->modifier_cb (cbs->cls, slicer->msg, &mod->header, slicer->message_id,
+                      mod->oper, (const char *) &mod[1],
+                      (const void *) &mod[1] + ntohs (mod->name_size),
+                      ntohs (mod->header.size) - sizeof (*mod) - ntohs 
(mod->name_size),
+                      ntohs (mod->value_size));
+    break;
+  }
+
+  case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MOD_CONT:
+  {
+    if (NULL == cbs->modifier_cb)
+      break;
+    cbs->modifier_cb (cbs->cls, slicer->msg, pmsg, slicer->message_id,
+                      slicer->mod_oper, slicer->mod_name, &pmsg[1],
+                      ntohs (pmsg->size) - sizeof (*pmsg),
+                      slicer->mod_full_value_size);
+    break;
+  }
+
+  case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_DATA:
+  {
+    if (NULL == cbs->data_cb)
+      break;
+    cbs->data_cb (cbs->cls, slicer->msg, pmsg, slicer->message_id,
+                  &pmsg[1], ntohs (pmsg->size) - sizeof (*pmsg));
+    break;
+  }
+
+  case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END:
+    if (NULL == cbs->eom_cb)
+      break;
+    cbs->eom_cb (cbs->cls, slicer->msg, pmsg, slicer->message_id, GNUNET_NO);
+    break;
+
+  case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL:
+    if (NULL == cbs->eom_cb)
+      break;
+    cbs->eom_cb (cbs->cls, slicer->msg, pmsg, slicer->message_id, GNUNET_YES);
+    break;
+  }
+  return GNUNET_YES;
+}
+
+
+/**
+ * Call a method handler for an incoming message part.
+ */
+static int
+slicer_modifier_handler_notify (void *cls, const struct GNUNET_HashCode *key,
+                                void *value)
+{
+  struct GNUNET_PSYC_Slicer *slicer = cls;
+  struct SlicerModifierCallbacks *cbs = value;
+
+  cbs->modifier_cb (cbs->cls, slicer->msg, slicer->pmsg, slicer->message_id,
+                    slicer->mod_oper, slicer->mod_name, slicer->mod_value,
+                    slicer->mod_value_size, slicer->mod_full_value_size);
+  return GNUNET_YES;
+}
+
+
+/**
+ * Process an incoming message and call matching handlers.
+ *
+ * @param slicer
+ *        The slicer to use.
+ * @param msg
+ *        The message as it arrived from the network.
+ */
+void
+GNUNET_PSYC_slicer_message (struct GNUNET_PSYC_Slicer *slicer,
+                            const struct GNUNET_PSYC_MessageHeader *msg)
+{
+  GNUNET_PSYC_receive_message (slicer->recv, msg);
+}
+
+
+/**
+ * Process an incoming message part and call matching handlers.
+ *
+ * @param cls
+ *        Closure.
+ * @param message_id
+ *        ID of the message.
+ * @param flags
+ *        Flags for the message.
+ *        @see enum GNUNET_PSYC_MessageFlags
+ * @param msg
+ *        The message part. as it arrived from the network.
+ */
+void
+GNUNET_PSYC_slicer_message_part (struct GNUNET_PSYC_Slicer *slicer,
+                                 const struct GNUNET_PSYC_MessageHeader *msg,
+                                 const struct GNUNET_MessageHeader *pmsg)
+{
+  slicer->msg = msg;
+  slicer->pmsg = pmsg;
+
+  uint64_t message_id = GNUNET_ntohll (msg->message_id);
+
+  uint16_t ptype = ntohs (pmsg->type);
+  if (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_METHOD == ptype)
+  {
+    struct GNUNET_PSYC_MessageMethod *
+      meth = (struct GNUNET_PSYC_MessageMethod *) pmsg;
+    slicer->method_name_size = ntohs (meth->header.size) - sizeof (*meth);
+    slicer->method_name = GNUNET_malloc (slicer->method_name_size);
+    GNUNET_memcpy (slicer->method_name, &meth[1], slicer->method_name_size);
+     slicer->message_id = message_id;
+  }
+  else
+  {
+    GNUNET_assert (message_id == slicer->message_id);
+  }
+
+  char *nym_str = GNUNET_CRYPTO_ecdsa_public_key_to_string 
(&msg->slave_pub_key);
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Slicer received message of type %u and size %u, "
+       "with ID %" PRIu64 " and method %s from %s\n",
+       ptype, ntohs (pmsg->size), message_id, slicer->method_name, nym_str);
+  GNUNET_free (nym_str);
+
+  /* try-and-slice modifier */
+
+  switch (ptype)
+  {
+  case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MODIFIER:
+  {
+    struct GNUNET_PSYC_MessageModifier *
+      mod = (struct GNUNET_PSYC_MessageModifier *) pmsg;
+    slicer->mod_oper = mod->oper;
+    slicer->mod_name_size = ntohs (mod->name_size);
+    slicer->mod_name = GNUNET_malloc (slicer->mod_name_size);
+    GNUNET_memcpy (slicer->mod_name, &mod[1], slicer->mod_name_size);
+    slicer->mod_value = (char *) &mod[1] + slicer->mod_name_size;
+    slicer->mod_full_value_size = ntohs (mod->value_size);
+    slicer->mod_value_remaining = slicer->mod_full_value_size;
+    slicer->mod_value_size
+      = ntohs (mod->header.size) - sizeof (*mod) - slicer->mod_name_size;
+    // fall through
+  }
+  case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MOD_CONT:
+    if (ptype == GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MOD_CONT)
+    {
+      slicer->mod_value = (char *) &pmsg[1];
+      slicer->mod_value_size = ntohs (pmsg->size) - sizeof (*pmsg);
+    }
+    slicer->mod_value_remaining -= slicer->mod_value_size;
+    char *name = GNUNET_malloc (slicer->mod_name_size);
+    GNUNET_memcpy (name, slicer->mod_name, slicer->mod_name_size);
+    do
+    {
+      struct GNUNET_HashCode key;
+      uint16_t name_len = strlen (name);
+      GNUNET_CRYPTO_hash (name, name_len, &key);
+      GNUNET_CONTAINER_multihashmap_get_multiple (slicer->modifier_handlers, 
&key,
+                                                  
slicer_modifier_handler_notify,
+                                                  slicer);
+      char *p = strrchr (name, '_');
+      if (NULL == p)
+        break;
+      *p = '\0';
+    } while (1);
+    GNUNET_free (name);
+  }
+
+  /* try-and-slice method */
+
+  char *name = GNUNET_malloc (slicer->method_name_size);
+  GNUNET_memcpy (name, slicer->method_name, slicer->method_name_size);
+  do
+  {
+    struct GNUNET_HashCode key;
+    uint16_t name_len = strlen (name);
+    GNUNET_CRYPTO_hash (name, name_len, &key);
+    GNUNET_CONTAINER_multihashmap_get_multiple (slicer->method_handlers, &key,
+                                                slicer_method_handler_notify,
+                                                slicer);
+    char *p = strrchr (name, '_');
+    if (NULL == p)
+      break;
+    *p = '\0';
+  } while (1);
+  GNUNET_free (name);
+
+  if (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END <= ptype)
+    GNUNET_free (slicer->method_name);
+
+  if (0 == slicer->mod_value_remaining && NULL != slicer->mod_name)
+  {
+    GNUNET_free (slicer->mod_name);
+    slicer->mod_name = NULL;
+    slicer->mod_name_size = 0;
+    slicer->mod_value_size = 0;
+    slicer->mod_full_value_size = 0;
+    slicer->mod_oper = 0;
+  }
+
+  slicer->msg = NULL;
+  slicer->pmsg = NULL;
+}
+
+
+/**
+ * Create a try-and-slice instance.
+ *
+ * A slicer processes incoming messages and notifies callbacks about matching
+ * methods or modifiers encountered.
+ *
+ * @return A new try-and-slice construct.
+ */
+struct GNUNET_PSYC_Slicer *
+GNUNET_PSYC_slicer_create (void)
+{
+  struct GNUNET_PSYC_Slicer *slicer = GNUNET_malloc (sizeof (*slicer));
+  slicer->method_handlers = GNUNET_CONTAINER_multihashmap_create (1, 
GNUNET_NO);
+  slicer->modifier_handlers = GNUNET_CONTAINER_multihashmap_create (1, 
GNUNET_NO);
+  slicer->recv = GNUNET_PSYC_receive_create (NULL,
+                                             (GNUNET_PSYC_MessagePartCallback)
+                                             GNUNET_PSYC_slicer_message_part,
+                                             slicer);
+  return slicer;
+}
+
+
+/**
+ * Add a method to the try-and-slice instance.
+ *
+ * The callbacks are called for messages with a matching @a method_name prefix.
+ *
+ * @param slicer
+ *        The try-and-slice instance to extend.
+ * @param method_name
+ *        Name of the given method, use empty string to match all.
+ * @param method_cb
+ *        Method handler invoked upon a matching message.
+ * @param modifier_cb
+ *        Modifier handler, invoked after @a method_cb
+ *        for each modifier in the message.
+ * @param data_cb
+ *        Data handler, invoked after @a modifier_cb for each data fragment.
+ * @param eom_cb
+ *        Invoked upon reaching the end of a matching message.
+ * @param cls
+ *        Closure for the callbacks.
+ */
+void
+GNUNET_PSYC_slicer_method_add (struct GNUNET_PSYC_Slicer *slicer,
+                               const char *method_name,
+                               GNUNET_PSYC_MessageCallback msg_cb,
+                               GNUNET_PSYC_MethodCallback method_cb,
+                               GNUNET_PSYC_ModifierCallback modifier_cb,
+                               GNUNET_PSYC_DataCallback data_cb,
+                               GNUNET_PSYC_EndOfMessageCallback eom_cb,
+                               void *cls)
+{
+  struct GNUNET_HashCode key;
+  GNUNET_CRYPTO_hash (method_name, strlen (method_name), &key);
+
+  struct SlicerMethodCallbacks *cbs = GNUNET_malloc (sizeof (*cbs));
+  cbs->msg_cb = msg_cb,
+  cbs->method_cb = method_cb;
+  cbs->modifier_cb = modifier_cb;
+  cbs->data_cb = data_cb;
+  cbs->eom_cb = eom_cb;
+  cbs->cls = cls;
+
+  GNUNET_CONTAINER_multihashmap_put (slicer->method_handlers, &key, cbs,
+                                     
GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
+}
+
+
+static int
+slicer_method_remove (void *cls, const struct GNUNET_HashCode *key, void 
*value)
+{
+  struct SlicerMethodRemoveClosure *rm_cls = cls;
+  struct GNUNET_PSYC_Slicer *slicer = rm_cls->slicer;
+  struct SlicerMethodCallbacks *rm_cbs = &rm_cls->rm_cbs;
+  struct SlicerMethodCallbacks *cbs = value;
+
+  if ((NULL == rm_cbs->msg_cb || cbs->msg_cb == rm_cbs->msg_cb)
+      && (NULL == rm_cbs->method_cb || cbs->method_cb == rm_cbs->method_cb)
+      && (NULL == rm_cbs->modifier_cb || cbs->modifier_cb == 
rm_cbs->modifier_cb)
+      && (NULL == rm_cbs->data_cb || cbs->data_cb == rm_cbs->data_cb)
+      && (NULL == rm_cbs->eom_cb || cbs->eom_cb == rm_cbs->eom_cb))
+  {
+    GNUNET_CONTAINER_multihashmap_remove (slicer->method_handlers, key, cbs);
+    GNUNET_free (cbs);
+    return GNUNET_NO;
+  }
+  return GNUNET_YES;
+}
+
+
+/**
+ * Remove a registered method from the try-and-slice instance.
+ *
+ * Removes one matching handler registered with the given
+ * @a method_name and  callbacks.
+ *
+ * @param slicer
+ *        The try-and-slice instance.
+ * @param method_name
+ *        Name of the method to remove.
+ * @param method_cb
+ *        Method handler.
+ * @param modifier_cb
+ *        Modifier handler.
+ * @param data_cb
+ *        Data handler.
+ * @param eom_cb
+ *        End of message handler.
+ *
+ * @return #GNUNET_OK if a method handler was removed,
+ *         #GNUNET_NO if no handler matched the given method name and 
callbacks.
+ */
+int
+GNUNET_PSYC_slicer_method_remove (struct GNUNET_PSYC_Slicer *slicer,
+                                  const char *method_name,
+                                  GNUNET_PSYC_MessageCallback msg_cb,
+                                  GNUNET_PSYC_MethodCallback method_cb,
+                                  GNUNET_PSYC_ModifierCallback modifier_cb,
+                                  GNUNET_PSYC_DataCallback data_cb,
+                                  GNUNET_PSYC_EndOfMessageCallback eom_cb)
+{
+  struct GNUNET_HashCode key;
+  GNUNET_CRYPTO_hash (method_name, strlen (method_name), &key);
+
+  struct SlicerMethodRemoveClosure rm_cls;
+  rm_cls.slicer = slicer;
+  struct SlicerMethodCallbacks *rm_cbs = &rm_cls.rm_cbs;
+  rm_cbs->msg_cb = msg_cb;
+  rm_cbs->method_cb = method_cb;
+  rm_cbs->modifier_cb = modifier_cb;
+  rm_cbs->data_cb = data_cb;
+  rm_cbs->eom_cb = eom_cb;
+
+  return
+    (GNUNET_SYSERR
+     == GNUNET_CONTAINER_multihashmap_get_multiple (slicer->method_handlers, 
&key,
+                                                    slicer_method_remove,
+                                                    &rm_cls))
+    ? GNUNET_NO
+    : GNUNET_OK;
+}
+
+
+/**
+ * Watch a place for changed objects.
+ *
+ * @param slicer
+ *        The try-and-slice instance.
+ * @param object_filter
+ *        Object prefix to match.
+ * @param modifier_cb
+ *        Function to call when encountering a state modifier.
+ * @param cls
+ *        Closure for callback.
+ */
+void
+GNUNET_PSYC_slicer_modifier_add (struct GNUNET_PSYC_Slicer *slicer,
+                                 const char *object_filter,
+                                 GNUNET_PSYC_ModifierCallback modifier_cb,
+                                 void *cls)
+{
+  struct SlicerModifierCallbacks *cbs = GNUNET_malloc (sizeof *cbs);
+  cbs->modifier_cb = modifier_cb;
+  cbs->cls = cls;
+
+  struct GNUNET_HashCode key;
+  GNUNET_CRYPTO_hash (object_filter, strlen (object_filter), &key);
+  GNUNET_CONTAINER_multihashmap_put (slicer->modifier_handlers, &key, cbs,
+                                     
GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
+}
+
+
+static int
+slicer_modifier_remove (void *cls, const struct GNUNET_HashCode *key, void 
*value)
+{
+  struct SlicerModifierRemoveClosure *rm_cls = cls;
+  struct GNUNET_PSYC_Slicer *slicer = rm_cls->slicer;
+  struct SlicerModifierCallbacks *rm_cbs = &rm_cls->rm_cbs;
+  struct SlicerModifierCallbacks *cbs = value;
+
+  if (cbs->modifier_cb == rm_cbs->modifier_cb)
+  {
+    GNUNET_CONTAINER_multihashmap_remove (slicer->modifier_handlers, key, cbs);
+    GNUNET_free (cbs);
+    return GNUNET_NO;
+  }
+  return GNUNET_YES;
+}
+
+
+/**
+ * Remove a registered modifier from the try-and-slice instance.
+ *
+ * Removes one matching handler registered with the given
+ * @a object_filter and @a modifier_cb.
+ *
+ * @param slicer
+ *        The try-and-slice instance.
+ * @param object_filter
+ *        Object prefix to match.
+ * @param modifier_cb
+ *        Function to call when encountering a state modifier changes.
+ */
+int
+GNUNET_PSYC_slicer_modifier_remove (struct GNUNET_PSYC_Slicer *slicer,
+                                    const char *object_filter,
+                                    GNUNET_PSYC_ModifierCallback modifier_cb)
+{
+  struct GNUNET_HashCode key;
+  GNUNET_CRYPTO_hash (object_filter, strlen (object_filter), &key);
+
+  struct SlicerModifierRemoveClosure rm_cls;
+  rm_cls.slicer = slicer;
+  struct SlicerModifierCallbacks *rm_cbs = &rm_cls.rm_cbs;
+  rm_cbs->modifier_cb = modifier_cb;
+
+  return
+    (GNUNET_SYSERR
+     == GNUNET_CONTAINER_multihashmap_get_multiple (slicer->modifier_handlers, 
&key,
+                                                    slicer_modifier_remove,
+                                                    &rm_cls))
+    ? GNUNET_NO
+    : GNUNET_OK;
+ }
+
+
+static int
+slicer_method_free (void *cls, const struct GNUNET_HashCode *key, void *value)
+{
+  struct SlicerMethodCallbacks *cbs = value;
+  GNUNET_free (cbs);
+  return GNUNET_YES;
+}
+
+
+static int
+slicer_modifier_free (void *cls, const struct GNUNET_HashCode *key, void 
*value)
+{
+  struct SlicerModifierCallbacks *cbs = value;
+  GNUNET_free (cbs);
+  return GNUNET_YES;
+}
+
+
+/**
+ * Remove all registered method handlers.
+ *
+ * @param slicer
+ *        Slicer to clear.
+ */
+void
+GNUNET_PSYC_slicer_method_clear (struct GNUNET_PSYC_Slicer *slicer)
+{
+  GNUNET_CONTAINER_multihashmap_iterate (slicer->method_handlers,
+                                         slicer_method_free, NULL);
+  GNUNET_CONTAINER_multihashmap_clear (slicer->method_handlers);
+}
+
+
+/**
+ * Remove all registered modifier handlers.
+ *
+ * @param slicer
+ *        Slicer to clear.
+ */
+void
+GNUNET_PSYC_slicer_modifier_clear (struct GNUNET_PSYC_Slicer *slicer)
+{
+  GNUNET_CONTAINER_multihashmap_iterate (slicer->modifier_handlers,
+                                         slicer_modifier_free, NULL);
+  GNUNET_CONTAINER_multihashmap_clear (slicer->modifier_handlers);
+}
+
+
+/**
+ * Remove all registered method & modifier handlers.
+ *
+ * @param slicer
+ *        Slicer to clear.
+ */
+void
+GNUNET_PSYC_slicer_clear (struct GNUNET_PSYC_Slicer *slicer)
+{
+  GNUNET_PSYC_slicer_method_clear (slicer);
+  GNUNET_PSYC_slicer_modifier_clear (slicer);
+}
+
+
+/**
+ * Destroy a given try-and-slice instance.
+ *
+ * @param slicer
+ *        Slicer to destroy
+ */
+void
+GNUNET_PSYC_slicer_destroy (struct GNUNET_PSYC_Slicer *slicer)
+{
+  GNUNET_PSYC_slicer_clear (slicer);
+  GNUNET_CONTAINER_multihashmap_destroy (slicer->method_handlers);
+  GNUNET_CONTAINER_multihashmap_destroy (slicer->modifier_handlers);
+  GNUNET_PSYC_receive_destroy (slicer->recv);
+  GNUNET_free (slicer);
+}
diff --git a/src/psycutil/test_psyc_env.c b/src/psycutil/test_psyc_env.c
new file mode 100644
index 0000000..432e155
--- /dev/null
+++ b/src/psycutil/test_psyc_env.c
@@ -0,0 +1,96 @@
+/*
+ * This file is part of GNUnet.
+ * Copyright (C) 2013 GNUnet e.V.
+ *
+ * GNUnet is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * GNUnet 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
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+     SPDX-License-Identifier: AGPL3.0-or-later
+ */
+
+/**
+ * @author Gabor X Toth
+ *
+ * @file
+ * Tests for the environment library.
+ */
+
+#include "platform.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_testing_lib.h"
+#include "gnunet_psyc_util_lib.h"
+
+struct GNUNET_PSYC_Modifier mods[] = {
+  { .oper = GNUNET_PSYC_OP_SET,
+    .name = "_foo", .value = "foo", .value_size = 3 },
+
+  { .oper = GNUNET_PSYC_OP_ASSIGN,
+    .name = "_foo_bar", .value = "foo bar", .value_size = 7 },
+
+  { .oper = GNUNET_PSYC_OP_AUGMENT,
+    .name = "_foo_bar_baz", .value = "foo bar baz", .value_size = 11 }
+};
+
+struct ItCls
+{
+  size_t n;
+};
+
+int
+iterator (void *cls, enum GNUNET_PSYC_Operator oper,
+          const char *name, const char *value, uint32_t value_size)
+{
+  struct ItCls *it_cls = cls;
+  struct GNUNET_PSYC_Modifier *m = &mods[it_cls->n++];
+
+  GNUNET_assert (oper == m->oper);
+  GNUNET_assert (value_size == m->value_size);
+  GNUNET_assert (0 == memcmp (name, m->name, strlen (m->name)));
+  GNUNET_assert (0 == memcmp (value, m->value, m->value_size));
+
+  return GNUNET_YES;
+}
+
+int
+main (int argc, char *argv[])
+{
+  GNUNET_log_setup ("test-env", "WARNING", NULL);
+
+  struct GNUNET_PSYC_Environment *env = GNUNET_PSYC_env_create ();
+  GNUNET_assert (NULL != env);
+  int i, len = 3;
+
+  for (i = 0; i < len; i++)
+  {
+    GNUNET_PSYC_env_add (env, mods[i].oper, mods[i].name,
+                         mods[i].value, mods[i].value_size);
+  }
+
+  struct ItCls it_cls = { .n = 0 };
+  GNUNET_PSYC_env_iterate (env, iterator, &it_cls);
+  GNUNET_assert (len == it_cls.n);
+
+  for (i = 0; i < len; i++)
+  {
+    enum GNUNET_PSYC_Operator oper;
+    const char *name;
+    const void *value;
+    size_t value_size;
+    GNUNET_PSYC_env_shift (env, &oper, &name, &value, &value_size);
+    GNUNET_assert (len - i - 1 == GNUNET_PSYC_env_get_count (env));
+  }
+
+  GNUNET_PSYC_env_destroy (env);
+
+  return 0;
+}
diff --git a/src/social/.gitignore b/src/social/.gitignore
new file mode 100644
index 0000000..875aa11
--- /dev/null
+++ b/src/social/.gitignore
@@ -0,0 +1,3 @@
+gnunet-social
+gnunet-service-social
+test_social
diff --git a/src/social/Makefile.am b/src/social/Makefile.am
new file mode 100644
index 0000000..94a9ba1
--- /dev/null
+++ b/src/social/Makefile.am
@@ -0,0 +1,79 @@
+# This Makefile.am is in the public domain
+AM_CPPFLAGS = -I$(top_srcdir)/src/include
+
+pkgcfgdir= $(pkgdatadir)/config.d/
+
+libexecdir= $(pkglibdir)/libexec/
+
+pkgcfg_DATA = \
+  social.conf
+
+
+if MINGW
+ WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols
+endif
+
+if USE_COVERAGE
+  AM_CFLAGS = --coverage -O0
+  XLIB = -lgcov
+endif
+
+lib_LTLIBRARIES = libgnunetsocial.la
+
+libgnunetsocial_la_SOURCES = \
+  social_api.c social.h
+libgnunetsocial_la_LIBADD = \
+  $(top_builddir)/src/util/libgnunetutil.la \
+  $(top_builddir)/src/psycutil/libgnunetpsycutil.la \
+  $(GN_LIBINTL) $(XLIB)
+libgnunetsocial_la_LDFLAGS = \
+  $(GN_LIB_LDFLAGS)  $(WINFLAGS) \
+  -version-info 0:0:0
+
+bin_PROGRAMS = \
+ gnunet-social
+
+libexec_PROGRAMS = \
+ gnunet-service-social
+
+gnunet_social_SOURCES = \
+  gnunet-social.c
+gnunet_social_LDADD = \
+  libgnunetsocial.la \
+  $(top_builddir)/src/psycutil/libgnunetpsycutil.la \
+  $(top_builddir)/src/util/libgnunetutil.la
+
+gnunet_service_social_SOURCES = \
+ gnunet-service-social.c
+gnunet_service_social_LDADD = \
+  $(top_builddir)/src/util/libgnunetutil.la \
+  $(top_builddir)/src/statistics/libgnunetstatistics.la \
+  $(top_builddir)/src/psycutil/libgnunetpsycutil.la \
+  $(top_builddir)/src/psyc/libgnunetpsyc.la \
+  $(top_builddir)/src/identity/libgnunetidentity.la \
+  $(top_builddir)/src/gns/libgnunetgns.la \
+  $(top_builddir)/src/namestore/libgnunetnamestore.la \
+  $(GN_LIBINTL)
+
+
+if HAVE_TESTING
+check_PROGRAMS = \
+ test_social
+endif
+
+if ENABLE_TEST_RUN
+AM_TESTS_ENVIRONMENT=export 
GNUNET_PREFIX=$${GNUNET_PREFIX:address@hidden@};export 
PATH=$${GNUNET_PREFIX:address@hidden@}/bin:$$PATH;unset XDG_DATA_HOME;unset 
XDG_CONFIG_HOME;
+TESTS = $(check_PROGRAMS)
+endif
+
+test_social_SOURCES = \
+ test_social.c
+test_social_LDADD = \
+  libgnunetsocial.la \
+  $(top_builddir)/src/testing/libgnunettesting.la \
+  $(top_builddir)/src/util/libgnunetutil.la \
+  $(top_builddir)/src/psycutil/libgnunetpsycutil.la \
+  $(top_builddir)/src/identity/libgnunetidentity.la
+
+EXTRA_DIST = \
+  test_social.conf
diff --git a/src/social/gnunet-service-social.c 
b/src/social/gnunet-service-social.c
new file mode 100644
index 0000000..33fabae
--- /dev/null
+++ b/src/social/gnunet-service-social.c
@@ -0,0 +1,3760 @@
+/*
+ * This file is part of GNUnet
+ * Copyright (C) 2013 GNUnet e.V.
+ *
+ * GNUnet is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * GNUnet 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
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+     SPDX-License-Identifier: AGPL3.0-or-later
+ */
+
+/**
+ * @file social/gnunet-service-social.c
+ * @brief Social service
+ * @author Gabor X Toth
+ */
+
+#include <inttypes.h>
+#include <strings.h>
+
+#include "platform.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_constants.h"
+#include "gnunet_protocols.h"
+#include "gnunet_identity_service.h"
+#include "gnunet_namestore_service.h"
+#include "gnunet_gns_service.h"
+#include "gnunet_statistics_service.h"
+#include "gnunet_psyc_service.h"
+#include "gnunet_psyc_util_lib.h"
+#include "gnunet_social_service.h"
+#include "social.h"
+
+
+/**
+ * Handle to our current configuration.
+ */
+static const struct GNUNET_CONFIGURATION_Handle *cfg;
+
+/**
+ * Service handle.
+ */
+static struct GNUNET_SERVICE_Handle *service;
+
+/* Handles to other services */
+static struct GNUNET_IDENTITY_Handle *id;
+static struct GNUNET_GNS_Handle *gns;
+static struct GNUNET_NAMESTORE_Handle *namestore;
+static struct GNUNET_STATISTICS_Handle *stats;
+
+/**
+ * ID of this peer.
+ */
+static struct GNUNET_PeerIdentity this_peer;
+
+/**
+ * All connected hosts.
+ * H(place_pub_key) -> struct Host
+ */
+static struct GNUNET_CONTAINER_MultiHashMap *hosts;
+
+/**
+ * All connected guests.
+ * H(place_pub_key) -> struct Guest
+ */
+static struct GNUNET_CONTAINER_MultiHashMap *guests;
+
+/**
+ * Connected guests per place.
+ * H(place_pub_key) -> ego_pub_key -> struct Guest
+ */
+static struct GNUNET_CONTAINER_MultiHashMap *place_guests;
+
+/**
+ * Places entered as host or guest.
+ * H(place_pub_key) -> struct HostEnterRequest OR struct GuestEnterRequest
+ */
+static struct GNUNET_CONTAINER_MultiHashMap *places;
+
+/**
+ * Places entered per application.
+ * H(app_id) -> H(place_pub_key) -> NULL
+ */
+static struct GNUNET_CONTAINER_MultiHashMap *apps_places;
+
+/**
+ * Application subscriptions per place.
+ * H(place_pub_key) -> H(app_id)
+ */
+//static struct GNUNET_CONTAINER_MultiHashMap *places_apps;
+
+/**
+ * Connected applications.
+ * H(app_id) -> struct Application
+ */
+static struct GNUNET_CONTAINER_MultiHashMap *apps;
+
+/**
+ * All egos.
+ * H(ego_pub_key) -> struct Ego
+ */
+static struct GNUNET_CONTAINER_MultiHashMap *egos;
+
+/**
+ * Directory for storing social data.
+ * Default: $GNUNET_DATA_HOME/social
+ */
+static char *dir_social;
+
+/**
+ * Directory for storing place data.
+ * $dir_social/places
+ */
+static char *dir_places;
+
+/**
+ * Directory for storing app data.
+ * $dir_social/apps
+ */
+static char *dir_apps;
+
+
+/**
+ * Message fragment transmission queue.
+ */
+struct FragmentTransmitQueue
+{
+  struct FragmentTransmitQueue *prev;
+  struct FragmentTransmitQueue *next;
+
+  struct GNUNET_SERVICE_Client *client;
+
+  /**
+   * Pointer to the next message part inside the data after this struct.
+   */
+  struct GNUNET_MessageHeader *next_part;
+
+  /**
+   * Size of message.
+   */
+  uint16_t size;
+
+  /**
+   * @see enum GNUNET_PSYC_MessageState
+   */
+  uint8_t state;
+
+  /* Followed by one or more message parts. */
+};
+
+
+/**
+ * Message transmission queue.
+ */
+struct MessageTransmitQueue
+{
+  struct MessageTransmitQueue *prev;
+  struct MessageTransmitQueue *next;
+
+  struct FragmentTransmitQueue *frags_head;
+  struct FragmentTransmitQueue *frags_tail;
+
+  struct GNUNET_SERVICE_Client *client;
+};
+
+/**
+ * List of connected clients.
+ */
+struct ClientListItem
+{
+  struct ClientListItem *prev;
+  struct ClientListItem *next;
+
+  struct GNUNET_SERVICE_Client *client;
+};
+
+
+/**
+ * Common part of the client context for both a host and guest.
+ */
+struct Place
+{
+  struct ClientListItem *clients_head;
+  struct ClientListItem *clients_tail;
+
+  struct MessageTransmitQueue *tmit_msgs_head;
+  struct MessageTransmitQueue *tmit_msgs_tail;
+
+  struct GNUNET_PSYC_Channel *channel;
+
+  /**
+   * Private key of home in case of a host.
+   */
+  struct GNUNET_CRYPTO_EddsaPublicKey key;
+
+  /**
+   * Public key of place.
+   */
+  struct GNUNET_CRYPTO_EddsaPublicKey pub_key;
+
+  /**
+   * Hash of @a pub_key.
+   */
+  struct GNUNET_HashCode pub_key_hash;
+
+  /**
+   * Private key of ego.
+   */
+  struct GNUNET_CRYPTO_EcdsaPrivateKey ego_key;
+
+  /**
+   * Public key of ego.
+   */
+  struct GNUNET_CRYPTO_EcdsaPublicKey ego_pub_key;
+
+  /**
+   * Hash of @a ego_pub_key.
+   */
+  struct GNUNET_HashCode ego_pub_hash;
+
+  /**
+   * Slicer for processing incoming messages.
+   */
+  struct GNUNET_PSYC_Slicer *slicer;
+
+  /**
+   * Last message ID received for the place.
+   * 0 if there is no such message.
+   */
+  uint64_t max_message_id;
+
+  /**
+   * Offset where the file is currently being written.
+   */
+  uint64_t file_offset;
+
+  /**
+   * Whether or not to save the file (#GNUNET_YES or #GNUNET_NO)
+   */
+  uint8_t file_save;
+
+  /**
+   * Is this place ready to receive messages from client?
+   * #GNUNET_YES or #GNUNET_NO
+   */
+  uint8_t is_ready;
+
+  /**
+   * Is the client disconnecting?
+   * #GNUNET_YES or #GNUNET_NO
+   */
+  uint8_t is_disconnecting;
+
+  /**
+   * Is this a host (#GNUNET_YES), or guest (#GNUNET_NO)?
+   */
+  uint8_t is_host;
+
+  union {
+    struct Host *host;
+    struct Guest *guest;
+  };
+};
+
+
+/**
+ * Client context for a host.
+ */
+struct Host
+{
+  /**
+   * Place struct common for Host and Guest
+   */
+  struct Place place;
+
+  /**
+   * Handle for the multicast origin.
+   */
+  struct GNUNET_PSYC_Master *master;
+
+  /**
+   * Transmit handle for multicast.
+   */
+  struct GNUNET_PSYC_MasterTransmitHandle *tmit_handle;
+
+  /**
+   * Incoming join requests.
+   * guest_key -> struct GNUNET_PSYC_JoinHandle *
+   */
+  struct GNUNET_CONTAINER_MultiHashMap *join_reqs;
+
+  /**
+   * Messages being relayed.
+   */
+  struct GNUNET_CONTAINER_MultiHashMap *relay_msgs;
+
+  /**
+   * @see enum GNUNET_PSYC_Policy
+   */
+  enum GNUNET_PSYC_Policy policy;
+};
+
+
+/**
+ * Client context for a guest.
+ */
+struct Guest
+{
+  /**
+   * Place struct common for Host and Guest.
+   */
+  struct Place place;
+
+  /**
+   * Handle for the PSYC slave.
+   */
+  struct GNUNET_PSYC_Slave *slave;
+
+  /**
+   * Transmit handle for multicast.
+   */
+  struct GNUNET_PSYC_SlaveTransmitHandle *tmit_handle;
+
+  /**
+   * Peer identity of the origin.
+   */
+  struct GNUNET_PeerIdentity origin;
+
+  /**
+   * Number of items in @a relays.
+   */
+  uint32_t relay_count;
+
+  /**
+   * Relays that multicast can use to connect.
+   */
+  struct GNUNET_PeerIdentity *relays;
+
+  /**
+   * Join request to be transmitted to the master on join.
+   */
+  struct GNUNET_MessageHeader *join_req; // FIXME: not used!
+
+  /**
+   * Join decision received from PSYC.
+   */
+  struct GNUNET_PSYC_JoinDecisionMessage *join_dcsn;
+
+  /**
+   * Join flags for the PSYC service.
+   */
+  enum GNUNET_PSYC_SlaveJoinFlags join_flags;
+};
+
+
+/**
+ * Context for a client.
+ */
+struct Client
+{
+  /**
+   * Client handle.
+   */
+  struct GNUNET_SERVICE_Client *client;
+
+  /**
+   * Place where the client entered.
+   */
+  struct Place *place;
+
+  /**
+   * Message queue for the message currently being transmitted
+   * by this client.
+   */
+  struct MessageTransmitQueue *tmit_msg;
+
+  /**
+   * ID for application clients.
+   */
+  char *app_id;
+};
+
+
+struct Application
+{
+  struct ClientListItem *clients_head;
+  struct ClientListItem *clients_tail;
+};
+
+
+struct Ego {
+  struct GNUNET_CRYPTO_EcdsaPrivateKey key;
+  char *name;
+};
+
+
+struct OperationClosure
+{
+  struct Client *client;
+  uint64_t op_id;
+  uint32_t flags;
+};
+
+
+static int
+psyc_transmit_message (struct Place *plc);
+
+
+/**
+ * Clean up place data structures after a client disconnected.
+ *
+ * @param cls the `struct Place` to clean up
+ */
+static void
+cleanup_place (void *cls);
+
+
+static struct MessageTransmitQueue *
+psyc_transmit_queue_message (struct Place *plc,
+                             struct GNUNET_SERVICE_Client *client,
+                             size_t data_size,
+                             const void *data,
+                             uint16_t first_ptype, uint16_t last_ptype,
+                             struct MessageTransmitQueue *tmit_msg);
+
+
+static int
+place_entry_cleanup (void *cls,
+                     const struct GNUNET_HashCode *key,
+                     void *value)
+{
+  struct Place *plc = value;
+
+  cleanup_place (plc);
+  return GNUNET_YES;
+}
+
+
+/**
+ * Task run during shutdown.
+ *
+ * @param cls unused
+ */
+static void
+shutdown_task (void *cls)
+{
+  GNUNET_CONTAINER_multihashmap_iterate (hosts, place_entry_cleanup, NULL);
+  GNUNET_CONTAINER_multihashmap_iterate (guests, place_entry_cleanup, NULL);
+
+  if (NULL != id)
+  {
+    GNUNET_IDENTITY_disconnect (id);
+    id = NULL;
+  }
+  if (NULL != namestore)
+  {
+    GNUNET_NAMESTORE_disconnect (namestore);
+    namestore = NULL;
+  }
+  if (NULL != gns)
+  {
+    GNUNET_GNS_disconnect (gns);
+    gns = NULL;
+  }
+  if (NULL != stats)
+  {
+    GNUNET_STATISTICS_destroy (stats, GNUNET_YES);
+    stats = NULL;
+  }
+}
+
+
+/**
+ * Clean up host data structures after a client disconnected.
+ */
+static void
+cleanup_host (struct Host *hst)
+{
+  struct Place *plc = &hst->place;
+
+  GNUNET_CONTAINER_multihashmap_destroy (hst->join_reqs);
+  GNUNET_CONTAINER_multihashmap_destroy (hst->relay_msgs);
+  GNUNET_CONTAINER_multihashmap_remove (hosts, &plc->pub_key_hash, plc);
+}
+
+
+/**
+ * Clean up guest data structures after a client disconnected.
+ */
+static void
+cleanup_guest (struct Guest *gst)
+{
+  struct Place *plc = &gst->place;
+  struct GNUNET_CONTAINER_MultiHashMap *
+    plc_gst = GNUNET_CONTAINER_multihashmap_get (place_guests,
+                                                 &plc->pub_key_hash);
+  if (NULL != plc_gst)
+  {
+    GNUNET_CONTAINER_multihashmap_remove (plc_gst, &plc->ego_pub_hash, gst);
+
+    if (0 == GNUNET_CONTAINER_multihashmap_size (plc_gst))
+    {
+      GNUNET_CONTAINER_multihashmap_remove (place_guests, &plc->pub_key_hash,
+                                            plc_gst);
+      GNUNET_CONTAINER_multihashmap_destroy (plc_gst);
+    }
+  }
+  GNUNET_CONTAINER_multihashmap_remove (guests, &plc->pub_key_hash, gst);
+  if (NULL != gst->join_req)
+    GNUNET_free (gst->join_req);
+  if (NULL != gst->relays)
+    GNUNET_free (gst->relays);
+  GNUNET_CONTAINER_multihashmap_remove (guests, &plc->pub_key_hash, plc);
+}
+
+
+/**
+ * Clean up place data structures after a client disconnected.
+ *
+ * @param cls the `struct Place` to clean up
+ */
+static void
+cleanup_place (void *cls)
+{
+  struct Place *plc = cls;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "cleaning up place %s\n",
+              GNUNET_h2s (&plc->pub_key_hash));
+
+  (GNUNET_YES == plc->is_host)
+    ? cleanup_host ((struct Host *) plc)
+    : cleanup_guest ((struct Guest *) plc);
+
+  GNUNET_PSYC_slicer_destroy (plc->slicer);
+  GNUNET_free (plc);
+}
+
+
+/**
+ * Called whenever a client is disconnected.
+ * Frees our resources associated with that client.
+ *
+ * @param cls closure
+ * @param client identification of the client
+ * @param app_ctx must match @a client
+ */
+static void
+client_notify_disconnect (void *cls,
+                          struct GNUNET_SERVICE_Client *client,
+                          void *app_ctx)
+{
+  struct Client *c = app_ctx;
+  struct Place *plc = c->place;
+
+  if (NULL != c->app_id)
+    GNUNET_free (c->app_id);
+
+  GNUNET_free (c);
+
+  if (NULL == plc)
+    return; // application client, nothing to do
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%p Client (%s) disconnected from place %s\n",
+              plc, (GNUNET_YES == plc->is_host) ? "host" : "guest",
+              GNUNET_h2s (&plc->pub_key_hash));
+
+  struct ClientListItem *cli = plc->clients_head;
+  while (NULL != cli)
+  {
+    if (cli->client == client)
+    {
+      GNUNET_CONTAINER_DLL_remove (plc->clients_head,
+                                   plc->clients_tail,
+                                   cli);
+      GNUNET_free (cli);
+      break;
+    }
+    cli = cli->next;
+  }
+  if (GNUNET_YES == plc->is_disconnecting)
+  {
+    GNUNET_PSYC_slicer_destroy (plc->slicer);
+    GNUNET_free (plc);
+  }
+}
+
+
+/**
+ * A new client connected.
+ *
+ * @param cls NULL
+ * @param client client to add
+ * @param mq message queue for @a client
+ * @return @a client
+ */
+static void *
+client_notify_connect (void *cls,
+                       struct GNUNET_SERVICE_Client *client,
+                       struct GNUNET_MQ_Handle *mq)
+{
+  struct Client *c = GNUNET_new (struct Client);
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Client %p connected with queue %p\n",
+              client,
+              mq);
+  c->client = client;
+  return c;
+}
+
+
+/**
+ * Send message to all clients connected to a place and
+ * takes care of freeing @env.
+ */
+static void
+place_send_msg (const struct Place *plc,
+                struct GNUNET_MQ_Envelope *env)
+{
+  struct ClientListItem *cli = plc->clients_head;
+  
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%p Sending message to clients of place.\n", plc);
+  while (NULL != cli)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "Sending message to client %p\n",
+                cli);
+    GNUNET_MQ_send_copy (GNUNET_SERVICE_client_get_mq (cli->client),
+                         env);
+    cli = cli->next;
+  }
+  GNUNET_MQ_discard (env);
+}
+
+
+static void
+place_send_leave_ack (struct Place *plc)
+{
+  struct GNUNET_MQ_Envelope *env;
+  
+  for (struct ClientListItem *cli = plc->clients_head;
+       NULL != cli;
+       cli = cli->next)
+  {
+    env = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_SOCIAL_PLACE_LEAVE_ACK);
+    GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (cli->client),
+                    env); 
+  }
+}
+
+
+/**
+ * Send a result code back to the client.
+ *
+ * @param client
+ *        Client that should receive the result code.
+ * @param result_code
+ *        Code to transmit.
+ * @param op_id
+ *        Operation ID in network byte order.
+ * @param data
+ *        Data payload or NULL.
+ * @param data_size
+ *        Size of @a data.
+ */
+static void
+client_send_result (struct GNUNET_SERVICE_Client *client, uint64_t op_id,
+                    int64_t result_code, const void *data, uint16_t data_size)
+{
+  struct GNUNET_MQ_Envelope *env;
+  struct GNUNET_OperationResultMessage *res;
+
+  env = GNUNET_MQ_msg_extra (res,
+                             data_size,
+                             GNUNET_MESSAGE_TYPE_PSYC_RESULT_CODE);
+  res->result_code = GNUNET_htonll (result_code);
+  res->op_id = op_id;
+  if (0 < data_size)
+    GNUNET_memcpy (&res[1], data, data_size);
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+             "%p Sending result to client for operation #%" PRIu64 ": "
+              "%" PRId64 " (size: %u)\n",
+             client, GNUNET_ntohll (op_id), result_code, data_size);
+  GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client), env);
+}
+
+
+static void
+client_send_host_enter_ack (struct GNUNET_SERVICE_Client *client,
+                            struct Host *hst, uint32_t result)
+{
+  struct GNUNET_MQ_Envelope *env;
+  struct HostEnterAck *hack;
+  struct Place *plc = &hst->place;
+
+  env = GNUNET_MQ_msg (hack,
+                       GNUNET_MESSAGE_TYPE_SOCIAL_HOST_ENTER_ACK); 
+  hack->result_code = htonl (result);
+  hack->max_message_id = GNUNET_htonll (plc->max_message_id);
+  hack->place_pub_key = plc->pub_key;
+
+  if (NULL != client)
+    GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client),
+                    env);
+  else
+    place_send_msg (plc, env);
+}
+
+
+/**
+ * Called after a PSYC master is started.
+ */
+static void
+psyc_master_started (void *cls, int result, uint64_t max_message_id)
+{
+  struct Host *hst = cls;
+  struct Place *plc = &hst->place;
+  plc->max_message_id = max_message_id;
+  plc->is_ready = GNUNET_YES;
+
+  client_send_host_enter_ack (NULL, hst, result);
+}
+
+
+/**
+ * Called when a PSYC master receives a join request.
+ */
+static void
+psyc_recv_join_request (void *cls,
+                        const struct GNUNET_PSYC_JoinRequestMessage *req,
+                        const struct GNUNET_CRYPTO_EcdsaPublicKey *slave_key,
+                        const struct GNUNET_PSYC_Message *join_msg,
+                        struct GNUNET_PSYC_JoinHandle *jh)
+{
+  struct Host *hst = cls;
+  struct GNUNET_HashCode slave_key_hash;
+  GNUNET_CRYPTO_hash (slave_key, sizeof (*slave_key), &slave_key_hash);
+  GNUNET_CONTAINER_multihashmap_put (hst->join_reqs, &slave_key_hash, jh,
+                                     
GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
+  place_send_msg (&hst->place,
+                  GNUNET_MQ_msg_copy (&req->header));
+}
+
+
+/**
+ * Called after a PSYC slave is connected.
+ */
+static void
+psyc_slave_connected (void *cls, int result, uint64_t max_message_id)
+{
+  struct GNUNET_PSYC_CountersResultMessage *res;
+  struct GNUNET_MQ_Envelope *env;
+  struct Guest *gst = cls;
+  struct Place *plc = &gst->place;
+
+  plc->max_message_id = max_message_id;
+  plc->is_ready = GNUNET_YES;
+  env = GNUNET_MQ_msg (res,
+                       GNUNET_MESSAGE_TYPE_SOCIAL_GUEST_ENTER_ACK);
+  res->result_code =
+    (result != GNUNET_SYSERR) ? htonl (GNUNET_OK) : htonl (GNUNET_SYSERR);
+  res->max_message_id = GNUNET_htonll (plc->max_message_id);
+  place_send_msg (plc, env);
+}
+
+
+static void
+slave_parted_after_join_decision (void *cls)
+{
+  struct Guest *gst = cls;
+
+  GNUNET_assert (NULL != gst->join_dcsn);
+  place_send_msg (&gst->place, GNUNET_MQ_msg_copy (&gst->join_dcsn->header));
+}
+
+
+/**
+ * Called when a PSYC slave receives a join decision.
+ */
+static void
+psyc_recv_join_dcsn (void *cls,
+                     const struct GNUNET_PSYC_JoinDecisionMessage *dcsn,
+                     int is_admitted,
+                     const struct GNUNET_PSYC_Message *join_msg)
+{
+  struct Guest *gst = cls;
+
+  gst->join_dcsn = GNUNET_malloc (dcsn->header.size);
+  GNUNET_memcpy (gst->join_dcsn,
+                 dcsn,
+                 dcsn->header.size);
+  if (GNUNET_NO == is_admitted)
+  {
+    GNUNET_PSYC_slave_part (gst->slave,
+                            GNUNET_NO,
+                            &slave_parted_after_join_decision,
+                            gst);
+    gst->slave = NULL;
+    return;
+  }
+  place_send_msg (&gst->place, GNUNET_MQ_msg_copy (&gst->join_dcsn->header));
+}
+
+
+/**
+ * Called when a PSYC master or slave receives a message.
+ */
+static void
+psyc_recv_message (void *cls,
+                   const struct GNUNET_PSYC_MessageHeader *msg)
+{
+  struct Place *plc = cls;
+
+  char *str = GNUNET_CRYPTO_ecdsa_public_key_to_string (&msg->slave_pub_key);
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%p Received PSYC message of size %u from %s.\n",
+              plc, ntohs (msg->header.size), str);
+  GNUNET_free (str);
+
+  GNUNET_PSYC_slicer_message (plc->slicer, msg);
+
+  place_send_msg (plc, GNUNET_MQ_msg_copy (&msg->header));
+}
+
+
+/**
+ * Relay a message part received from a guest to the the place.
+ *
+ * @param hst
+ *        Host.
+ * @param pmsg
+ *        Message part.
+ * @param nym_pub_key
+ *        Nym the message is received from.
+ */
+static void
+host_relay_message_part (struct Host *hst,
+                         const struct GNUNET_MessageHeader *pmsg,
+                         const struct GNUNET_CRYPTO_EcdsaPublicKey 
*nym_pub_key)
+{
+  /* separate queue per nym */
+  struct GNUNET_HashCode nym_pub_hash;
+  GNUNET_CRYPTO_hash (nym_pub_key, sizeof (*nym_pub_key), &nym_pub_hash);
+
+  struct MessageTransmitQueue *
+    tmit_msg = GNUNET_CONTAINER_multihashmap_get (hst->relay_msgs, 
&nym_pub_hash);
+
+  uint16_t ptype = ntohs (pmsg->type);
+
+  if (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_METHOD == ptype)
+  {
+    /* FIXME: last message was unfinished, cancel & remove from queue */
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+               "FIXME: last message was unfinished.\n");
+  }
+
+  tmit_msg = psyc_transmit_queue_message (&hst->place, NULL, ntohs 
(pmsg->size),
+                                          pmsg, ptype, ptype, tmit_msg);
+
+  switch (ptype)
+  {
+  case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_METHOD:
+    GNUNET_assert (GNUNET_YES == GNUNET_CONTAINER_multihashmap_put
+                                     (hst->relay_msgs, &nym_pub_hash, tmit_msg,
+                                       
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
+    break;
+  case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END:
+  case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL:
+    GNUNET_assert (GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove
+                                    (hst->relay_msgs, &nym_pub_hash, 
tmit_msg));
+    break;
+  }
+}
+
+
+/**
+ * Received a method to be relayed from a guest.
+ */
+static void
+place_recv_relay_method (void *cls,
+                         const struct GNUNET_PSYC_MessageHeader *msg,
+                         const struct GNUNET_PSYC_MessageMethod *meth,
+                         uint64_t message_id,
+                         const char *method_name)
+{
+  struct Place *plc = cls;
+
+  if (GNUNET_PSYC_MESSAGE_REQUEST & ntohs (msg->flags)
+      && GNUNET_YES == plc->is_host)
+  {
+    struct Host *hst = cls;
+    host_relay_message_part (hst, &meth->header, &msg->slave_pub_key);
+  }
+}
+
+
+/**
+ * Received a modifier to be relayed from a guest.
+ */
+static void
+place_recv_relay_modifier (void *cls,
+                           const struct GNUNET_PSYC_MessageHeader *msg,
+                           const struct GNUNET_MessageHeader *pmsg,
+                           uint64_t message_id,
+                           enum GNUNET_PSYC_Operator oper,
+                           const char *name,
+                           const void *value,
+                           uint16_t value_size,
+                           uint16_t full_value_size)
+{
+  struct Place *plc = cls;
+
+  if (GNUNET_PSYC_MESSAGE_REQUEST & ntohs (msg->flags)
+      && GNUNET_YES == plc->is_host)
+  {
+    struct Host *hst = cls;
+    host_relay_message_part (hst, pmsg, &msg->slave_pub_key);
+  }
+}
+
+/**
+ * Received a data fragment to be relayed from a guest.
+ */
+static void
+place_recv_relay_data (void *cls,
+                       const struct GNUNET_PSYC_MessageHeader *msg,
+                       const struct GNUNET_MessageHeader *pmsg,
+                       uint64_t message_id,
+                       const void *data,
+                       uint16_t data_size)
+{
+  struct Place *plc = cls;
+
+  if (GNUNET_PSYC_MESSAGE_REQUEST & ntohs (msg->flags)
+      && GNUNET_YES == plc->is_host)
+  {
+    struct Host *hst = cls;
+    host_relay_message_part (hst, pmsg, &msg->slave_pub_key);
+  }
+}
+
+
+/**
+ * Received end of message to be relayed from a guest.
+ */
+static void
+place_recv_relay_eom (void *cls,
+                      const struct GNUNET_PSYC_MessageHeader *msg,
+                      const struct GNUNET_MessageHeader *pmsg,
+                      uint64_t message_id,
+                      uint8_t is_cancelled)
+{
+  struct Place *plc = cls;
+
+  if (GNUNET_PSYC_MESSAGE_REQUEST & ntohs (msg->flags)
+      && GNUNET_YES == plc->is_host)
+  {
+    struct Host *hst = cls;
+    host_relay_message_part (hst, pmsg, &msg->slave_pub_key);
+  }
+}
+
+
+/**
+ * Received a method to be saved to disk.
+ *
+ * Create a new file for writing the data part of the message into,
+ * if the file does not yet exist.
+ */
+static void
+place_recv_save_method (void *cls,
+                        const struct GNUNET_PSYC_MessageHeader *msg,
+                        const struct GNUNET_PSYC_MessageMethod *meth,
+                        uint64_t message_id,
+                        const char *method_name)
+{
+  struct Place *plc = cls;
+  plc->file_offset = 0;
+  plc->file_save = GNUNET_NO;
+
+  char *place_pub_str = GNUNET_CRYPTO_eddsa_public_key_to_string 
(&plc->pub_key);
+  char *filename = NULL;
+  GNUNET_asprintf (&filename, "%s%c" "%s%c" "%s%c" "%" PRIu64 ".part",
+                   dir_social, DIR_SEPARATOR,
+                   "files", DIR_SEPARATOR,
+                   place_pub_str, DIR_SEPARATOR,
+                   GNUNET_ntohll (msg->message_id));
+  GNUNET_free (place_pub_str);
+
+  /* save if does not already exist */
+  if (GNUNET_YES != GNUNET_DISK_file_test (filename))
+  {
+    if (0 == GNUNET_DISK_fn_write (filename, NULL, 0,
+                                   GNUNET_DISK_PERM_USER_READ
+                                   | GNUNET_DISK_PERM_USER_WRITE))
+    {
+      plc->file_save = GNUNET_YES;
+    }
+    else
+    {
+      GNUNET_break (0);
+    }
+  }
+  GNUNET_free (filename);
+}
+
+
+/**
+ * Received a data fragment to be saved to disk.
+ *
+ * Append data fragment to the file.
+ */
+static void
+place_recv_save_data (void *cls,
+                      const struct GNUNET_PSYC_MessageHeader *msg,
+                      const struct GNUNET_MessageHeader *pmsg,
+                      uint64_t message_id,
+                      const void *data,
+                      uint16_t data_size)
+{
+  struct Place *plc = cls;
+  if (GNUNET_YES != plc->file_save)
+    return;
+
+  char *place_pub_str = GNUNET_CRYPTO_eddsa_public_key_to_string 
(&plc->pub_key);
+  char *filename = NULL;
+  GNUNET_asprintf (&filename, "%s%c" "%s%c" "%s%c" "%" PRIu64 ".part",
+                   dir_social, DIR_SEPARATOR,
+                   "files", DIR_SEPARATOR,
+                   place_pub_str, DIR_SEPARATOR,
+                   GNUNET_ntohll (msg->message_id));
+  GNUNET_free (place_pub_str);
+  if (GNUNET_SYSERR == GNUNET_DISK_directory_create_for_file (filename))
+  {
+    GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "create", filename);
+    GNUNET_free (filename);
+    return;
+  }
+
+  struct GNUNET_DISK_FileHandle *
+    fh = GNUNET_DISK_file_open (filename, GNUNET_DISK_OPEN_WRITE,
+                                GNUNET_DISK_PERM_NONE);
+  if (NULL != fh)
+  {
+    if (plc->file_offset != GNUNET_DISK_file_seek
+                           (fh, plc->file_offset, GNUNET_DISK_SEEK_SET)) {
+      GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "seek", filename);
+      GNUNET_DISK_file_close (fh);
+      GNUNET_free (filename);
+      return;
+    }
+    GNUNET_DISK_file_write (fh, data, data_size);
+    GNUNET_DISK_file_close (fh);
+    GNUNET_free (filename);
+  }
+  else
+  {
+    GNUNET_free (filename);
+    GNUNET_break (0);
+  }
+  plc->file_offset += data_size;
+}
+
+
+/**
+ * Received end of message to be saved to disk.
+ *
+ * Remove .part ending from the filename.
+ */
+static void
+place_recv_save_eom (void *cls,
+                     const struct GNUNET_PSYC_MessageHeader *msg,
+                     const struct GNUNET_MessageHeader *pmsg,
+                     uint64_t message_id,
+                     uint8_t is_cancelled)
+{
+  struct Place *plc = cls;
+  if (GNUNET_YES != plc->file_save)
+    return;
+
+  char *place_pub_str = GNUNET_CRYPTO_eddsa_public_key_to_string 
(&plc->pub_key);
+  char *fn = NULL;
+  GNUNET_asprintf (&fn, "%s%c%s%c%s%c%" PRIu64,
+                   dir_social, DIR_SEPARATOR,
+                   "files", DIR_SEPARATOR,
+                   place_pub_str, DIR_SEPARATOR,
+                   GNUNET_ntohll (msg->message_id));
+  GNUNET_free (place_pub_str);
+  char *fn_part = NULL;
+  GNUNET_asprintf (&fn_part, "%s.part", fn);
+
+  if (rename (fn_part, fn)) {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                 "Failed to rename %s into %s: %s (%d)\n",
+                 fn_part, fn, strerror (errno), errno);
+  }
+
+  GNUNET_free (fn);
+  GNUNET_free (fn_part);
+}
+
+
+/**
+ * Initialize place data structure.
+ */
+static void
+place_init (struct Place *plc)
+{
+  plc->slicer = GNUNET_PSYC_slicer_create ();
+}
+
+
+/**
+ * Add a place to the @e places hash map.
+ *
+ * @param ereq
+ *        Entry request.
+ *
+ * @return #GNUNET_OK if the place was added
+ *         #GNUNET_NO if the place already exists in the hash map
+ *         #GNUNET_SYSERR on error
+ */
+static int
+place_add (const struct PlaceEnterRequest *ereq)
+{
+  struct EgoPlacePublicKey ego_place_pub_key = {
+    .ego_pub_key = ereq->ego_pub_key,
+    .place_pub_key = ereq->place_pub_key,
+  };
+  struct GNUNET_HashCode ego_place_pub_hash;
+  GNUNET_CRYPTO_hash (&ego_place_pub_key, sizeof (ego_place_pub_key), 
&ego_place_pub_hash);
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "  ego_place_pub_hash = %s\n", GNUNET_h2s (&ego_place_pub_hash));
+
+  struct GNUNET_MessageHeader *
+    place_msg = GNUNET_CONTAINER_multihashmap_get (places, 
&ego_place_pub_hash);
+  if (NULL != place_msg)
+    return GNUNET_NO;
+
+  place_msg = GNUNET_copy_message (&ereq->header);
+  if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put (places, 
&ego_place_pub_hash, place_msg,
+                                                      
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
+  {
+    GNUNET_break (0);
+    GNUNET_free (place_msg);
+    return GNUNET_SYSERR;
+  }
+
+  return GNUNET_OK;
+}
+
+/**
+ * Add a place to the @e app_places hash map.
+ *
+ * @param app_id
+ *        Application ID.
+ * @param ereq
+ *        Entry request.
+ *
+ * @return #GNUNET_OK if the place was added
+ *         #GNUNET_NO if the place already exists in the hash map
+ *         #GNUNET_SYSERR on error
+ */
+static int
+app_place_add (const char *app_id,
+               const struct PlaceEnterRequest *ereq)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Adding app place to hashmap:\n");
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "  app_id = %s\n", app_id);
+
+  struct GNUNET_HashCode app_id_hash;
+  GNUNET_CRYPTO_hash (app_id, strlen (app_id) + 1, &app_id_hash);
+
+  struct EgoPlacePublicKey ego_place_pub_key = {
+    .ego_pub_key = ereq->ego_pub_key,
+    .place_pub_key = ereq->place_pub_key,
+  };
+  struct GNUNET_HashCode ego_place_pub_hash;
+  GNUNET_CRYPTO_hash (&ego_place_pub_key, sizeof (ego_place_pub_key), 
&ego_place_pub_hash);
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "  ego_place_pub_hash = %s\n", GNUNET_h2s (&ego_place_pub_hash));
+
+  struct GNUNET_CONTAINER_MultiHashMap *
+    app_places = GNUNET_CONTAINER_multihashmap_get (apps_places, &app_id_hash);
+  if (NULL == app_places)
+  {
+    app_places = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO);
+    GNUNET_CONTAINER_multihashmap_put (apps_places, &app_id_hash, app_places,
+                                       
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
+  }
+
+  if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (app_places, 
&ego_place_pub_hash))
+    return GNUNET_NO;
+
+  if (GNUNET_SYSERR == place_add (ereq))
+  {
+    return GNUNET_SYSERR;
+  }
+
+  if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put (app_places, 
&ego_place_pub_hash, NULL,
+                                                      
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
+  {
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+  return GNUNET_OK;
+}
+
+
+/**
+ * Save place entry message to disk.
+ *
+ * @param app_id
+ *        Application ID.
+ * @param ereq
+ *        Entry request message.
+ */
+static int
+app_place_save (const char *app_id,
+                const struct PlaceEnterRequest *ereq)
+{
+  if (GNUNET_SYSERR == app_place_add (app_id, ereq))
+  {
+    GNUNET_assert (0);
+  }
+
+  if (NULL == dir_places)
+    return GNUNET_SYSERR;
+
+  char *ego_pub_str = GNUNET_CRYPTO_ecdsa_public_key_to_string 
(&ereq->ego_pub_key);
+  char *place_pub_str = GNUNET_CRYPTO_eddsa_public_key_to_string 
(&ereq->place_pub_key);
+  char *filename = NULL;
+  GNUNET_asprintf (&filename, "%s%c" "%s%c" "%s%c" "%s",
+                   dir_social, DIR_SEPARATOR,
+                   "places", DIR_SEPARATOR,
+                   ego_pub_str, DIR_SEPARATOR,
+                   place_pub_str);
+  int ret = GNUNET_DISK_directory_create_for_file (filename);
+  if (GNUNET_OK != ret
+      || 0 > GNUNET_DISK_fn_write (filename, ereq, ntohs (ereq->header.size),
+                                   GNUNET_DISK_PERM_USER_READ
+                                   | GNUNET_DISK_PERM_USER_WRITE))
+  {
+    GNUNET_break (0);
+    ret = GNUNET_SYSERR;
+  }
+  GNUNET_free (filename);
+
+  if (ret == GNUNET_OK)
+  {
+    GNUNET_asprintf (&filename, "%s%c" "%s%c" "%s%c" "%s%c" "%s",
+                     dir_social, DIR_SEPARATOR,
+                     "apps", DIR_SEPARATOR,
+                     app_id, DIR_SEPARATOR,
+                     ego_pub_str, DIR_SEPARATOR,
+                     place_pub_str);
+    ret = GNUNET_DISK_directory_create_for_file (filename);
+    if (GNUNET_OK != ret
+        || 0 > GNUNET_DISK_fn_write (filename, "", 0,
+                                     GNUNET_DISK_PERM_USER_READ
+                                     | GNUNET_DISK_PERM_USER_WRITE))
+    {
+      GNUNET_break (0);
+      ret = GNUNET_SYSERR;
+    }
+    GNUNET_free (filename);
+  }
+  GNUNET_free (ego_pub_str);
+  GNUNET_free (place_pub_str);
+  return ret;
+}
+
+
+int
+app_place_remove (const char *app_id,
+                  const struct GNUNET_CRYPTO_EcdsaPublicKey *ego_pub_key,
+                  const struct GNUNET_CRYPTO_EddsaPublicKey *place_pub_key)
+{
+  struct GNUNET_HashCode ego_pub_hash;
+  struct GNUNET_HashCode place_pub_hash;
+  GNUNET_CRYPTO_hash (ego_pub_key, sizeof (*ego_pub_key), &ego_pub_hash);
+  GNUNET_CRYPTO_hash (place_pub_key, sizeof (*place_pub_key), &place_pub_hash);
+
+  char *ego_pub_str = GNUNET_CRYPTO_ecdsa_public_key_to_string (ego_pub_key);
+  char *place_pub_str = GNUNET_CRYPTO_eddsa_public_key_to_string 
(place_pub_key);
+  char *app_place_filename = NULL;
+  GNUNET_asprintf (&app_place_filename,
+                   "%s%c" "%s%c" "%s%c" "%s%c" "%s",
+                   dir_social, DIR_SEPARATOR,
+                   "apps", DIR_SEPARATOR,
+                   app_id, DIR_SEPARATOR,
+                   ego_pub_str, DIR_SEPARATOR,
+                   place_pub_str);
+  GNUNET_free (ego_pub_str);
+  GNUNET_free (place_pub_str);
+
+  struct GNUNET_HashCode app_id_hash;
+  GNUNET_CRYPTO_hash (app_id, strlen (app_id) + 1, &app_id_hash);
+
+  struct GNUNET_CONTAINER_MultiHashMap *
+    app_places = GNUNET_CONTAINER_multihashmap_get (apps_places, &app_id_hash);
+
+  if (NULL != app_places)
+    GNUNET_CONTAINER_multihashmap_remove (app_places, &place_pub_hash, NULL);
+
+  int ret = GNUNET_OK;
+
+  if (0 != unlink (app_place_filename))
+  {
+    GNUNET_break (0);
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Error removing app place file: %s: %s (%d)\n",
+                app_place_filename, strerror (errno), errno);
+    ret = GNUNET_SYSERR;
+  }
+  GNUNET_free (app_place_filename);
+
+  return ret;
+}
+
+
+/**
+ * Enter place as host.
+ *
+ * @param hreq
+ *        Host entry request.
+ * @param[out] ret_hst
+ *        Returned Host struct.
+ *
+ * @return #GNUNET_YES if the host entered the place just now,
+ *         #GNUNET_NO  if the place is already entered,
+ *         #GNUNET_SYSERR if place_pub_key was set
+ *                        but its private key was not found
+ */
+static int
+host_enter (const struct HostEnterRequest *hreq, struct Host **ret_hst)
+{
+  int ret = GNUNET_NO;
+  struct GNUNET_HashCode place_pub_hash;
+  GNUNET_CRYPTO_hash (&hreq->place_pub_key, sizeof (hreq->place_pub_key),
+                      &place_pub_hash);
+  struct Host *hst = GNUNET_CONTAINER_multihashmap_get (hosts, 
&place_pub_hash);
+
+  if (NULL == hst)
+  {
+    hst = GNUNET_new (struct Host);
+    hst->policy = hreq->policy;
+    hst->join_reqs = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO);
+    hst->relay_msgs = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO);
+
+    struct Place *plc = &hst->place;
+    place_init (plc);
+    plc->is_host = GNUNET_YES;
+    plc->pub_key = hreq->place_pub_key;
+    plc->pub_key_hash = place_pub_hash;
+
+    GNUNET_CONTAINER_multihashmap_put (hosts, &plc->pub_key_hash, plc,
+                                       
GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
+    hst->master = GNUNET_PSYC_master_start (cfg, &hreq->place_key, hst->policy,
+                                            &psyc_master_started,
+                                            &psyc_recv_join_request,
+                                            &psyc_recv_message, NULL, hst);
+    plc->channel = GNUNET_PSYC_master_get_channel (hst->master);
+    ret = GNUNET_YES;
+  }
+
+  if (NULL != ret_hst)
+    *ret_hst = hst;
+  return ret;
+}
+
+
+static int
+msg_proc_parse (const struct MsgProcRequest *mpreq,
+                uint32_t *flags,
+                const char **method_prefix,
+                struct GNUNET_HashCode *method_hash)
+{
+  ssize_t method_size = ntohs (mpreq->header.size) - sizeof (*mpreq);
+  uint16_t offset;
+
+  if (method_size < 0)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "MsgProcRequest has invalid size\n");
+    return GNUNET_SYSERR;
+  }
+
+  offset = GNUNET_STRINGS_buffer_tokenize ((const char *) &mpreq[1],
+                                           method_size,
+                                           1,
+                                           method_prefix);
+  if (0 == offset || offset != method_size || *method_prefix == NULL)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "MsgProcRequest contains invalid method\n");
+    return GNUNET_SYSERR;
+  }
+  GNUNET_CRYPTO_hash (*method_prefix, (size_t) method_size, method_hash);
+  *flags = ntohl (mpreq->flags);
+  return GNUNET_OK;
+}
+
+
+void
+app_notify_place (const struct GNUNET_MessageHeader *msg,
+                  struct GNUNET_SERVICE_Client *client)
+{
+  struct AppPlaceMessage *amsg;
+  struct GNUNET_MQ_Envelope *env;
+  uint16_t msg_size = ntohs (msg->size);
+  
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%p Sending place notification of type %u to client.\n",
+              client, ntohs (msg->type));
+  switch (ntohs (msg->type))
+  {
+  case GNUNET_MESSAGE_TYPE_SOCIAL_HOST_ENTER:
+  {
+    struct HostEnterRequest *hreq = (struct HostEnterRequest *) msg;
+    if (msg_size < sizeof (struct HostEnterRequest))
+      return;
+    env = GNUNET_MQ_msg (amsg,
+                         GNUNET_MESSAGE_TYPE_SOCIAL_APP_PLACE);
+    // FIXME: also notify about not entered places
+    amsg->place_state = GNUNET_SOCIAL_PLACE_STATE_ENTERED;
+    amsg->is_host = GNUNET_YES;
+    amsg->ego_pub_key = hreq->ego_pub_key;
+    amsg->place_pub_key = hreq->place_pub_key;
+    GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client),
+                    env);
+    break;
+  }
+  case GNUNET_MESSAGE_TYPE_SOCIAL_GUEST_ENTER:
+  {
+    if (msg_size < sizeof (struct GuestEnterRequest))
+      return;
+    struct GuestEnterRequest *greq = (struct GuestEnterRequest *) msg;
+    env = GNUNET_MQ_msg (amsg,
+                         GNUNET_MESSAGE_TYPE_SOCIAL_APP_PLACE);
+    // FIXME: also notify about not entered places
+    amsg->place_state = GNUNET_SOCIAL_PLACE_STATE_ENTERED;
+    amsg->is_host = GNUNET_NO;
+    amsg->ego_pub_key = greq->ego_pub_key;
+    amsg->place_pub_key = greq->place_pub_key;
+    GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client),
+                    env);
+    break;
+  }
+  default:
+    return;
+  }
+}
+
+
+void
+app_notify_place_end (struct GNUNET_SERVICE_Client *client)
+{
+  struct GNUNET_MQ_Envelope *env;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%p Sending end of place list notification to client\n",
+              client);
+  env = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_SOCIAL_APP_PLACE_END);
+  GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client),
+                  env);
+}
+
+
+void
+app_notify_ego (struct Ego *ego, struct GNUNET_SERVICE_Client *client)
+{
+  struct AppEgoMessage *emsg;
+  struct GNUNET_MQ_Envelope *env;
+  size_t name_size = strlen (ego->name) + 1;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%p Sending ego notification to client: %s\n",
+              client, ego->name);
+  env = GNUNET_MQ_msg_extra (emsg,
+                             name_size,
+                             GNUNET_MESSAGE_TYPE_SOCIAL_APP_EGO);
+  GNUNET_CRYPTO_ecdsa_key_get_public (&ego->key, &emsg->ego_pub_key);
+  GNUNET_memcpy (&emsg[1], ego->name, name_size);
+  GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client),
+                  env);
+}
+
+
+void
+app_notify_ego_end (struct GNUNET_SERVICE_Client *client)
+{
+  struct GNUNET_MQ_Envelope *env;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%p Sending end of ego list notification to client\n",
+              client);
+  env = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_SOCIAL_APP_EGO_END);
+  GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client),
+                  env);
+}
+
+
+int
+app_place_entry_notify (void *cls, const struct GNUNET_HashCode *key, void 
*value)
+{
+  struct GNUNET_MessageHeader *
+    msg = GNUNET_CONTAINER_multihashmap_get (places, key);
+  if (NULL != msg)
+    app_notify_place (msg, cls);
+  return GNUNET_YES;
+}
+
+
+int
+ego_entry (void *cls, const struct GNUNET_HashCode *key, void *value)
+{
+  app_notify_ego (value, cls);
+  return GNUNET_YES;
+}
+
+
+static int
+check_client_msg_proc_set (void *cls,
+                           const struct MsgProcRequest *mpreq)
+{
+  return GNUNET_OK;
+}
+
+
+/**
+ * Handle a client setting message proccesing flags for a method prefix.
+ */
+static void
+handle_client_msg_proc_set (void *cls,
+                            const struct MsgProcRequest *mpreq)
+{
+  struct Client *c = cls;
+  struct GNUNET_SERVICE_Client *client = c->client;
+  struct Place *plc = c->place;
+  if (NULL == plc)
+  {
+    GNUNET_break (0);
+    GNUNET_SERVICE_client_drop (client);
+    return;
+  }
+
+  const char *method_prefix = NULL;
+  uint32_t flags = 0;
+  struct GNUNET_HashCode method_hash;
+
+  if (GNUNET_OK !=
+      msg_proc_parse (mpreq, &flags, &method_prefix, &method_hash))
+  {
+    GNUNET_break (0);
+    GNUNET_SERVICE_client_drop (client);
+    return;
+  }
+#if 0
+  GNUNET_PSYC_slicer_method_remove (plc->slicer, method_prefix,
+                                    place_recv_relay_method,
+                                    place_recv_relay_modifier,
+                                    place_recv_relay_data,
+                                    place_recv_relay_eom);
+  GNUNET_PSYC_slicer_method_remove (plc->slicer, method_prefix,
+                                    place_recv_save_method,
+                                    NULL,
+                                    place_recv_save_data,
+                                    place_recv_save_eom);
+#endif
+  if (flags & GNUNET_SOCIAL_MSG_PROC_RELAY)
+  {
+    GNUNET_PSYC_slicer_method_add (plc->slicer, method_prefix, NULL,
+                                   place_recv_relay_method,
+                                   place_recv_relay_modifier,
+                                   place_recv_relay_data,
+                                   place_recv_relay_eom,
+                                   plc);
+  }
+  if (flags & GNUNET_SOCIAL_MSG_PROC_SAVE)
+  {
+    GNUNET_PSYC_slicer_method_add (plc->slicer, method_prefix, NULL,
+                                   place_recv_save_method,
+                                   NULL,
+                                   place_recv_save_data,
+                                   place_recv_save_eom,
+                                   plc);
+  }
+
+  /** @todo Save flags to be able to resume relaying/saving after restart */
+
+  GNUNET_SERVICE_client_continue (client);
+}
+
+
+/**
+ * Handle a connecting client requesting to clear all relay rules.
+ */
+static void
+handle_client_msg_proc_clear (void *cls,
+                              const struct GNUNET_MessageHeader *msg)
+{
+  struct Client *c = cls;
+  struct GNUNET_SERVICE_Client *client = c->client;
+  struct Place *plc = c->place;
+  if (NULL == plc)
+  {
+    GNUNET_break (0);
+    GNUNET_SERVICE_client_drop (client);
+    return;
+  }
+
+  GNUNET_PSYC_slicer_clear (plc->slicer);
+
+  GNUNET_SERVICE_client_continue (client);
+}
+
+
+static int
+check_client_host_enter (void *cls,
+                         const struct HostEnterRequest *hr)
+{
+  return GNUNET_OK;
+}
+
+
+/**
+ * Handle a connecting client entering a place as host.
+ */
+static void
+handle_client_host_enter (void *cls,
+                          const struct HostEnterRequest *hr)
+{
+  struct Client *c = cls;
+  struct GNUNET_SERVICE_Client *client = c->client;
+  struct HostEnterRequest *
+    hreq = (struct HostEnterRequest *) GNUNET_copy_message (&hr->header);
+
+  uint8_t app_id_size = ntohs (hreq->header.size) - sizeof (*hreq);
+  const char *app_id = NULL;
+  uint16_t offset = GNUNET_STRINGS_buffer_tokenize ((const char *) &hreq[1],
+                                                    app_id_size, 1, &app_id);
+  if (0 == offset || offset != app_id_size || app_id == NULL)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "offset = %u, app_id_size = %u, app_id = %s\n",
+                offset, app_id_size, app_id);
+    GNUNET_break (0);
+    GNUNET_SERVICE_client_drop (client);
+    return;
+  }
+
+  struct Host *hst = NULL;
+  struct Place *plc = NULL;
+  int ret = GNUNET_OK;
+
+  struct GNUNET_CRYPTO_EddsaPublicKey empty_pub_key;
+  memset (&empty_pub_key, 0, sizeof (empty_pub_key));
+
+  if (0 == memcmp (&hreq->place_pub_key, &empty_pub_key, sizeof 
(empty_pub_key)))
+  { // no public key set: create new private key & save the place
+    struct GNUNET_CRYPTO_EddsaPrivateKey *
+      place_key = GNUNET_CRYPTO_eddsa_key_create ();
+    hreq->place_key = *place_key;
+    GNUNET_CRYPTO_eddsa_key_get_public (place_key, &hreq->place_pub_key);
+    GNUNET_CRYPTO_eddsa_key_clear (place_key);
+    GNUNET_free (place_key);
+
+    app_place_save (app_id, (const struct PlaceEnterRequest *) hreq);
+  }
+
+  switch (host_enter (hreq, &hst))
+  {
+  case GNUNET_YES:
+    plc = c->place = &hst->place;
+    plc->host = hst;
+    break;
+
+  case GNUNET_NO:
+  {
+    plc = c->place = &hst->place;
+    plc->host = hst;
+    client_send_host_enter_ack (client, hst, GNUNET_OK);
+    break;
+  }
+  case GNUNET_SYSERR:
+    ret = GNUNET_SYSERR;
+  }
+
+  if (ret != GNUNET_SYSERR)
+  {
+
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "%p Client connected as host to place %s.\n",
+                hst, GNUNET_h2s (&plc->pub_key_hash));
+
+    struct ClientListItem *cli = GNUNET_new (struct ClientListItem);
+    cli->client = client;
+    GNUNET_CONTAINER_DLL_insert (plc->clients_head, plc->clients_tail, cli);
+    c->place = plc;
+    app_notify_place (&hreq->header, client);
+  }
+
+  GNUNET_CRYPTO_eddsa_key_clear (&hreq->place_key);
+  GNUNET_free (hreq);
+
+  if (GNUNET_OK == ret)
+    GNUNET_SERVICE_client_continue (client);
+  else
+    GNUNET_SERVICE_client_drop (client);
+}
+
+
+/**
+ * Enter place as guest.
+ *
+ * @param greq
+ *        Guest entry request.
+ * @param[out] ret_gst
+ *        Returned Guest struct.
+ *
+ * @return #GNUNET_YES if the guest entered the place just now,
+ *         #GNUNET_NO  if the place is already entered,
+ *         #GNUNET_SYSERR on error.
+ */
+static int
+guest_enter (const struct GuestEnterRequest *greq, struct Guest **ret_gst)
+{
+  int ret = GNUNET_NO;
+  uint16_t greq_size = ntohs (greq->header.size);
+
+  struct GNUNET_CRYPTO_EcdsaPublicKey ego_pub_key = greq->ego_pub_key;
+  struct GNUNET_HashCode ego_pub_hash;
+  GNUNET_CRYPTO_hash (&ego_pub_key, sizeof (ego_pub_key), &ego_pub_hash);
+  struct Ego *ego = GNUNET_CONTAINER_multihashmap_get (egos, &ego_pub_hash);
+
+  if (NULL == ego)
+  {
+    return GNUNET_SYSERR;
+  }
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "entering as guest\n");
+  struct GNUNET_HashCode place_pub_hash;
+  GNUNET_CRYPTO_hash (&greq->place_pub_key, sizeof (greq->place_pub_key),
+                      &place_pub_hash);
+
+  struct GNUNET_CONTAINER_MultiHashMap *
+    plc_gst = GNUNET_CONTAINER_multihashmap_get (place_guests, 
&place_pub_hash);
+  struct Guest *gst = NULL;
+  int new_guest;
+
+  if (NULL != plc_gst)
+    gst = GNUNET_CONTAINER_multihashmap_get (plc_gst, &ego_pub_hash);
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "plc_gst = %p, gst = %p\n",
+              plc_gst,
+              gst);
+
+  if (NULL == gst)
+  {
+    gst = GNUNET_new (struct Guest);
+    new_guest = GNUNET_YES;
+  }
+  else new_guest = GNUNET_NO;
+
+  if (NULL == gst->slave)
+  {
+    gst->origin = greq->origin;
+    gst->relay_count = ntohl (greq->relay_count);
+
+    uint16_t len;
+    uint16_t remaining = ntohs (greq->header.size) - sizeof (*greq);
+    const char *app_id = (const char *) &greq[1];
+    const char *p = app_id;
+
+    len = strnlen (app_id, remaining);
+    if (len == remaining)
+    {
+      GNUNET_free (gst);
+      GNUNET_break (0);
+      return GNUNET_SYSERR;
+    }
+    p += len + 1;
+    remaining -= len + 1;
+
+    const struct GNUNET_PeerIdentity *relays = NULL;
+    uint16_t relay_size = gst->relay_count * sizeof (*relays);
+    if (remaining < relay_size)
+    {
+      GNUNET_free (gst);
+      GNUNET_break (0);
+      return GNUNET_SYSERR;
+    }
+    if (0 < relay_size)
+      relays = (const struct GNUNET_PeerIdentity *) p;
+    p += relay_size;
+    remaining -= relay_size;
+
+    struct GNUNET_PSYC_Message *join_msg = NULL;
+    uint16_t join_msg_size = 0;
+
+    if (sizeof (struct GNUNET_MessageHeader) <= remaining)
+    {
+      join_msg = (struct GNUNET_PSYC_Message *) p;
+      join_msg_size = ntohs (join_msg->header.size);
+      p += join_msg_size;
+      remaining -= join_msg_size;
+    }
+    if (0 != remaining)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "%zu + %u + %u != %u\n",
+                  sizeof (*greq), relay_size, join_msg_size, greq_size);
+      GNUNET_free (gst);
+      GNUNET_break (0);
+      return GNUNET_SYSERR;
+    }
+    if (0 < relay_size)
+    {
+      gst->relays = GNUNET_malloc (relay_size);
+      GNUNET_memcpy (gst->relays, relays, relay_size);
+    }
+
+    gst->join_flags = ntohl (greq->flags);
+
+    struct Place *plc = &gst->place;
+    place_init (plc);
+    plc->is_host = GNUNET_NO;
+    plc->pub_key = greq->place_pub_key;
+    plc->pub_key_hash = place_pub_hash;
+    plc->ego_pub_key = ego_pub_key;
+    plc->ego_pub_hash = ego_pub_hash;
+    plc->ego_key = ego->key;
+
+    if (NULL == plc_gst)
+    {
+      plc_gst = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES);
+      (void) GNUNET_CONTAINER_multihashmap_put (place_guests, 
&plc->pub_key_hash, plc_gst,
+                                                
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
+    }
+    if (GNUNET_YES == new_guest)
+    {
+      (void) GNUNET_CONTAINER_multihashmap_put (plc_gst, &plc->ego_pub_hash, 
gst,
+                                                
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
+      (void) GNUNET_CONTAINER_multihashmap_put (guests, &plc->pub_key_hash, 
gst,
+                                              
GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
+
+    }
+    gst->slave
+      = GNUNET_PSYC_slave_join (cfg, &plc->pub_key, &plc->ego_key,
+                                gst->join_flags, &gst->origin,
+                                gst->relay_count, gst->relays,
+                                &psyc_recv_message, NULL,
+                                &psyc_slave_connected,
+                                &psyc_recv_join_dcsn,
+                                gst, join_msg);
+    plc->channel = GNUNET_PSYC_slave_get_channel (gst->slave);
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "slave entered channel %p\n",
+                plc->channel);
+    ret = GNUNET_YES;
+  }
+
+  // TODO: explain to automatic code scanners why free(gst) not necessary
+  if (NULL != ret_gst)
+    *ret_gst = gst;
+  return ret;
+}
+
+
+static int
+client_guest_enter (struct Client *c,
+                    const struct GuestEnterRequest *greq)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "client_guest_enter\n");
+  struct GNUNET_PSYC_CountersResultMessage *result_msg;
+  struct GNUNET_MQ_Envelope *env;
+  struct GNUNET_SERVICE_Client *client = c->client;
+  uint16_t remaining = ntohs (greq->header.size) - sizeof (*greq);
+  const char *app_id = NULL;
+  uint16_t offset = GNUNET_STRINGS_buffer_tokenize ((const char *) &greq[1],
+                                                    remaining, 1, &app_id);
+  struct Guest *gst = NULL;
+  struct Place *plc = NULL;
+
+  if (0 == offset)
+  {
+    return GNUNET_SYSERR;
+  }
+  switch (guest_enter (greq, &gst))
+  {
+  case GNUNET_YES:
+  {
+    plc = c->place = &gst->place;
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "guest entered successfully to local place %s\n",
+                GNUNET_h2s (&plc->pub_key_hash));
+    plc->guest = gst;
+    app_place_save (app_id, (const struct PlaceEnterRequest *) greq);
+    app_notify_place (&greq->header, client);
+    break;
+  }
+  case GNUNET_NO:
+  {
+    plc = c->place = &gst->place;
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "guest re-entered successfully to local place %s\n",
+                GNUNET_h2s (&plc->pub_key_hash));
+    plc->guest = gst;
+    env = GNUNET_MQ_msg (result_msg,
+                         GNUNET_MESSAGE_TYPE_SOCIAL_GUEST_ENTER_ACK);
+    result_msg->result_code = htonl (GNUNET_OK);
+    result_msg->max_message_id = GNUNET_htonll (plc->max_message_id);
+    GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client),
+                    env);
+    if (NULL != gst->join_dcsn)
+    { 
+      env = GNUNET_MQ_msg_copy (&gst->join_dcsn->header);
+      GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client),
+                      env);
+    }
+    break;
+  }
+  case GNUNET_SYSERR:
+  {
+    return GNUNET_SYSERR;
+  }
+  }
+
+  struct ClientListItem *cli = GNUNET_new (struct ClientListItem);
+  cli->client = client;
+  GNUNET_CONTAINER_DLL_insert (plc->clients_head, plc->clients_tail, cli);
+  return GNUNET_OK;
+}
+
+
+static int
+check_client_guest_enter (void *cls,
+                          const struct GuestEnterRequest *greq)
+{
+  return GNUNET_OK;
+}
+
+
+/**
+ * Handle a connecting client entering a place as guest.
+ */
+static void
+handle_client_guest_enter (void *cls,
+                           const struct GuestEnterRequest *greq)
+{
+  struct Client *c = cls;
+
+  if (GNUNET_SYSERR == client_guest_enter (c, greq))
+  {
+    GNUNET_break (0);
+    GNUNET_SERVICE_client_drop (c->client);
+    return;
+  }
+  GNUNET_SERVICE_client_continue (c->client);
+}
+
+
+struct GuestEnterByNameClosure
+{
+  struct Client *client;
+  char *app_id;
+  char *password;
+  struct GNUNET_CRYPTO_EcdsaPublicKey ego_pub_key;
+  struct GNUNET_MessageHeader *join_msg;
+};
+
+
+/**
+ * Result of a GNS name lookup for entering a place.
+ *
+ * @see GNUNET_SOCIAL_guest_enter_by_name
+ */
+static void
+gns_result_guest_enter (void *cls, uint32_t rd_count,
+                        const struct GNUNET_GNSRECORD_Data *rd)
+{
+  struct GuestEnterByNameClosure *gcls = cls;
+  struct Client *c = gcls->client;
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%p GNS result: %u records.\n",
+              c, rd_count);
+
+  const struct GNUNET_GNSRECORD_PlaceData *
+    rec = (const struct GNUNET_GNSRECORD_PlaceData *) rd->data;
+
+  if (0 == rd_count || rd->data_size < sizeof (*rec))
+  {
+    GNUNET_break (0);
+    GNUNET_SERVICE_client_drop (c->client);
+    return;
+  }
+
+  uint16_t relay_count = ntohl (rec->relay_count);
+  struct GNUNET_PeerIdentity *relays = NULL;
+
+  if (0 < relay_count)
+  {
+    if (rd->data_size == sizeof (*rec) + relay_count * sizeof (struct 
GNUNET_PeerIdentity))
+    {
+      relays = (struct GNUNET_PeerIdentity *) &rec[1];
+    }
+    else
+    {
+      relay_count = 0;
+      GNUNET_break_op (0);
+    }
+  }
+
+  uint16_t app_id_size = strlen (gcls->app_id) + 1;
+  uint16_t relay_size = relay_count * sizeof (*relays);
+  uint16_t join_msg_size = 0;
+  if (NULL != gcls->join_msg)
+    join_msg_size = ntohs (gcls->join_msg->size);
+  uint16_t greq_size = sizeof (struct GuestEnterRequest)
+    + app_id_size + relay_size + join_msg_size;
+  struct GuestEnterRequest *greq = GNUNET_malloc (greq_size);
+  greq->header.size = htons (greq_size);
+  greq->header.type = htons (GNUNET_MESSAGE_TYPE_SOCIAL_GUEST_ENTER);
+  greq->ego_pub_key = gcls->ego_pub_key;
+  greq->place_pub_key = rec->place_pub_key;
+  greq->origin = rec->origin;
+  greq->relay_count = rec->relay_count;
+
+  void *p = &greq[1];
+  GNUNET_memcpy (p, gcls->app_id, app_id_size);
+  p += app_id_size;
+  GNUNET_memcpy (p, relays, relay_size);
+  p += relay_size;
+  GNUNET_memcpy (p, gcls->join_msg, join_msg_size);
+
+  client_guest_enter (c, greq);
+
+  GNUNET_free (gcls->app_id);
+  if (NULL != gcls->password)
+    GNUNET_free (gcls->password);
+  if (NULL != gcls->join_msg)
+    GNUNET_free (gcls->join_msg);
+  GNUNET_free (gcls);
+  GNUNET_free (greq);
+}
+
+
+static int
+check_client_guest_enter_by_name (void *cls,
+                                  const struct GuestEnterByNameRequest *greq)
+{
+  return GNUNET_OK;
+}
+
+
+/**
+ * Handle a connecting client entering a place as guest using a GNS address.
+ *
+ * Look up GNS address and generate a GuestEnterRequest from that.
+ */
+static void
+handle_client_guest_enter_by_name (void *cls,
+                                   const struct GuestEnterByNameRequest *greq)
+{
+  struct Client *c = cls;
+  struct GNUNET_SERVICE_Client *client = c->client;
+
+  struct GuestEnterByNameClosure *gcls = GNUNET_malloc (sizeof (*gcls));
+  gcls->client = c;
+  gcls->ego_pub_key = greq->ego_pub_key;
+
+  const char *p = (const char *) &greq[1];
+  const char *app_id = NULL, *password = NULL, *gns_name = NULL;
+  uint16_t remaining = ntohs (greq->header.size) - sizeof (*greq);
+  uint16_t offset = GNUNET_STRINGS_buffer_tokenize (p, remaining, 3,
+                                                    &app_id,
+                                                    &gns_name,
+                                                    &password);
+  p += offset;
+  remaining -= offset;
+
+  if (0 != offset && sizeof (*gcls->join_msg) <= remaining)
+  {
+    gcls->join_msg = GNUNET_copy_message ((struct GNUNET_MessageHeader *) p);
+    remaining -= ntohs (gcls->join_msg->size);
+  }
+
+  if (0 == offset || 0 != remaining)
+  {
+    if (NULL != gcls->join_msg)
+      GNUNET_free (gcls->join_msg);
+    GNUNET_free (gcls);
+    GNUNET_break (0);
+    GNUNET_SERVICE_client_drop (client);
+    return;
+  }
+
+  uint16_t app_id_size = strlen (app_id) + 1;
+  gcls->app_id = GNUNET_malloc (app_id_size);
+  GNUNET_memcpy (gcls->app_id, app_id, app_id_size);
+
+  uint16_t password_size = strlen (password);
+  if (0 < password_size++)
+  {
+    gcls->password = GNUNET_malloc (password_size);
+    GNUNET_memcpy (gcls->password, password, password_size);
+  }
+
+  GNUNET_GNS_lookup (gns, gns_name,
+                     &greq->ego_pub_key,
+                     GNUNET_GNSRECORD_TYPE_PLACE,
+                     GNUNET_GNS_LO_DEFAULT,
+                     &gns_result_guest_enter, gcls);
+  GNUNET_SERVICE_client_continue (client);
+}
+
+
+static int
+check_client_app_connect (void *cls,
+                          const struct AppConnectRequest *creq)
+{
+  return GNUNET_OK;
+}
+
+
+/**
+ * Handle application connection.
+ */
+static void
+handle_client_app_connect (void *cls,
+                           const struct AppConnectRequest *creq)
+{
+  struct Client *c = cls;
+  struct GNUNET_SERVICE_Client *client = c->client;
+  ssize_t app_id_size = ntohs (creq->header.size) - sizeof (*creq);
+  const char *app_id = NULL;
+  uint16_t offset;
+ 
+  if (app_id_size < 0)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "AppConnectRequest has invalid size\n");
+    GNUNET_break (0);
+    GNUNET_SERVICE_client_drop (client);
+    return;
+  }
+
+  offset = GNUNET_STRINGS_buffer_tokenize ((const char *) &creq[1],
+                                           (size_t) app_id_size,
+                                           1, 
+                                           &app_id);
+  if (0 == offset || offset != app_id_size)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "AppConnectRequest contains invalid app ID\n"); 
+    GNUNET_break (0);
+    GNUNET_SERVICE_client_drop (client);
+    return;
+  }
+
+  struct GNUNET_HashCode app_id_hash;
+  GNUNET_CRYPTO_hash (app_id, (size_t) app_id_size, &app_id_hash);
+
+  GNUNET_CONTAINER_multihashmap_iterate (egos, ego_entry, client);
+  app_notify_ego_end (client);
+
+  struct GNUNET_CONTAINER_MultiHashMap *
+    app_places = GNUNET_CONTAINER_multihashmap_get (apps_places, &app_id_hash);
+  if (NULL != app_places)
+    GNUNET_CONTAINER_multihashmap_iterate (app_places, app_place_entry_notify, 
client);
+  app_notify_place_end (client);
+
+  struct ClientListItem *cli = GNUNET_new (struct ClientListItem);
+  cli->client = client;
+  struct Application *app = GNUNET_CONTAINER_multihashmap_get (apps,
+                                                               &app_id_hash);
+  if (NULL == app) {
+    app = GNUNET_malloc (sizeof (*app));
+    (void) GNUNET_CONTAINER_multihashmap_put (apps, &app_id_hash, app,
+                                              
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
+  }
+  GNUNET_CONTAINER_DLL_insert (app->clients_head, app->clients_tail, cli);
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%p Application %s connected.\n", app, app_id);
+
+  c->app_id = GNUNET_malloc ((size_t) app_id_size);
+  GNUNET_memcpy (c->app_id, app_id, (size_t) app_id_size);
+
+  GNUNET_SERVICE_client_continue (client);
+}
+
+
+/**
+ * Handle application detach request.
+ */
+static void
+handle_client_app_detach (void *cls,
+                          const struct AppDetachRequest *req)
+{
+  struct Client *c = cls;
+  struct GNUNET_SERVICE_Client *client = c->client;
+
+  int ret = app_place_remove (c->app_id, &req->ego_pub_key, 
&req->place_pub_key);
+  client_send_result (client, req->op_id, ret, NULL, 0);
+
+  GNUNET_SERVICE_client_continue (client);
+}
+
+
+static void
+place_leave_cb (void *cls)
+{
+  struct Place *plc = cls;
+
+  place_send_leave_ack (plc);
+  (GNUNET_YES == plc->is_host)
+    ? cleanup_host ((struct Host *) plc)
+    : cleanup_guest ((struct Guest *) plc);
+}
+
+
+/**
+ * Handle application leave request.
+ */
+static void
+handle_client_place_leave (void *cls,
+                           const struct GNUNET_MessageHeader *msg)
+{
+  struct Client *c = cls;
+  struct GNUNET_SERVICE_Client *client = c->client;
+  struct Place *plc = c->place;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "got leave request from %s for place %s",
+              plc->is_host? "host" : "slave",
+              GNUNET_h2s (&plc->pub_key_hash)); 
+  if (NULL == plc)
+  {
+    GNUNET_break (0);
+    GNUNET_SERVICE_client_drop (client);
+    return;
+  }
+
+  if (GNUNET_YES != plc->is_disconnecting)
+  {
+    plc->is_disconnecting = GNUNET_YES;
+    if (plc->is_host)
+    {
+      struct Host *host = plc->host;
+      GNUNET_assert (NULL != host);
+      GNUNET_PSYC_master_stop (host->master, GNUNET_NO, &place_leave_cb, plc);
+    }
+    else
+    {
+      struct Guest *guest = plc->guest;
+      GNUNET_assert (NULL != guest);
+      GNUNET_PSYC_slave_part (guest->slave, GNUNET_NO, &place_leave_cb, plc);
+    }
+  }
+  else
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "got leave request but place is already leaving\n");
+  }
+  GNUNET_SERVICE_client_continue (client);
+}
+
+
+struct JoinDecisionClosure
+{
+  int32_t is_admitted;
+  struct GNUNET_PSYC_Message *msg;
+};
+
+
+/**
+ * Iterator callback for responding to join requests.
+ */
+static int
+psyc_send_join_decision (void *cls, const struct GNUNET_HashCode *pub_key_hash,
+                         void *value)
+{
+  struct JoinDecisionClosure *jcls = cls;
+  struct GNUNET_PSYC_JoinHandle *jh = value;
+  // FIXME: add relays
+  GNUNET_PSYC_join_decision (jh, jcls->is_admitted, 0, NULL, jcls->msg);
+  return GNUNET_YES;
+}
+
+
+static int
+check_client_join_decision (void *cls,
+                            const struct GNUNET_PSYC_JoinDecisionMessage *dcsn)
+{
+  return GNUNET_OK;
+}
+
+
+/**
+ * Handle an entry decision from a host client.
+ */
+static void
+handle_client_join_decision (void *cls,
+                             const struct GNUNET_PSYC_JoinDecisionMessage 
*dcsn)
+{
+  struct Client *c = cls;
+  struct GNUNET_SERVICE_Client *client = c->client;
+  struct Place *plc = c->place;
+  if (NULL == plc || GNUNET_YES != plc->is_host)
+  {
+    GNUNET_break (0);
+    GNUNET_SERVICE_client_drop (client);
+    return;
+  }
+  struct Host *hst = plc->host;
+
+  struct JoinDecisionClosure jcls;
+  jcls.is_admitted = ntohl (dcsn->is_admitted);
+  jcls.msg
+    = (sizeof (*dcsn) + sizeof (*jcls.msg) <= ntohs (dcsn->header.size))
+    ? (struct GNUNET_PSYC_Message *) &dcsn[1]
+    : NULL;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "jcls.msg = %p\n",
+              jcls.msg);
+  struct GNUNET_HashCode slave_pub_hash;
+  GNUNET_CRYPTO_hash (&dcsn->slave_pub_key, sizeof (dcsn->slave_pub_key),
+                      &slave_pub_hash);
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%p Got join decision (%d) from client for place %s..\n",
+              hst, jcls.is_admitted, GNUNET_h2s (&plc->pub_key_hash));
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%p ..and slave %s.\n",
+              hst, GNUNET_h2s (&slave_pub_hash));
+
+  GNUNET_CONTAINER_multihashmap_get_multiple (hst->join_reqs, &slave_pub_hash,
+                                              &psyc_send_join_decision, &jcls);
+  GNUNET_CONTAINER_multihashmap_remove_all (hst->join_reqs, &slave_pub_hash);
+
+  GNUNET_SERVICE_client_continue (client);
+}
+
+
+/**
+ * Send acknowledgement to a client.
+ *
+ * Sent after a message fragment has been passed on to multicast.
+ *
+ * @param plc The place struct for the client.
+ */
+static void
+send_message_ack (struct Place *plc, struct GNUNET_SERVICE_Client *client)
+{
+  struct GNUNET_MQ_Envelope *env;
+
+  env = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_ACK);
+  GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client),
+                  env);
+}
+
+
+/**
+ * Proceed to the next message part in the transmission queue.
+ *
+ * @param plc
+ *        Place where the transmission is going on.
+ * @param tmit_msg
+ *        Currently transmitted message.
+ * @param tmit_frag
+ *        Currently transmitted message fragment.
+ *
+ * @return @a tmit_frag, or NULL if reached the end of fragment.
+ */
+static struct FragmentTransmitQueue *
+psyc_transmit_queue_next_part (struct Place *plc,
+                               struct MessageTransmitQueue *tmit_msg,
+                               struct FragmentTransmitQueue *tmit_frag)
+{
+  uint16_t psize = ntohs (tmit_frag->next_part->size);
+  if ((char *) tmit_frag->next_part + psize - ((char *) &tmit_frag[1])
+      < tmit_frag->size)
+  {
+    tmit_frag->next_part
+      = (struct GNUNET_MessageHeader *) ((char *) tmit_frag->next_part + 
psize);
+  }
+  else /* Reached end of current fragment. */
+  {
+    if (NULL != tmit_frag->client)
+      send_message_ack (plc, tmit_frag->client);
+    GNUNET_CONTAINER_DLL_remove (tmit_msg->frags_head, tmit_msg->frags_tail, 
tmit_frag);
+    GNUNET_free (tmit_frag);
+    tmit_frag = NULL;
+  }
+  return tmit_frag;
+}
+
+
+/**
+ * Proceed to next message in transmission queue.
+ *
+ * @param plc
+ *        Place where the transmission is going on.
+ * @param tmit_msg
+ *        Currently transmitted message.
+ *
+ * @return The next message in queue, or NULL if queue is empty.
+ */
+static struct MessageTransmitQueue *
+psyc_transmit_queue_next_msg (struct Place *plc,
+                              struct MessageTransmitQueue *tmit_msg)
+{
+  GNUNET_CONTAINER_DLL_remove (plc->tmit_msgs_head, plc->tmit_msgs_tail, 
tmit_msg);
+  GNUNET_free (tmit_msg);
+  return plc->tmit_msgs_head;
+}
+
+
+/**
+ * Callback for data transmission to PSYC.
+ */
+static int
+psyc_transmit_notify_data (void *cls, uint16_t *data_size, void *data)
+{
+  struct Place *plc = cls;
+  struct MessageTransmitQueue *tmit_msg = plc->tmit_msgs_head;
+  GNUNET_assert (NULL != tmit_msg);
+  struct FragmentTransmitQueue *tmit_frag = tmit_msg->frags_head;
+  if (NULL == tmit_frag)
+  { /* Rest of the message have not arrived yet, pause transmission */
+    *data_size = 0;
+    return GNUNET_NO;
+  }
+  struct GNUNET_MessageHeader *pmsg = tmit_frag->next_part;
+  if (NULL == pmsg)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "%p psyc_transmit_notify_data: nothing to send.\n", plc);
+    *data_size = 0;
+    return GNUNET_NO;
+  }
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%p psyc_transmit_notify_data()\n", plc);
+  GNUNET_PSYC_log_message (GNUNET_ERROR_TYPE_DEBUG, pmsg);
+
+  uint16_t ptype = ntohs (pmsg->type);
+  uint16_t pdata_size = ntohs (pmsg->size) - sizeof (*pmsg);
+  int ret;
+
+  switch (ptype)
+  {
+  case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_DATA:
+    if (*data_size < pdata_size)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "%p psyc_transmit_notify_data: buffer size too small for 
data.\n", plc);
+      *data_size = 0;
+      return GNUNET_NO;
+    }
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "%p psyc_transmit_notify_data: sending %u bytes.\n",
+                plc, pdata_size);
+
+    *data_size = pdata_size;
+    GNUNET_memcpy (data, &pmsg[1], *data_size);
+    ret = GNUNET_NO;
+    break;
+
+  case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END:
+    *data_size = 0;
+    ret = GNUNET_YES;
+    break;
+
+  case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL:
+    *data_size = 0;
+    ret = GNUNET_SYSERR;
+    break;
+
+  default:
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "%p psyc_transmit_notify_data: unexpected message part of type 
%u.\n",
+                plc, ptype);
+    ret = GNUNET_SYSERR;
+  }
+
+  if (GNUNET_SYSERR == ret && GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL != ptype)
+  {
+    *data_size = 0;
+    tmit_msg = psyc_transmit_queue_next_msg (plc, tmit_msg);
+    GNUNET_SERVICE_client_drop (tmit_frag->client);
+    GNUNET_SCHEDULER_add_now (&cleanup_place, plc);
+    return ret;
+  }
+  else
+  {
+    tmit_frag = psyc_transmit_queue_next_part (plc, tmit_msg, tmit_frag);
+    if (NULL != tmit_frag)
+    {
+      struct GNUNET_MessageHeader *pmsg = tmit_frag->next_part;
+      ptype = ntohs (pmsg->type);
+      switch (ptype)
+      {
+      case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END:
+        ret = GNUNET_YES;
+        break;
+      case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL:
+        ret = GNUNET_SYSERR;
+        break;
+      }
+      switch (ptype)
+      {
+      case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END:
+      case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL:
+        tmit_frag = psyc_transmit_queue_next_part (plc, tmit_msg, tmit_frag);
+      }
+    }
+
+    if (NULL == tmit_msg->frags_head
+        && GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END <= ptype)
+    { /* Reached end of current message. */
+      tmit_msg = psyc_transmit_queue_next_msg (plc, tmit_msg);
+    }
+  }
+
+  if (ret != GNUNET_NO)
+  {
+    if (NULL != tmit_msg)
+    {
+      psyc_transmit_message (plc);
+    }
+    /* FIXME: handle partial message (when still in_transmit) */
+  }
+  return ret;
+}
+
+
+/**
+ * Callback for modifier transmission to PSYC.
+ */
+static int
+psyc_transmit_notify_mod (void *cls, uint16_t *data_size, void *data,
+                          uint8_t *oper, uint32_t *full_value_size)
+{
+  struct Place *plc = cls;
+  struct MessageTransmitQueue *tmit_msg = plc->tmit_msgs_head;
+  GNUNET_assert (NULL != tmit_msg);
+  struct FragmentTransmitQueue *tmit_frag = tmit_msg->frags_head;
+  if (NULL == tmit_frag)
+  { /* Rest of the message have not arrived yet, pause transmission */
+    *data_size = 0;
+    return GNUNET_NO;
+  }
+  struct GNUNET_MessageHeader *pmsg = tmit_frag->next_part;
+  if (NULL == pmsg)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "%p psyc_transmit_notify_mod: nothing to send.\n", plc);
+    *data_size = 0;
+    return GNUNET_NO;
+  }
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%p psyc_transmit_notify_mod()\n", plc);
+  GNUNET_PSYC_log_message (GNUNET_ERROR_TYPE_DEBUG, pmsg);
+
+  uint16_t ptype = ntohs (pmsg->type);
+  int ret;
+
+  switch (ptype)
+  {
+  case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MODIFIER:
+  {
+    if (NULL == oper)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "%p psyc_transmit_notify_mod: oper is NULL.\n", plc);
+      ret = GNUNET_SYSERR;
+      break;
+    }
+    struct GNUNET_PSYC_MessageModifier *
+      pmod = (struct GNUNET_PSYC_MessageModifier *) tmit_frag->next_part;
+    uint16_t mod_size = ntohs (pmod->header.size) - sizeof (*pmod);
+
+    if (*data_size < mod_size)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "%p psyc_transmit_notify_mod: buffer size too small for 
data.\n", plc);
+      *data_size = 0;
+      return GNUNET_NO;
+    }
+
+    *full_value_size = ntohl (pmod->value_size);
+    *oper = pmod->oper;
+    *data_size = mod_size;
+    GNUNET_memcpy (data, &pmod[1], mod_size);
+    ret = GNUNET_NO;
+    break;
+  }
+
+  case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MOD_CONT:
+  {
+    if (NULL != oper)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "%p psyc_transmit_notify_mod: oper is not NULL.\n", plc);
+      ret = GNUNET_SYSERR;
+      break;
+    }
+    uint16_t mod_size = ntohs (pmsg->size) - sizeof (*pmsg);
+    if (*data_size < mod_size)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "%p psyc_transmit_notify_mod: buffer size too small for 
data.\n", plc);
+      *data_size = 0;
+      return GNUNET_NO;
+    }
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "%p psyc_transmit_notify_mod: sending %u bytes.\n", plc, 
mod_size);
+
+    *data_size = mod_size;
+    GNUNET_memcpy (data, &pmsg[1], *data_size);
+    ret = GNUNET_NO;
+    break;
+  }
+
+  case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_DATA:
+  case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END:
+  case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL:
+    *data_size = 0;
+    ret = GNUNET_YES;
+    break;
+
+  default:
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "%p psyc_transmit_notify_mod: unexpected message part of type 
%u.\n",
+                plc, ptype);
+    ret = GNUNET_SYSERR;
+  }
+
+  if (GNUNET_SYSERR == ret)
+  {
+    *data_size = 0;
+    ret = GNUNET_SYSERR;
+    tmit_msg = psyc_transmit_queue_next_msg (plc, tmit_msg);
+    GNUNET_SERVICE_client_drop (tmit_frag->client);
+    GNUNET_SCHEDULER_add_now (&cleanup_place, plc);
+  }
+  else
+  {
+    if (GNUNET_YES != ret)
+      psyc_transmit_queue_next_part (plc, tmit_msg, tmit_frag);
+
+    if (NULL == tmit_msg->frags_head
+        && GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END <= ptype)
+    { /* Reached end of current message. */
+      tmit_msg = psyc_transmit_queue_next_msg (plc, tmit_msg);
+    }
+  }
+  return ret;
+}
+
+/**
+ * Callback for data transmission from a host to PSYC.
+ */
+static int
+host_transmit_notify_data (void *cls, uint16_t *data_size, void *data)
+{
+  int ret = psyc_transmit_notify_data (cls, data_size, data);
+
+  if (GNUNET_NO != ret)
+  {
+    struct Host *hst = cls;
+    hst->tmit_handle = NULL;
+  }
+  return ret;
+}
+
+
+/**
+ * Callback for the transmit functions of multicast.
+ */
+static int
+guest_transmit_notify_data (void *cls, uint16_t *data_size, void *data)
+{
+  int ret = psyc_transmit_notify_data (cls, data_size, data);
+
+  if (GNUNET_NO != ret)
+  {
+    struct Guest *gst = cls;
+    gst->tmit_handle = NULL;
+  }
+  return ret;
+}
+
+
+/**
+ * Callback for modifier transmission from a host to PSYC.
+ */
+static int
+host_transmit_notify_mod (void *cls, uint16_t *data_size, void *data,
+                          uint8_t *oper, uint32_t *full_value_size)
+{
+  int ret = psyc_transmit_notify_mod (cls, data_size, data,
+                                      oper, full_value_size);
+  if (GNUNET_SYSERR == ret)
+  {
+    struct Host *hst = cls;
+    hst->tmit_handle = NULL;
+  }
+  return ret;
+}
+
+
+/**
+ * Callback for modifier transmission from a guest to PSYC.
+ */
+static int
+guest_transmit_notify_mod (void *cls, uint16_t *data_size, void *data,
+                           uint8_t *oper, uint32_t *full_value_size)
+{
+  int ret = psyc_transmit_notify_mod (cls, data_size, data,
+                                      oper, full_value_size);
+  if (GNUNET_SYSERR == ret)
+  {
+    struct Guest *gst = cls;
+    gst->tmit_handle = NULL;
+  }
+  return ret;
+}
+
+
+/**
+ * Get method part of next message from transmission queue.
+ *
+ * @param plc
+ *        Place
+ *
+ * @return #GNUNET_OK on success
+ *         #GNUNET_NO if there are no more messages in queue.
+ *         #GNUNET_SYSERR if the next message is malformed.
+ */
+static struct GNUNET_PSYC_MessageMethod *
+psyc_transmit_queue_next_method (struct Place *plc)
+{
+  struct MessageTransmitQueue *tmit_msg = plc->tmit_msgs_head;
+  if (NULL == tmit_msg)
+    return GNUNET_NO;
+
+  struct FragmentTransmitQueue *tmit_frag = tmit_msg->frags_head;
+  if (NULL == tmit_frag)
+  {
+    GNUNET_break (0);
+    return GNUNET_NO;
+  }
+
+  struct GNUNET_MessageHeader *pmsg = tmit_frag->next_part;
+  if (NULL == pmsg
+      || GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_METHOD != ntohs (pmsg->type))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "%p psyc_transmit_queue_next_method: unexpected message part 
of type %u.\n",
+                plc, NULL != pmsg ? ntohs (pmsg->type) : 0);
+    GNUNET_break (0);
+    return NULL;
+  }
+
+  uint16_t psize = ntohs (pmsg->size);
+  struct GNUNET_PSYC_MessageMethod *
+    pmeth = (struct GNUNET_PSYC_MessageMethod *) GNUNET_copy_message (pmsg);
+
+  if (psize < sizeof (*pmeth) + 1 || '\0' != *((char *) pmeth + psize - 1))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "%p psyc_transmit_queue_next_method: invalid method name.\n",
+                plc);
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "%zu <= %u || NUL != %u\n",
+                sizeof (*pmeth), psize, *((char *) pmeth + psize - 1));
+    GNUNET_break (0);
+    GNUNET_free (pmeth);
+    return NULL;
+  }
+
+  psyc_transmit_queue_next_part (plc, tmit_msg, tmit_frag);
+  return pmeth;
+}
+
+
+/**
+ * Transmit the next message in queue from the host to the PSYC channel.
+ */
+static int
+psyc_master_transmit_message (struct Host *hst)
+{
+  struct Place *plc = &hst->place;
+
+  if (NULL == hst->tmit_handle)
+  {
+    struct GNUNET_PSYC_MessageMethod *
+      pmeth = psyc_transmit_queue_next_method (plc);
+    if (NULL == pmeth)
+      return GNUNET_SYSERR;
+
+    hst->tmit_handle = (void *) &hst->tmit_handle;
+    struct GNUNET_PSYC_MasterTransmitHandle *
+      tmit_handle = GNUNET_PSYC_master_transmit (hst->master, (const char *) 
&pmeth[1],
+                                                 &host_transmit_notify_mod,
+                                                 &host_transmit_notify_data, 
hst,
+                                                 pmeth->flags);
+    if (NULL != hst->tmit_handle)
+      hst->tmit_handle = tmit_handle;
+    GNUNET_free (pmeth);
+  }
+  else
+  {
+    GNUNET_PSYC_master_transmit_resume (hst->tmit_handle);
+  }
+  return GNUNET_OK;
+}
+
+
+/**
+ * Transmit the next message in queue from a guest to the PSYC channel.
+ */
+static int
+psyc_slave_transmit_message (struct Guest *gst)
+{
+  struct Place *plc = &gst->place;
+
+  if (NULL == gst->tmit_handle)
+  {
+    struct GNUNET_PSYC_MessageMethod *
+      pmeth = psyc_transmit_queue_next_method (plc);
+    if (NULL == pmeth)
+      return GNUNET_SYSERR;
+
+    gst->tmit_handle = (void *) &gst->tmit_handle;
+    struct GNUNET_PSYC_SlaveTransmitHandle *
+      tmit_handle = GNUNET_PSYC_slave_transmit (gst->slave, (const char *) 
&pmeth[1],
+                                                 &guest_transmit_notify_mod,
+                                                 &guest_transmit_notify_data, 
gst,
+                                                 pmeth->flags);
+    if (NULL != gst->tmit_handle)
+      gst->tmit_handle = tmit_handle;
+    GNUNET_free (pmeth);
+  }
+  else
+  {
+    GNUNET_PSYC_slave_transmit_resume (gst->tmit_handle);
+  }
+  return GNUNET_OK;
+}
+
+
+/**
+ * Transmit a message to PSYC.
+ */
+static int
+psyc_transmit_message (struct Place *plc)
+{
+  return
+    (plc->is_host)
+    ? psyc_master_transmit_message ((struct Host *) plc)
+    : psyc_slave_transmit_message ((struct Guest *) plc);
+}
+
+
+/**
+ * Queue message parts for sending to PSYC.
+ *
+ * @param plc          Place to send to.
+ * @param client       Client the message originates from.
+ * @param data_size    Size of @a data.
+ * @param data         Concatenated message parts.
+ * @param first_ptype  First message part type in @a data.
+ * @param last_ptype   Last message part type in @a data.
+ */
+static struct MessageTransmitQueue *
+psyc_transmit_queue_message (struct Place *plc,
+                             struct GNUNET_SERVICE_Client *client,
+                             size_t data_size,
+                             const void *data,
+                             uint16_t first_ptype, uint16_t last_ptype,
+                             struct MessageTransmitQueue *tmit_msg)
+{
+  if (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_METHOD == first_ptype)
+  {
+    tmit_msg = GNUNET_malloc (sizeof (*tmit_msg));
+    GNUNET_CONTAINER_DLL_insert_tail (plc->tmit_msgs_head, 
plc->tmit_msgs_tail, tmit_msg);
+  }
+  else if (NULL == tmit_msg)
+  {
+    return NULL;
+  }
+
+  struct FragmentTransmitQueue *
+    tmit_frag = GNUNET_malloc (sizeof (*tmit_frag) + data_size);
+  GNUNET_memcpy (&tmit_frag[1], data, data_size);
+  tmit_frag->next_part = (struct GNUNET_MessageHeader *) &tmit_frag[1];
+  tmit_frag->client = client;
+  tmit_frag->size = data_size;
+
+  GNUNET_CONTAINER_DLL_insert_tail (tmit_msg->frags_head, 
tmit_msg->frags_tail, tmit_frag);
+  tmit_msg->client = client;
+  return tmit_msg;
+}
+
+
+///**
+// * Cancel transmission of current message to PSYC.
+// *
+// * @param plc          Place to send to.
+// * @param client  Client the message originates from.
+// */
+//static void
+//psyc_transmit_cancel (struct Place *plc, struct GNUNET_SERVICE_Client 
*client)
+//{
+//  uint16_t type = GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL;
+//
+//  struct GNUNET_MessageHeader msg;
+//  msg.size = htons (sizeof (msg));
+//  msg.type = htons (type);
+//
+//  psyc_transmit_queue_message (plc, client, sizeof (msg), &msg, type, type, 
NULL);
+//  psyc_transmit_message (plc);
+//
+//  /* FIXME: cleanup */
+//}
+
+
+static int
+check_client_psyc_message (void *cls,
+                           const struct GNUNET_MessageHeader *msg)
+{
+  return GNUNET_OK;
+}
+
+
+/**
+ * Handle an incoming message from a client, to be transmitted to the place.
+ */
+static void
+handle_client_psyc_message (void *cls,
+                            const struct GNUNET_MessageHeader *msg)
+{
+  struct Client *c = cls;
+  struct GNUNET_SERVICE_Client *client = c->client;
+  struct Place *plc = c->place;
+  int ret;
+
+  if (NULL == plc)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "received PSYC message for non-existing client %p\n",
+                client);
+    GNUNET_break (0);
+    GNUNET_SERVICE_client_drop (client);
+    return;
+  }
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%p Received message of type %d from client.\n", plc, ntohs 
(msg->type));
+  GNUNET_PSYC_log_message (GNUNET_ERROR_TYPE_DEBUG, msg);
+
+  if (GNUNET_YES != plc->is_ready)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "%p Place is not ready yet, disconnecting client.\n", plc);
+    GNUNET_break (0);
+    GNUNET_SERVICE_client_drop (client);
+    return;
+  }
+
+  uint16_t size = ntohs (msg->size);
+  uint16_t psize = size - sizeof (*msg);
+  if (psize < sizeof (struct GNUNET_MessageHeader)
+      || GNUNET_MULTICAST_FRAGMENT_MAX_PAYLOAD < psize)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "%p Received message with invalid payload size (%u) from 
client.\n",
+                plc, psize);
+    GNUNET_break (0);
+    GNUNET_SERVICE_client_drop (client);
+    return;
+  }
+
+  uint16_t first_ptype = 0;
+  uint16_t last_ptype = 0;
+  if (GNUNET_SYSERR ==
+      GNUNET_PSYC_receive_check_parts (psize, (const char *) &msg[1],
+                                       &first_ptype, &last_ptype))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "%p Received invalid message part from client.\n", plc);
+    GNUNET_break (0);
+    GNUNET_SERVICE_client_drop (client);
+    return;
+  }
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%p Received message with first part type %u and last part type 
%u.\n",
+              plc, first_ptype, last_ptype);
+
+  c->tmit_msg
+    = psyc_transmit_queue_message (plc, client, psize, &msg[1],
+                                   first_ptype, last_ptype, c->tmit_msg);
+  if (NULL != c->tmit_msg)
+  {
+    if (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END <= last_ptype)
+      c->tmit_msg = NULL;
+    ret = psyc_transmit_message (plc);
+  }
+  else
+  {
+    ret = GNUNET_SYSERR;
+  }
+  if (GNUNET_OK != ret)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "%p Received invalid message part from client.\n", plc);
+    GNUNET_break (0);
+    GNUNET_SERVICE_client_drop (client);
+    return;
+  }
+  GNUNET_SERVICE_client_continue (client);
+}
+
+
+/**
+ * A historic message arrived from PSYC.
+ */
+static void
+psyc_recv_history_message (void *cls, const struct GNUNET_PSYC_MessageHeader 
*msg)
+{
+  struct OperationClosure *opcls = cls;
+  struct Client *c = opcls->client;
+  struct Place *plc = c->place;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%p Received historic message #%" PRId64 " (flags: %x)\n",
+              plc, GNUNET_ntohll (msg->message_id), ntohl (msg->flags));
+
+  uint16_t size = ntohs (msg->header.size);
+
+  struct GNUNET_OperationResultMessage *
+    res = GNUNET_malloc (sizeof (*res) + size);
+  res->header.size = htons (sizeof (*res) + size);
+  res->header.type = htons (GNUNET_MESSAGE_TYPE_PSYC_HISTORY_RESULT);
+  res->op_id = opcls->op_id;
+  res->result_code = GNUNET_htonll (GNUNET_OK);
+
+  GNUNET_memcpy (&res[1], msg, size);
+
+  /** @todo FIXME: send only to requesting client */
+  place_send_msg (plc, GNUNET_MQ_msg_copy (&res->header));
+
+  GNUNET_free (res);
+}
+
+
+/**
+ * Result of message history replay from PSYC.
+ */
+static void
+psyc_recv_history_result (void *cls, int64_t result,
+                          const void *err_msg, uint16_t err_msg_size)
+{
+  struct OperationClosure *opcls = cls;
+  struct Client *c = opcls->client;
+  struct Place *plc = c->place;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%p History replay #%" PRIu64 ": "
+              "PSYCstore returned %" PRId64 " (%.*s)\n",
+              plc, GNUNET_ntohll (opcls->op_id), result,
+              err_msg_size, (const char *) err_msg);
+
+  // FIXME: place might have been destroyed
+  client_send_result (c->client, opcls->op_id, result, err_msg, err_msg_size);
+}
+
+
+static int
+check_client_history_replay (void *cls,
+                             const struct GNUNET_PSYC_HistoryRequestMessage 
*req)
+{
+  return GNUNET_OK;
+}
+
+
+/**
+ * Client requests channel history.
+ */
+static void
+handle_client_history_replay (void *cls,
+                              const struct GNUNET_PSYC_HistoryRequestMessage 
*req)
+{
+  struct Client *c = cls;
+  struct GNUNET_SERVICE_Client *client = c->client;
+  struct Place *plc = c->place;
+  if (NULL == plc)
+  {
+    GNUNET_break (0);
+    GNUNET_SERVICE_client_drop (client);
+    return;
+  }
+
+  uint16_t size = ntohs (req->header.size);
+  const char *method_prefix = (const char *) &req[1];
+
+  if (size < sizeof (*req) + 1
+      || '\0' != method_prefix[size - sizeof (*req) - 1])
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "%p History replay #%" PRIu64 ": "
+                "invalid method prefix. size: %u < %zu?\n",
+                plc, GNUNET_ntohll (req->op_id), size, sizeof (*req) + 1);
+    GNUNET_break (0);
+    GNUNET_SERVICE_client_drop (client);
+    return;
+  }
+
+  struct OperationClosure *opcls = GNUNET_malloc (sizeof (*opcls));
+  opcls->client = c;
+  opcls->op_id = req->op_id;
+  opcls->flags = ntohl (req->flags);
+
+  if (0 == req->message_limit)
+    GNUNET_PSYC_channel_history_replay (plc->channel,
+                                        GNUNET_ntohll (req->start_message_id),
+                                        GNUNET_ntohll (req->end_message_id),
+                                        method_prefix, opcls->flags,
+                                        psyc_recv_history_message, NULL,
+                                        psyc_recv_history_result, opcls);
+  else
+    GNUNET_PSYC_channel_history_replay_latest (plc->channel,
+                                               GNUNET_ntohll 
(req->message_limit),
+                                               method_prefix, opcls->flags,
+                                               psyc_recv_history_message, NULL,
+                                               psyc_recv_history_result, 
opcls);
+
+  GNUNET_SERVICE_client_continue (client);
+}
+
+
+/**
+ * A state variable part arrived from PSYC.
+ */
+void
+psyc_recv_state_var (void *cls,
+                     const struct GNUNET_MessageHeader *mod,
+                     const char *name,
+                     const void *value,
+                     uint32_t value_size,
+                     uint32_t full_value_size)
+{
+  struct GNUNET_OperationResultMessage *result_msg;
+  struct GNUNET_MQ_Envelope *env;
+  struct OperationClosure *opcls = cls;
+  struct Client *c = opcls->client;
+  struct Place *plc = c->place;
+  uint16_t size = ntohs (mod->size);
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%p Received state variable %s from PSYC\n",
+              plc, name);
+  env = GNUNET_MQ_msg_extra (result_msg,
+                             size,
+                             GNUNET_MESSAGE_TYPE_PSYC_STATE_RESULT);
+  result_msg->op_id = opcls->op_id;
+  result_msg->result_code = GNUNET_htonll (GNUNET_OK);
+  GNUNET_memcpy (&result_msg[1], mod, size);
+  /** @todo FIXME: send only to requesting client */
+  place_send_msg (plc, env);
+}
+
+
+/**
+ * Result of retrieving state variable from PSYC.
+ */
+static void
+psyc_recv_state_result (void *cls, int64_t result,
+                        const void *err_msg, uint16_t err_msg_size)
+{
+  struct OperationClosure *opcls = cls;
+  struct Client *c = opcls->client;
+  struct Place *plc = c->place;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%p State get #%" PRIu64 ": "
+              "PSYCstore returned %" PRId64 " (%.*s)\n",
+              plc, GNUNET_ntohll (opcls->op_id), result,
+              err_msg_size, (const char *) err_msg);
+
+  // FIXME: place might have been destroyed
+  client_send_result (c->client, opcls->op_id, result, err_msg, err_msg_size);
+}
+
+
+static int
+check_client_state_get (void *cls,
+                        const struct GNUNET_PSYC_StateRequestMessage *req)
+{
+  return GNUNET_OK;
+}
+
+
+/**
+ * Client requests channel history.
+ */
+static void
+handle_client_state_get (void *cls,
+                         const struct GNUNET_PSYC_StateRequestMessage *req)
+{
+  struct Client *c = cls;
+  struct GNUNET_SERVICE_Client *client = c->client;
+  struct Place *plc = c->place;
+  if (NULL == plc)
+  {
+    GNUNET_break (0);
+    GNUNET_SERVICE_client_drop (client);
+    return;
+  }
+
+  uint16_t size = ntohs (req->header.size);
+  const char *name = (const char *) &req[1];
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%p State get #%" PRIu64 ": %s\n",
+              plc, GNUNET_ntohll (req->op_id), name);
+
+  if (size < sizeof (*req) + 1
+      || '\0' != name[size - sizeof (*req) - 1])
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "%p State get #%" PRIu64 ": "
+                "invalid name. size: %u < %zu?\n",
+                plc, GNUNET_ntohll (req->op_id), size, sizeof (*req) + 1);
+    GNUNET_break (0);
+    GNUNET_SERVICE_client_drop (client);
+    return;
+  }
+
+  struct OperationClosure *opcls = GNUNET_malloc (sizeof (*opcls));
+  opcls->client = c;
+  opcls->op_id = req->op_id;
+
+  switch (ntohs (req->header.type))
+  {
+  case GNUNET_MESSAGE_TYPE_PSYC_STATE_GET:
+      GNUNET_PSYC_channel_state_get (plc->channel, name,
+                                     psyc_recv_state_var,
+                                     psyc_recv_state_result, opcls);
+      break;
+
+  case GNUNET_MESSAGE_TYPE_PSYC_STATE_GET_PREFIX:
+      GNUNET_PSYC_channel_state_get_prefix (plc->channel, name,
+                                            psyc_recv_state_var,
+                                            psyc_recv_state_result, opcls);
+      break;
+
+  default:
+      GNUNET_assert (0);
+  }
+
+  GNUNET_SERVICE_client_continue (client);
+}
+
+
+#define check_client_state_get_prefix check_client_state_get
+#define handle_client_state_get_prefix handle_client_state_get
+
+
+static void
+namestore_recv_records_store_result (void *cls, int32_t result,
+                                     const char *err_msg)
+{
+  struct OperationClosure *opcls = cls;
+  struct Client *c = opcls->client;
+
+  // FIXME: client might have been disconnected
+  client_send_result (c->client, opcls->op_id, result, err_msg,
+                      (NULL != err_msg) ? strlen (err_msg) : 0);
+  GNUNET_free (opcls);
+}
+
+
+static int
+check_client_zone_add_place (void *cls,
+                             const struct ZoneAddPlaceRequest *preq)
+{
+  return GNUNET_OK;
+}
+
+
+/**
+ * Handle request to add PLACE record to GNS zone.
+ */
+static void
+handle_client_zone_add_place (void *cls,
+                              const struct ZoneAddPlaceRequest *preq)
+{
+  struct Client *c = cls;
+  struct GNUNET_SERVICE_Client *client = c->client;
+
+  uint16_t remaining = ntohs (preq->header.size) - sizeof (*preq);
+  const char *p = (const char *) &preq[1];
+  const char *name = NULL, *password = NULL;
+  uint16_t offset = GNUNET_STRINGS_buffer_tokenize (p, remaining, 2,
+                                                    &name, &password);
+  remaining -= offset;
+  p += offset;
+  const struct GNUNET_PeerIdentity *
+    relays = (const struct GNUNET_PeerIdentity *) p;
+  uint16_t relay_size = ntohl (preq->relay_count) * sizeof (*relays);
+
+  if (0 == offset || remaining != relay_size)
+  {
+    GNUNET_break (0);
+    client_send_result (client, preq->op_id, GNUNET_SYSERR, NULL, 0);
+    GNUNET_SERVICE_client_drop (client);
+    return;
+  }
+
+  struct GNUNET_GNSRECORD_Data rd = { };
+  rd.record_type = GNUNET_GNSRECORD_TYPE_PLACE;
+  rd.flags = GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION;
+  rd.expiration_time = GNUNET_ntohll (preq->expiration_time);
+
+  struct GNUNET_GNSRECORD_PlaceData *
+    rec = GNUNET_malloc (sizeof (*rec) + relay_size);
+  rec->place_pub_key = preq->place_pub_key;
+  rec->origin = this_peer;
+  rec->relay_count = preq->relay_count;
+  GNUNET_memcpy (&rec[1], relays, relay_size);
+
+  rd.data = rec;
+  rd.data_size = sizeof (*rec) + relay_size;
+
+  struct GNUNET_HashCode ego_pub_hash;
+  GNUNET_CRYPTO_hash (&preq->ego_pub_key, sizeof (preq->ego_pub_key), 
&ego_pub_hash);
+  struct Ego *ego = GNUNET_CONTAINER_multihashmap_get (egos, &ego_pub_hash);
+  if (NULL == ego)
+  {
+    client_send_result (client, preq->op_id, GNUNET_SYSERR, NULL, 0);
+  }
+  else
+  {
+    struct OperationClosure *opcls = GNUNET_malloc (sizeof (*opcls));
+    opcls->client = c;
+    opcls->op_id = preq->op_id;
+    GNUNET_NAMESTORE_records_store (namestore, &ego->key,
+                                    name, 1, &rd,
+                                    namestore_recv_records_store_result, 
opcls);
+    /** @todo refresh stored records later */
+  }
+  GNUNET_SERVICE_client_continue (client);
+}
+
+
+static int
+check_client_zone_add_nym (void *cls,
+                           const struct ZoneAddNymRequest *nreq)
+{
+  return GNUNET_OK;
+}
+
+
+/**
+ * Handle request to add PLACE record to GNS zone.
+ */
+static void
+handle_client_zone_add_nym (void *cls,
+                            const struct ZoneAddNymRequest *nreq)
+{
+  struct Client *c = cls;
+  struct GNUNET_SERVICE_Client *client = c->client;
+
+  uint16_t name_size = ntohs (nreq->header.size) - sizeof (*nreq);
+  const char *name = NULL;
+  uint16_t offset = GNUNET_STRINGS_buffer_tokenize ((const char *) &nreq[1],
+                                                    name_size, 1, &name);
+  if (0 == offset || offset != name_size)
+  {
+    GNUNET_break (0);
+    client_send_result (client, nreq->op_id, GNUNET_SYSERR, NULL, 0);
+    GNUNET_SERVICE_client_continue (client);
+    return;
+  }
+
+  struct GNUNET_GNSRECORD_Data rd = { };
+  rd.record_type = GNUNET_GNSRECORD_TYPE_PKEY;
+  rd.flags = GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION;
+  rd.expiration_time = GNUNET_ntohll (nreq->expiration_time);
+  rd.data = &nreq->nym_pub_key;
+  rd.data_size = sizeof (nreq->nym_pub_key);
+
+  struct GNUNET_HashCode ego_pub_hash;
+  GNUNET_CRYPTO_hash (&nreq->ego_pub_key, sizeof (nreq->ego_pub_key), 
&ego_pub_hash);
+  struct Ego *ego = GNUNET_CONTAINER_multihashmap_get (egos, &ego_pub_hash);
+  if (NULL == ego)
+  {
+    client_send_result (client, nreq->op_id, GNUNET_SYSERR, NULL, 0);
+  }
+  else
+  {
+    struct OperationClosure *opcls = GNUNET_malloc (sizeof (*opcls));
+    opcls->client = c;
+    opcls->op_id = nreq->op_id;
+    GNUNET_NAMESTORE_records_store (namestore, &ego->key,
+                                    name, 1, &rd,
+                                    namestore_recv_records_store_result, 
opcls);
+    /** @todo refresh stored records later */
+  }
+  GNUNET_SERVICE_client_continue (client);
+}
+
+
+const char *
+path_basename (const char *path)
+{
+  const char *basename = strrchr (path, DIR_SEPARATOR);
+  if (NULL != basename)
+    basename++;
+
+  if (NULL == basename || '\0' == *basename)
+    return NULL;
+
+  return basename;
+}
+
+
+struct PlaceLoadClosure
+{
+  const char *app_id;
+  const char *ego_pub_str;
+};
+
+
+/** Load a place file */
+int
+file_place_load (void *cls, const char *place_filename)
+{
+  struct PlaceLoadClosure *plcls = cls;
+
+  const char *place_pub_str = path_basename (place_filename);
+  if (NULL == place_pub_str)
+  {
+    GNUNET_break (0);
+    return GNUNET_OK;
+  }
+
+  char *filename = NULL;
+  GNUNET_asprintf (&filename, "%s%c" "%s%c" "%s%c" "%s",
+                   dir_social, DIR_SEPARATOR,
+                   "places", DIR_SEPARATOR,
+                   plcls->ego_pub_str, DIR_SEPARATOR,
+                   place_pub_str);
+
+  uint64_t file_size = 0;
+  if (GNUNET_OK !=
+      GNUNET_DISK_file_size (filename, &file_size, GNUNET_YES, GNUNET_YES)
+      || file_size < sizeof (struct PlaceEnterRequest))
+  {
+    GNUNET_free (filename);
+    return GNUNET_OK;
+  }
+
+  struct PlaceEnterRequest *ereq = GNUNET_malloc (file_size);
+  ssize_t read_size = GNUNET_DISK_fn_read (filename, ereq, file_size);
+  GNUNET_free (filename);
+  if (read_size < 0 || read_size < sizeof (*ereq))
+  {
+    GNUNET_free (ereq);
+    return GNUNET_OK;
+  }
+
+  uint16_t ereq_size = ntohs (ereq->header.size);
+  if (read_size != ereq_size)
+  {
+    GNUNET_free (ereq);
+    return GNUNET_OK;
+  }
+
+  switch (ntohs (ereq->header.type))
+  {
+  case GNUNET_MESSAGE_TYPE_SOCIAL_HOST_ENTER:
+    if (ereq_size < sizeof (struct HostEnterRequest))
+    {
+      GNUNET_free (ereq);
+      return GNUNET_OK;
+    }
+    struct HostEnterRequest *hreq = (struct HostEnterRequest *) ereq;
+    host_enter (hreq, NULL);
+    break;
+
+  case GNUNET_MESSAGE_TYPE_SOCIAL_GUEST_ENTER:
+    if (ereq_size < sizeof (struct GuestEnterRequest))
+    {
+      GNUNET_free (ereq);
+      return GNUNET_OK;
+    }
+    struct GuestEnterRequest *greq = (struct GuestEnterRequest *) ereq;
+    guest_enter (greq, NULL);
+    break;
+
+  default:
+    GNUNET_free (ereq);
+    return GNUNET_OK;
+  }
+
+  if (GNUNET_SYSERR == app_place_add (plcls->app_id, ereq))
+  {
+    GNUNET_assert (0);
+  }
+  GNUNET_free (ereq);
+  return GNUNET_OK;
+}
+
+
+/**
+ * Read @e place_pub_str entries in @a dir_ego
+ *
+ * @param dir_ego
+ *        Data directory of an application ego.
+ *        $GNUNET_DATA_HOME/social/apps/$app_id/$ego_pub_str/
+ */
+int
+scan_app_ego_dir (void *cls, const char *dir_ego)
+{
+  struct PlaceLoadClosure *plcls = cls;
+  plcls->ego_pub_str = path_basename (dir_ego);
+
+  if (NULL != plcls->ego_pub_str)
+    GNUNET_DISK_directory_scan (dir_ego, file_place_load, plcls);
+
+  return GNUNET_OK;
+}
+
+/**
+ * Read @e ego_pub_str entries in @a dir_app
+ *
+ * @param dir_app
+ *        Data directory of an application.
+ *        $GNUNET_DATA_HOME/social/apps/$app_id/
+ */
+int
+scan_app_dir (void *cls, const char *dir_app)
+{
+  if (GNUNET_YES != GNUNET_DISK_directory_test (dir_app, GNUNET_YES))
+    return GNUNET_OK;
+
+  struct PlaceLoadClosure plcls;
+  plcls.app_id = path_basename (dir_app);
+
+  if (NULL != plcls.app_id)
+    GNUNET_DISK_directory_scan (dir_app, scan_app_ego_dir, &plcls);
+
+  return GNUNET_OK;
+}
+
+
+static void
+identity_recv_ego (void *cls, struct GNUNET_IDENTITY_Ego *id_ego,
+                   void **ctx, const char *name)
+{
+  if (NULL == id_ego) // end of initial list of egos
+    return;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "social service received ego %s\n",
+              name);
+
+  struct GNUNET_CRYPTO_EcdsaPublicKey ego_pub_key;
+  GNUNET_IDENTITY_ego_get_public_key (id_ego, &ego_pub_key);
+
+  struct GNUNET_HashCode ego_pub_hash;
+  GNUNET_CRYPTO_hash (&ego_pub_key, sizeof (ego_pub_key), &ego_pub_hash);
+
+  struct Ego *ego = GNUNET_CONTAINER_multihashmap_get (egos, &ego_pub_hash);
+  if (NULL == ego && NULL == name)
+  {
+    // an ego that is none of our business has been deleted
+    return;
+  }
+  if (NULL != ego)
+  {
+    // one of our egos has been changed
+    GNUNET_free (ego->name);
+    if (NULL == name)
+    {
+      // one of our egos has been deleted
+      GNUNET_CONTAINER_multihashmap_remove (egos, &ego_pub_hash, ego);
+      GNUNET_free (ego);
+      return;
+    }
+  }
+  else
+  {
+    ego = GNUNET_malloc (sizeof (*ego));
+  }
+  ego->key = *(GNUNET_IDENTITY_ego_get_private_key (id_ego));
+  size_t name_size = strlen (name) + 1;
+  ego->name = GNUNET_malloc (name_size);
+  GNUNET_memcpy (ego->name, name, name_size);
+
+  GNUNET_CONTAINER_multihashmap_put (egos, &ego_pub_hash, ego,
+                                     
GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE);
+
+  // FIXME: notify clients about changed ego
+}
+
+
+/**
+ * Initialize the PSYC service.
+ *
+ * @param cls Closure.
+ * @param server The initialized server.
+ * @param c Configuration to use.
+ */
+static void
+run (void *cls,
+     const struct GNUNET_CONFIGURATION_Handle *c,
+     struct GNUNET_SERVICE_Handle *svc)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "starting social service\n");
+
+  cfg = c;
+  service = svc;
+  GNUNET_CRYPTO_get_peer_identity (cfg, &this_peer);
+
+  hosts = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES);
+  guests = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES);
+  place_guests = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO);
+
+  egos = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO);
+  apps = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO);
+  places = GNUNET_CONTAINER_multihashmap_create(1, GNUNET_NO);
+  apps_places = GNUNET_CONTAINER_multihashmap_create(1, GNUNET_NO);
+  //places_apps = GNUNET_CONTAINER_multihashmap_create(1, GNUNET_NO);
+
+  id = GNUNET_IDENTITY_connect (cfg, &identity_recv_ego, NULL);
+  gns = GNUNET_GNS_connect (cfg);
+  namestore = GNUNET_NAMESTORE_connect (cfg);
+  stats = GNUNET_STATISTICS_create ("social", cfg);
+
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_get_value_filename (cfg, "social", "DATA_HOME",
+                                               &dir_social))
+  {
+    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+                               "social", "DATA_HOME");
+    GNUNET_break (0);
+    return;
+  }
+  GNUNET_asprintf (&dir_places, "%s%c%s",
+                   dir_social, DIR_SEPARATOR, "places");
+  GNUNET_asprintf (&dir_apps, "%s%c%s",
+                   dir_social, DIR_SEPARATOR, "apps");
+
+  GNUNET_DISK_directory_scan (dir_apps, scan_app_dir, NULL);
+
+  GNUNET_SCHEDULER_add_shutdown (shutdown_task, NULL);
+}
+
+
+/**
+ * Define "main" method using service macro.
+ */
+GNUNET_SERVICE_MAIN
+("social",
+ GNUNET_SERVICE_OPTION_NONE,
+ run,
+ client_notify_connect,
+ client_notify_disconnect,
+ NULL,
+ GNUNET_MQ_hd_var_size (client_host_enter,
+                        GNUNET_MESSAGE_TYPE_SOCIAL_HOST_ENTER,
+                        struct HostEnterRequest,
+                        NULL),
+ GNUNET_MQ_hd_var_size (client_guest_enter,
+                        GNUNET_MESSAGE_TYPE_SOCIAL_GUEST_ENTER,
+                        struct GuestEnterRequest,
+                        NULL),
+ GNUNET_MQ_hd_var_size (client_guest_enter_by_name,
+                        GNUNET_MESSAGE_TYPE_SOCIAL_GUEST_ENTER_BY_NAME,
+                        struct GuestEnterByNameRequest,
+                        NULL),
+ GNUNET_MQ_hd_var_size (client_join_decision,
+                        GNUNET_MESSAGE_TYPE_PSYC_JOIN_DECISION,
+                        struct GNUNET_PSYC_JoinDecisionMessage,
+                        NULL),
+ GNUNET_MQ_hd_var_size (client_psyc_message,
+                        GNUNET_MESSAGE_TYPE_PSYC_MESSAGE,
+                        struct GNUNET_MessageHeader,
+                        NULL),
+ GNUNET_MQ_hd_var_size (client_history_replay,
+                        GNUNET_MESSAGE_TYPE_PSYC_HISTORY_REPLAY,
+                        struct GNUNET_PSYC_HistoryRequestMessage,
+                        NULL),
+ GNUNET_MQ_hd_var_size (client_state_get,
+                        GNUNET_MESSAGE_TYPE_PSYC_STATE_GET,
+                        struct GNUNET_PSYC_StateRequestMessage,
+                        NULL),
+ GNUNET_MQ_hd_var_size (client_state_get_prefix,
+                        GNUNET_MESSAGE_TYPE_PSYC_STATE_GET_PREFIX,
+                        struct GNUNET_PSYC_StateRequestMessage,
+                        NULL),
+ GNUNET_MQ_hd_var_size (client_zone_add_place,
+                        GNUNET_MESSAGE_TYPE_SOCIAL_ZONE_ADD_PLACE,
+                        struct ZoneAddPlaceRequest,
+                        NULL),
+ GNUNET_MQ_hd_var_size (client_zone_add_nym,
+                        GNUNET_MESSAGE_TYPE_SOCIAL_ZONE_ADD_NYM,
+                        struct ZoneAddNymRequest,
+                        NULL),
+ GNUNET_MQ_hd_var_size (client_app_connect,
+                        GNUNET_MESSAGE_TYPE_SOCIAL_APP_CONNECT,
+                        struct AppConnectRequest,
+                        NULL),
+ GNUNET_MQ_hd_fixed_size (client_app_detach,
+                          GNUNET_MESSAGE_TYPE_SOCIAL_APP_DETACH,
+                          struct AppDetachRequest,
+                          NULL),
+ GNUNET_MQ_hd_fixed_size (client_place_leave,
+                          GNUNET_MESSAGE_TYPE_SOCIAL_PLACE_LEAVE,
+                          struct GNUNET_MessageHeader,
+                          NULL),
+ GNUNET_MQ_hd_var_size (client_msg_proc_set,
+                        GNUNET_MESSAGE_TYPE_SOCIAL_MSG_PROC_SET,
+                        struct MsgProcRequest,
+                        NULL),
+ GNUNET_MQ_hd_fixed_size (client_msg_proc_clear,
+                          GNUNET_MESSAGE_TYPE_SOCIAL_MSG_PROC_CLEAR,
+                          struct GNUNET_MessageHeader,
+                          NULL));
+
+/* end of gnunet-service-social.c */
diff --git a/src/social/gnunet-social.c b/src/social/gnunet-social.c
new file mode 100644
index 0000000..14701bf
--- /dev/null
+++ b/src/social/gnunet-social.c
@@ -0,0 +1,1411 @@
+/*
+     This file is part of GNUnet.
+     Copyright (C) 2016 GNUnet e.V.
+
+     GNUnet is free software: you can redistribute it and/or modify it
+     under the terms of the GNU Affero General Public License as published
+     by the Free Software Foundation, either version 3 of the License,
+     or (at your option) any later version.
+
+     GNUnet 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
+     Affero General Public License for more details.
+    
+     You should have received a copy of the GNU Affero General Public License
+     along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+     SPDX-License-Identifier: AGPL3.0-or-later
+*/
+
+/**
+ * CLI tool to interact with the social service.
+ *
+ * @author Gabor X Toth
+ */
+
+#include <inttypes.h>
+
+#include "platform.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_social_service.h"
+
+#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
+
+#define DATA2ARG(data) data, sizeof (data)
+
+/* operations corresponding to API calls */
+
+/** --status */
+static int op_status;
+
+/** --host-enter */
+static int op_host_enter;
+
+/** --host-reconnect */
+static int op_host_reconnect;
+
+/** --host-leave */
+static int op_host_leave;
+
+/** --host-announce */
+static int op_host_announce;
+
+/** --host-assign */
+static int op_host_assign;
+
+/** --guest-enter */
+static int op_guest_enter;
+
+/** --guest-reconnect */
+static int op_guest_reconnect;
+
+/** --guest-leave */
+static int op_guest_leave;
+
+/** --guest-talk */
+static int op_guest_talk;
+
+/** --replay */
+static int op_replay;
+
+/** --replay-latest */
+static int op_replay_latest;
+
+/** --look-at */
+static int op_look_at;
+
+/** --look-for */
+static int op_look_for;
+
+
+/* options */
+
+/** --app */
+static char *opt_app = "cli";
+
+/** --place */
+static char *opt_place;
+
+/** --ego */
+static char *opt_ego;
+
+/** --gns */
+static char *opt_gns;
+
+/** --peer */
+static char *opt_peer;
+
+/** --follow */
+static int opt_follow;
+
+/** --welcome */
+static int opt_welcome;
+
+/** --deny */
+static int opt_deny;
+
+/** --method */
+static char *opt_method;
+
+/** --data */
+// FIXME: should come from STDIN
+static char *opt_data;
+
+/** --name */
+static char *opt_name;
+
+/** --start */
+static unsigned long long opt_start;
+
+/** --until */
+static unsigned long long opt_until;
+
+/** --limit */
+static unsigned long long opt_limit;
+
+
+/* global vars */
+
+/** exit code */
+static int ret = 1;
+
+/** are we waiting for service to close our connection */
+static char is_disconnecting = 0;
+
+/** Task handle for timeout termination. */
+struct GNUNET_SCHEDULER_Task *timeout_task;
+
+const struct GNUNET_CONFIGURATION_Handle *cfg;
+
+struct GNUNET_PeerIdentity peer, this_peer;
+
+struct GNUNET_SOCIAL_App *app;
+
+/** public key of connected place */
+struct GNUNET_CRYPTO_EddsaPublicKey place_pub_key;
+
+struct GNUNET_PSYC_Slicer *slicer;
+
+struct GNUNET_SOCIAL_Ego *ego;
+struct GNUNET_CRYPTO_EcdsaPublicKey ego_pub_key;
+
+struct GNUNET_SOCIAL_Host *hst;
+struct GNUNET_SOCIAL_Guest *gst;
+struct GNUNET_SOCIAL_Place *plc;
+
+const char *method_received;
+
+
+/* DISCONNECT */
+
+
+/**
+ * Callback called after the host or guest place disconnected.
+ */
+static void
+disconnected (void *cls)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "disconnected()\n");
+  GNUNET_SCHEDULER_shutdown ();
+}
+
+
+/**
+ * Callback called after the application disconnected.
+ */
+static void
+app_disconnected (void *cls)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "app_disconnected()\n");
+  if (hst || gst)
+  {
+    if (hst)
+    {
+      GNUNET_SOCIAL_host_disconnect (hst, disconnected, NULL);
+    }
+    if (gst)
+    {
+      GNUNET_SOCIAL_guest_disconnect (gst, disconnected, NULL);
+    }
+  }
+  else
+  {
+    GNUNET_SCHEDULER_shutdown ();
+  }
+}
+
+
+/**
+ * Disconnect from connected GNUnet services.
+ */
+static void
+disconnect ()
+{
+  // handle that we get called several times from several places, but should 
we?
+  if (!is_disconnecting++) {
+    GNUNET_SOCIAL_app_disconnect (app, app_disconnected, NULL);
+  }
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "disconnect() called for the #%d 
time\n", is_disconnecting);
+}
+
+
+static void
+scheduler_shutdown (void *cls)
+{
+  disconnect ();
+}
+
+
+/**
+ * Callback called when the program failed to finish the requested operation 
in time.
+ */
+static void
+timeout (void *cls)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "timeout()\n");
+  disconnect ();
+}
+
+static void
+schedule_success (void *cls)
+{
+  ret = 0;
+  disconnect ();
+}
+
+
+static void
+schedule_fail (void *cls)
+{
+  disconnect ();
+}
+
+
+/**
+ * Schedule exit with success result.
+ */
+static void
+exit_success ()
+{
+  if (timeout_task != NULL)
+  {
+    GNUNET_SCHEDULER_cancel (timeout_task);
+    timeout_task = NULL;
+  }
+  GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, schedule_success, 
NULL);
+}
+
+
+/**
+ * Schedule exit with failure result.
+ */
+static void
+exit_fail ()
+{
+  if (timeout_task != NULL)
+  {
+    GNUNET_SCHEDULER_cancel (timeout_task);
+    timeout_task = NULL;
+  }
+  GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, schedule_fail, NULL);
+}
+
+
+/* LEAVE */
+
+
+/**
+ * Callback notifying about the host has left and stopped hosting the place.
+ *
+ * This also indicates the end of the connection to the service.
+ */
+static void
+host_left (void *cls)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+              "The host has left the place.\n");
+  exit_success ();
+}
+
+
+/**
+ * Leave a place permanently and stop hosting a place.
+ */
+static void
+host_leave ()
+{
+  GNUNET_SOCIAL_host_leave (hst, NULL, host_left, NULL);
+  hst = NULL;
+  plc = NULL;
+}
+
+
+/**
+ * Callback notifying about the guest has left the place.
+ *
+ * This also indicates the end of the connection to the service.
+ */
+static void
+guest_left (void *cls)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+              "Guest has left the place.\n");
+}
+
+
+/**
+ * Leave a place permanently as guest.
+ */
+static void
+guest_leave ()
+{
+  struct GNUNET_PSYC_Environment *env = GNUNET_PSYC_env_create ();
+  // FIXME: wrong use of vars
+  GNUNET_PSYC_env_add (env, GNUNET_PSYC_OP_SET,
+                       "_message", DATA2ARG ("Leaving."));
+  GNUNET_SOCIAL_guest_leave (gst, env, guest_left, NULL);
+  GNUNET_PSYC_env_destroy (env);
+  gst = NULL;
+  plc = NULL;
+}
+
+
+/* ANNOUNCE / ASSIGN / TALK */
+
+
+struct TransmitClosure
+{
+  const char *data;
+  size_t size;
+} tmit;
+
+
+/**
+ * Callback notifying about available buffer space to write message data
+ * when transmitting messages using host_announce() or guest_talk()
+ */
+static int
+notify_data (void *cls, uint16_t *data_size, void *data)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Transmit notify data: %u bytes available\n",
+              *data_size);
+
+  struct TransmitClosure *tmit = cls;
+  uint16_t size = tmit->size < *data_size ? tmit->size : *data_size;
+  *data_size = size;
+  GNUNET_memcpy (data, tmit->data, size);
+
+  tmit->size -= size;
+  tmit->data += size;
+
+  if (0 == tmit->size)
+  {
+    if ((op_host_announce || op_host_assign || op_guest_talk) && !opt_follow)
+    {
+      exit_success ();
+    }
+    return GNUNET_YES;
+  }
+  else
+  {
+    return GNUNET_NO;
+  }
+}
+
+
+/**
+ * Host announcement - send a message to the place.
+ */
+static void
+host_announce (const char *method, const char *data, size_t data_size)
+{
+  struct GNUNET_PSYC_Environment *env = GNUNET_PSYC_env_create ();
+  GNUNET_PSYC_env_add (env, GNUNET_PSYC_OP_SET,
+                       "_foo", DATA2ARG ("bar baz"));
+
+  tmit = (struct TransmitClosure) {};
+  tmit.data = data;
+  tmit.size = data_size;
+
+  GNUNET_SOCIAL_host_announce (hst, method, env,
+                               notify_data, &tmit,
+                               GNUNET_SOCIAL_ANNOUNCE_NONE);
+  GNUNET_PSYC_env_destroy (env);
+}
+
+
+/**
+ * Assign a state var of @a name to the value of @a data.
+ */
+static void
+host_assign (const char *name, const char *data, size_t data_size)
+{
+  struct GNUNET_PSYC_Environment *env = GNUNET_PSYC_env_create ();
+  GNUNET_PSYC_env_add (env, GNUNET_PSYC_OP_ASSIGN,
+                       name, data, data_size);
+
+  tmit = (struct TransmitClosure) {};
+  GNUNET_SOCIAL_host_announce (hst, "_assign", env,
+                               notify_data, &tmit,
+                               GNUNET_SOCIAL_ANNOUNCE_NONE);
+  GNUNET_PSYC_env_destroy (env);
+}
+
+
+/**
+ * Guest talk request to host.
+ */
+static void
+guest_talk (const char *method,
+            const char *data, size_t data_size)
+{
+  struct GNUNET_PSYC_Environment *env = GNUNET_PSYC_env_create ();
+  GNUNET_PSYC_env_add (env, GNUNET_PSYC_OP_SET,
+                       "_foo", DATA2ARG ("bar baz"));
+
+  tmit = (struct TransmitClosure) {};
+  tmit.data = data;
+  tmit.size = data_size;
+
+  GNUNET_SOCIAL_guest_talk (gst, method, env,
+                            notify_data, &tmit,
+                            GNUNET_SOCIAL_TALK_NONE);
+  GNUNET_PSYC_env_destroy (env);
+}
+
+
+/* HISTORY REPLAY */
+
+
+/**
+ * Callback notifying about the end of history replay results.
+ */
+static void
+recv_history_replay_result (void *cls, int64_t result,
+                            const void *data, uint16_t data_size)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Received history replay result: %" PRId64 "\n"
+              "%.*s\n",
+              result, data_size, (const char *) data);
+
+  if (op_replay || op_replay_latest)
+  {
+    exit_success ();
+  }
+}
+
+
+/**
+ * Replay history between a given @a start and @a end message IDs,
+ * optionally filtered by a method @a prefix.
+ */
+static void
+history_replay (uint64_t start, uint64_t end, const char *prefix)
+{
+  GNUNET_SOCIAL_place_history_replay (plc, start, end, prefix,
+                                      GNUNET_PSYC_HISTORY_REPLAY_LOCAL,
+                                      slicer,
+                                      recv_history_replay_result,
+                                      NULL);
+}
+
+
+/**
+ * Replay latest @a limit messages.
+ */
+static void
+history_replay_latest (uint64_t limit, const char *prefix)
+{
+  GNUNET_SOCIAL_place_history_replay_latest (plc, limit, prefix,
+                                             GNUNET_PSYC_HISTORY_REPLAY_LOCAL,
+                                             slicer,
+                                             recv_history_replay_result,
+                                             NULL);
+}
+
+
+/* LOOK AT/FOR */
+
+
+/**
+ * Callback notifying about the end of state var results.
+ */
+static void
+look_result (void *cls, int64_t result_code,
+             const void *data, uint16_t data_size)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Received look result: %" PRId64 "\n", result_code);
+
+  if (op_look_at || op_look_for)
+  {
+    exit_success ();
+  }
+}
+
+
+/**
+ * Callback notifying about a state var result.
+ */
+static void
+look_var (void *cls,
+          const struct GNUNET_MessageHeader *mod,
+          const char *name,
+          const void *value,
+          uint32_t value_size,
+          uint32_t full_value_size)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+              "Received var: %s\n%.*s\n",
+              name, value_size, (const char *) value);
+}
+
+
+/**
+ * Look for a state var using exact match of the name.
+ */
+static void
+look_at (const char *full_name)
+{
+  GNUNET_SOCIAL_place_look_at (plc, full_name, look_var, look_result, NULL);
+}
+
+
+/**
+ * Look for state vars by name prefix.
+ */
+static void
+look_for (const char *name_prefix)
+{
+  GNUNET_SOCIAL_place_look_for (plc, name_prefix, look_var, look_result, NULL);
+}
+
+
+/* SLICER */
+
+
+/**
+ * Callback notifying about the start of a new incoming message.
+ */
+static void
+slicer_recv_method (void *cls,
+                    const struct GNUNET_PSYC_MessageHeader *msg,
+                    const struct GNUNET_PSYC_MessageMethod *meth,
+                    uint64_t message_id,
+                    const char *method_name)
+{
+  method_received = method_name;
+  GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+              "Received method for message ID %" PRIu64 ":\n"
+              "%s (flags: %x)\n",
+              message_id, method_name, ntohl (meth->flags));
+  /* routing header is missing, so we just print double newline */
+  printf("\n");
+  /* we output . instead of | to indicate that this is not proper PSYC syntax 
*/
+  /* FIXME: use libpsyc here */
+}
+
+
+/**
+ * Callback notifying about an incoming modifier.
+ */
+static void
+slicer_recv_modifier (void *cls,
+                      const struct GNUNET_PSYC_MessageHeader *msg,
+                      const struct GNUNET_MessageHeader *pmsg,
+                      uint64_t message_id,
+                      enum GNUNET_PSYC_Operator oper,
+                      const char *name,
+                      const void *value,
+                      uint16_t value_size,
+                      uint16_t full_value_size)
+{
+#if 0
+  GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+              "Received modifier for message ID %" PRIu64 ":\n"
+              "%c%s: %.*s (size: %u)\n",
+              message_id, oper, name, value_size, (const char *) value, 
value_size);
+#else
+  /* obviously not binary safe */
+  printf("%c%s\t%.*s\n",
+              oper, name, value_size, (const char *) value);
+#endif
+}
+
+
+/**
+ * Callback notifying about an incoming data fragment.
+ */
+static void
+slicer_recv_data (void *cls,
+                  const struct GNUNET_PSYC_MessageHeader *msg,
+                  const struct GNUNET_MessageHeader *pmsg,
+                  uint64_t message_id,
+                  const void *data,
+                  uint16_t data_size)
+{
+#if 0
+  GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+              "Received data for message ID %" PRIu64 ":\n"
+              "%.*s\n",
+              message_id, data_size, (const char *) data);
+#else
+  /* obviously not binary safe */
+  printf("%s\n%.*s\n",
+              method_received, data_size, (const char *) data);
+#endif
+}
+
+
+/**
+ * Callback notifying about the end of a message.
+ */
+static void
+slicer_recv_eom (void *cls,
+                const struct GNUNET_PSYC_MessageHeader *msg,
+                const struct GNUNET_MessageHeader *pmsg,
+                uint64_t message_id,
+                uint8_t is_cancelled)
+{
+  printf(".\n");
+  GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+              "Received end of message ID %" PRIu64
+              ", cancelled: %u\n",
+              message_id, is_cancelled);
+}
+
+
+/**
+ * Create a slicer for receiving message parts.
+ */
+static struct GNUNET_PSYC_Slicer *
+slicer_create ()
+{
+  slicer = GNUNET_PSYC_slicer_create ();
+
+  /* register slicer to receive incoming messages with any method name */
+  GNUNET_PSYC_slicer_method_add (slicer, "", NULL,
+                                 slicer_recv_method, slicer_recv_modifier,
+                                 slicer_recv_data, slicer_recv_eom, NULL);
+  return slicer;
+}
+
+
+/* GUEST ENTER */
+
+
+/**
+ * Callback called when the guest receives an entry decision from the host.
+ *
+ * It is called once after using guest_enter() or guest_enter_by_name(),
+ * in case of a reconnection only the local enter callback is called.
+ */
+static void
+guest_recv_entry_decision (void *cls,
+                           int is_admitted,
+                           const struct GNUNET_PSYC_Message *entry_msg)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+              "Guest received entry decision %d\n",
+              is_admitted);
+
+  if (NULL != entry_msg)
+  {
+    struct GNUNET_PSYC_Environment *env = GNUNET_PSYC_env_create ();
+    const char *method_name = NULL;
+    const void *data = NULL;
+    uint16_t data_size = 0;
+    struct GNUNET_PSYC_MessageHeader *
+      pmsg = GNUNET_PSYC_message_header_create_from_psyc (entry_msg);
+    GNUNET_PSYC_message_parse (pmsg, &method_name, env, &data, &data_size);
+    GNUNET_free (pmsg);
+
+    GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+                "%s\n%.*s\n",
+                method_name, data_size, (const char *) data);
+  }
+
+  if (op_guest_enter && !opt_follow)
+  {
+    exit_success ();
+  }
+}
+
+
+/**
+ * Callback called after a guest connection is established to the local 
service.
+ */
+static void
+guest_recv_local_enter (void *cls, int result,
+                        const struct GNUNET_CRYPTO_EddsaPublicKey *pub_key,
+                        uint64_t max_message_id)
+{
+  char *pub_str = GNUNET_CRYPTO_eddsa_public_key_to_string (pub_key);
+  GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+              "Guest entered local place: %s, max_message_id: %" PRIu64 "\n",
+              pub_str, max_message_id);
+  GNUNET_free (pub_str);
+  GNUNET_assert (0 <= result);
+
+  if (op_guest_enter && !opt_follow)
+  {
+    exit_success ();
+  }
+}
+
+
+/**
+ * Create entry request message.
+ */
+static struct GNUNET_PSYC_Message *
+guest_enter_msg_create ()
+{
+  const char *method_name = "_request_enter";
+  struct GNUNET_PSYC_Environment *env = GNUNET_PSYC_env_create ();
+  GNUNET_PSYC_env_add (env, GNUNET_PSYC_OP_SET,
+                       "_foo", DATA2ARG ("bar"));
+  void *data = "let me in";
+  uint16_t data_size = strlen (data) + 1;
+
+  return GNUNET_PSYC_message_create (method_name, env, data, data_size);
+}
+
+
+/**
+ * Enter a place as guest, using its public key and peer ID.
+ */
+static void
+guest_enter (const struct GNUNET_CRYPTO_EddsaPublicKey *pub_key,
+             const struct GNUNET_PeerIdentity *peer)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Entering to place as guest.\n");
+
+  if (NULL == ego)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "--ego missing or invalid\n");
+    exit_fail ();
+    return;
+  }
+
+  struct GNUNET_PSYC_Message *join_msg = guest_enter_msg_create ();
+  gst = GNUNET_SOCIAL_guest_enter (app, ego, pub_key,
+                                   GNUNET_PSYC_SLAVE_JOIN_NONE,
+                                   peer, 0, NULL, join_msg, slicer_create (),
+                                   guest_recv_local_enter,
+                                   guest_recv_entry_decision, NULL);
+  GNUNET_free (join_msg);
+  plc = GNUNET_SOCIAL_guest_get_place (gst);
+}
+
+
+/**
+ * Enter a place as guest using its GNS address.
+ */
+static void
+guest_enter_by_name (const char *gns_name)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Entering to place by name as guest.\n");
+
+  struct GNUNET_PSYC_Message *join_msg = guest_enter_msg_create ();
+  gst = GNUNET_SOCIAL_guest_enter_by_name (app, ego, gns_name, NULL,
+                                           join_msg, slicer,
+                                           guest_recv_local_enter,
+                                           guest_recv_entry_decision, NULL);
+  GNUNET_free (join_msg);
+  plc = GNUNET_SOCIAL_guest_get_place (gst);
+}
+
+
+/* HOST ENTER */
+
+
+/**
+ * Callback called when a @a nym wants to enter the place.
+ *
+ * The request needs to be replied with an entry decision.
+ */
+static void
+host_answer_door (void *cls,
+                  struct GNUNET_SOCIAL_Nym *nym,
+                  const char *method_name,
+                  struct GNUNET_PSYC_Environment *env,
+                  const void *data,
+                  size_t data_size)
+{
+  const struct GNUNET_CRYPTO_EcdsaPublicKey *
+    nym_key = GNUNET_SOCIAL_nym_get_pub_key (nym);
+  char *
+    nym_str = GNUNET_CRYPTO_ecdsa_public_key_to_string (nym_key);
+
+  GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+              "Entry request: %s\n", nym_str);
+  GNUNET_free (nym_str);
+
+  if (opt_welcome)
+  {
+    struct GNUNET_PSYC_Message *
+      resp = GNUNET_PSYC_message_create ("_notice_place_admit", env,
+                                         DATA2ARG ("Welcome, nym!"));
+    GNUNET_SOCIAL_host_entry_decision (hst, nym, GNUNET_YES, resp);
+    GNUNET_free (resp);
+  }
+  else if (opt_deny)
+  {
+    struct GNUNET_PSYC_Message *
+      resp = GNUNET_PSYC_message_create ("_notice_place_refuse", NULL,
+                                         DATA2ARG ("Go away!"));
+    GNUNET_SOCIAL_host_entry_decision (hst, nym, GNUNET_NO, resp);
+    GNUNET_free (resp);
+  }
+
+
+}
+
+
+/**
+ * Callback called when a @a nym has left the place.
+ */
+static void
+host_farewell (void *cls,
+               const struct GNUNET_SOCIAL_Nym *nym,
+               struct GNUNET_PSYC_Environment *env)
+{
+  const struct GNUNET_CRYPTO_EcdsaPublicKey *
+    nym_key = GNUNET_SOCIAL_nym_get_pub_key (nym);
+  char *
+    nym_str = GNUNET_CRYPTO_ecdsa_public_key_to_string (nym_key);
+
+  GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+              "Farewell: %s\n", nym_str);
+  GNUNET_free (nym_str);
+}
+
+
+/**
+ * Callback called after the host entered the place.
+ */
+static void
+host_entered (void *cls, int result,
+              const struct GNUNET_CRYPTO_EddsaPublicKey *pub_key,
+              uint64_t max_message_id)
+{
+  place_pub_key = *pub_key;
+  char *pub_str = GNUNET_CRYPTO_eddsa_public_key_to_string (pub_key);
+  GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+              "Host entered: %s, max_message_id: %" PRIu64 "\n",
+              pub_str, max_message_id);
+  GNUNET_free (pub_str);
+
+  if (op_host_enter && !opt_follow)
+  {
+    exit_success ();
+  }
+}
+
+
+/**
+ * Enter and start hosting a place.
+ */
+static void
+host_enter ()
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "host_enter()\n");
+
+  if (NULL == ego)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "--ego missing or invalid\n");
+    exit_fail ();
+    return;
+  }
+
+  hst = GNUNET_SOCIAL_host_enter (app, ego,
+                                  GNUNET_PSYC_CHANNEL_PRIVATE,
+                                  slicer_create (), host_entered,
+                                  host_answer_door, host_farewell, NULL);
+  plc = GNUNET_SOCIAL_host_get_place (hst);
+}
+
+
+/* PLACE RECONNECT */
+
+
+/**
+ * Perform operations common to both host & guest places.
+ */
+static void
+place_reconnected ()
+{
+  static int first_run = GNUNET_YES;
+  if (GNUNET_NO == first_run)
+    return;
+  first_run = GNUNET_NO;
+
+  if (op_replay) {
+    history_replay (opt_start, opt_until, opt_method);
+  }
+  else if (op_replay_latest) {
+    history_replay_latest (opt_limit, opt_method);
+  }
+  else if (op_look_at) {
+    look_at (opt_name);
+  }
+  else if (op_look_for) {
+    look_for (opt_name);
+  }
+}
+
+
+/**
+ * Callback called after reconnecting to a host place.
+ */
+static void
+host_reconnected (void *cls, int result,
+                 const struct GNUNET_CRYPTO_EddsaPublicKey *place_pub_key,
+                 uint64_t max_message_id)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Host reconnected.\n");
+
+  if (op_host_leave) {
+    host_leave ();
+  }
+  else if (op_host_announce) {
+    host_announce (opt_method, opt_data, strlen (opt_data));
+  }
+  else if (op_host_assign) {
+    host_assign (opt_name, opt_data, strlen (opt_data) + 1);
+  }
+  else {
+    place_reconnected ();
+  }
+}
+
+
+/**
+ * Callback called after reconnecting to a guest place.
+ */
+static void
+guest_reconnected (void *cls, int result,
+                   const struct GNUNET_CRYPTO_EddsaPublicKey *place_pub_key,
+                   uint64_t max_message_id)
+{
+  char *place_pub_str = GNUNET_CRYPTO_eddsa_public_key_to_string 
(place_pub_key);
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Guest reconnected to place %s.\n", place_pub_str);
+  GNUNET_free (place_pub_str);
+
+  if (op_guest_leave) {
+    guest_leave ();
+  }
+  else if (op_guest_talk) {
+    guest_talk (opt_method, opt_data, strlen (opt_data));
+  }
+  else {
+    place_reconnected ();
+  }
+}
+
+
+/* APP */
+
+
+/**
+ * Callback called after the ego and place callbacks.
+ */
+static void
+app_connected (void *cls)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "App connected: %p\n", cls);
+
+  if (op_status)
+  {
+    exit_success ();
+  }
+  else if (op_host_enter)
+  {
+    host_enter ();
+  }
+  else if (op_guest_enter)
+  {
+    if (opt_gns)
+    {
+      guest_enter_by_name (opt_gns);
+    }
+    else
+    {
+      if (opt_peer)
+      {
+        if (GNUNET_OK != GNUNET_CRYPTO_eddsa_public_key_from_string (opt_peer,
+                                                                     strlen 
(opt_peer),
+                                                                     
&peer.public_key))
+        {
+          GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                      "--peer invalid");
+          exit_fail ();
+          return;
+        }
+      }
+      else
+      {
+        peer = this_peer;
+      }
+      guest_enter (&place_pub_key, &peer);
+    }
+  }
+  printf(".\n");
+}
+
+
+/**
+ * Callback notifying about a host place available for reconnection.
+ */
+static void
+app_recv_host (void *cls,
+               struct GNUNET_SOCIAL_HostConnection *hconn,
+               struct GNUNET_SOCIAL_Ego *ego,
+               const struct GNUNET_CRYPTO_EddsaPublicKey *host_pub_key,
+               enum GNUNET_SOCIAL_AppPlaceState place_state)
+{
+  char *host_pub_str = GNUNET_CRYPTO_eddsa_public_key_to_string (host_pub_key);
+  printf ("Host\t%s\n", host_pub_str);
+  GNUNET_free (host_pub_str);
+
+  if ((op_host_reconnect || op_host_leave || op_host_announce || op_host_assign
+       || op_replay || op_replay_latest
+       || op_look_at || op_look_for)
+      && 0 == memcmp (&place_pub_key, host_pub_key, sizeof (*host_pub_key)))
+  {
+    hst = GNUNET_SOCIAL_host_enter_reconnect (hconn, slicer_create (), 
host_reconnected,
+                                              host_answer_door, host_farewell, 
NULL);
+    plc = GNUNET_SOCIAL_host_get_place (hst);
+  }
+}
+
+
+/**
+ * Callback notifying about a guest place available for reconnection.
+ */
+static void
+app_recv_guest (void *cls,
+                struct GNUNET_SOCIAL_GuestConnection *gconn,
+                struct GNUNET_SOCIAL_Ego *ego,
+                const struct GNUNET_CRYPTO_EddsaPublicKey *guest_pub_key,
+                enum GNUNET_SOCIAL_AppPlaceState place_state)
+{
+  char *guest_pub_str = GNUNET_CRYPTO_eddsa_public_key_to_string 
(guest_pub_key);
+  printf ("Guest\t%s\n", guest_pub_str);
+  GNUNET_free (guest_pub_str);
+
+  if ((op_guest_reconnect || op_guest_leave || op_guest_talk
+       || op_replay || op_replay_latest
+       || op_look_at || op_look_for)
+      && 0 == memcmp (&place_pub_key, guest_pub_key, sizeof (*guest_pub_key)))
+  {
+    gst = GNUNET_SOCIAL_guest_enter_reconnect (gconn, 
GNUNET_PSYC_SLAVE_JOIN_NONE,
+                                               slicer_create (), 
guest_reconnected, NULL);
+    plc = GNUNET_SOCIAL_guest_get_place (gst);
+  }
+}
+
+
+/**
+ * Callback notifying about an available ego.
+ */
+static void
+app_recv_ego (void *cls,
+              struct GNUNET_SOCIAL_Ego *e,
+              const struct GNUNET_CRYPTO_EcdsaPublicKey *pub_key,
+              const char *name)
+{
+  char *s = GNUNET_CRYPTO_ecdsa_public_key_to_string (pub_key);
+  printf ("Ego\t%s\t%s\n", s, name);
+  GNUNET_free (s);
+
+  if (0 == memcmp (&ego_pub_key, pub_key, sizeof (*pub_key))
+      || (NULL != opt_ego && 0 == strcmp (opt_ego, name)))
+  {
+    ego = e;
+  }
+
+}
+
+
+
+/**
+ * Establish application connection to receive available egos and places.
+ */
+static void
+app_connect (void *cls)
+{
+  app = GNUNET_SOCIAL_app_connect (cfg, opt_app,
+                                   app_recv_ego,
+                                   app_recv_host,
+                                   app_recv_guest,
+                                   app_connected,
+                                   NULL);
+}
+
+
+/**
+ * Main function run by the scheduler.
+ *
+ * @param cls closure
+ * @param args remaining command-line arguments
+ * @param cfgfile name of the configuration file used (for saving, can be 
NULL!)
+ * @param c configuration
+ */
+static void
+run (void *cls, char *const *args, const char *cfgfile,
+     const struct GNUNET_CONFIGURATION_Handle *c)
+{
+  cfg = c;
+  GNUNET_CRYPTO_get_peer_identity (cfg, &this_peer);
+
+  if (!opt_method)
+    opt_method = "message";
+  if (!opt_data)
+    opt_data = "";
+  if (!opt_name)
+    opt_name = "";
+
+  if (! (op_status
+         || op_host_enter || op_host_reconnect || op_host_leave
+         || op_host_announce || op_host_assign
+         || op_guest_enter || op_guest_reconnect
+         || op_guest_leave || op_guest_talk
+         || op_replay || op_replay_latest
+         || op_look_at || op_look_for))
+  {
+    op_status = 1;
+    fputs("Caution: This tool does not produce correct binary safe PSYC 
syntax.\n\n", stderr);
+  }
+
+  GNUNET_SCHEDULER_add_shutdown (scheduler_shutdown, NULL);
+  if (!opt_follow)
+  {
+    timeout_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, timeout, NULL);
+  }
+
+  if ((op_host_reconnect || op_host_leave || op_host_announce || op_host_assign
+       || op_guest_reconnect || (op_guest_enter && !opt_gns)
+       || op_guest_leave || op_guest_talk
+       || op_replay || op_replay_latest
+       || op_look_at || op_look_for)
+      && (!opt_place
+          || GNUNET_OK != GNUNET_CRYPTO_eddsa_public_key_from_string 
(opt_place,
+                                                                      strlen 
(opt_place),
+                                                                      
&place_pub_key)))
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                _("--place missing or invalid.\n"));
+    /* FIXME: why does it segfault here? */
+    exit_fail ();
+    return;
+  }
+
+  if (opt_ego)
+  {
+    if (GNUNET_OK !=
+        GNUNET_CRYPTO_ecdsa_public_key_from_string (opt_ego,
+                                                strlen (opt_ego),
+                                                &ego_pub_key))
+    {
+      FPRINTF (stderr,
+               _("Public key `%s' malformed\n"),
+               opt_ego);
+      exit_fail ();
+      return;
+    }
+  }
+
+  GNUNET_SCHEDULER_add_now (app_connect, NULL);
+}
+
+
+/**
+ * The main function to obtain peer information.
+ *
+ * @param argc number of arguments from the command line
+ * @param argv command line arguments
+ * @return 0 ok, 1 on error
+ */
+int
+main (int argc, char *const *argv)
+{
+  int res;
+  struct GNUNET_GETOPT_CommandLineOption options[] = {
+    /*
+     * gnunet program options in addition to the ones below:
+     *
+     * -c, --config=FILENAME
+     * -l, --logfile=LOGFILE
+     * -L, --log=LOGLEVEL
+     * -h, --help
+     * -v, --version
+     */
+
+    /* operations */
+
+    GNUNET_GETOPT_option_flag ('A',
+                                  "host-assign",
+                                  gettext_noop ("assign --name in state to 
--data"),
+                                  &op_host_assign),
+
+    GNUNET_GETOPT_option_flag ('B',
+                                  "guest-leave",
+                                  gettext_noop ("say good-bye and leave 
somebody else's place"),
+                                  &op_guest_leave),
+
+    GNUNET_GETOPT_option_flag ('C',
+                                  "host-enter",
+                                  gettext_noop ("create a place"),
+                                  &op_host_enter),
+
+    GNUNET_GETOPT_option_flag ('D',
+                                  "host-leave",
+                                  gettext_noop ("destroy a place we were 
hosting"),
+                                  &op_host_leave),
+
+    GNUNET_GETOPT_option_flag ('E',
+                                  "guest-enter",
+                                  gettext_noop ("enter somebody else's place"),
+                                  &op_guest_enter),
+
+
+    GNUNET_GETOPT_option_flag ('F',
+                                  "look-for",
+                                  gettext_noop ("find state matching name 
prefix"),
+                                  &op_look_for),
+
+    GNUNET_GETOPT_option_flag ('H',
+                                  "replay-latest",
+                                  gettext_noop ("replay history of messages up 
to the given --limit"),
+                                  &op_replay_latest),
+
+    GNUNET_GETOPT_option_flag ('N',
+                                  "host-reconnect",
+                                  gettext_noop ("reconnect to a previously 
created place"),
+                                  &op_host_reconnect),
+
+    GNUNET_GETOPT_option_flag ('P',
+                                  "host-announce",
+                                  gettext_noop ("publish something to a place 
we are hosting"),
+                                  &op_host_announce),
+
+    GNUNET_GETOPT_option_flag ('R',
+                                  "guest-reconnect",
+                                  gettext_noop ("reconnect to a previously 
entered place"),
+                                  &op_guest_reconnect),
+
+    GNUNET_GETOPT_option_flag ('S',
+                                  "look-at",
+                                  gettext_noop ("search for state matching 
exact name"),
+                                  &op_look_at),
+
+    GNUNET_GETOPT_option_flag ('T',
+                                  "guest-talk",
+                                  gettext_noop ("submit something to 
somebody's place"),
+                                  &op_guest_talk),
+
+    GNUNET_GETOPT_option_flag ('U',
+                                  "status",
+                                  gettext_noop ("list of egos and subscribed 
places"),
+                                  &op_status),
+
+    GNUNET_GETOPT_option_flag ('X',
+                                  "replay",
+                                  gettext_noop ("extract and replay history 
between message IDs --start and --until"),
+                                  &op_replay),
+
+
+    /* options */
+
+    GNUNET_GETOPT_option_string ('a',
+                                 "app",
+                                 "APPLICATION_ID",
+                                 gettext_noop ("application ID to use when 
connecting"),
+                                 &opt_app),
+
+    GNUNET_GETOPT_option_string ('d',
+                                 "data",
+                                 "DATA",
+                                 gettext_noop ("message body or state value"),
+                                 &opt_data),
+
+    GNUNET_GETOPT_option_string ('e',
+                                 "ego",
+                                 "NAME|PUBKEY",
+                                 gettext_noop ("name or public key of ego"),
+                                 &opt_ego),
+
+    GNUNET_GETOPT_option_flag ('f',
+                                  "follow",
+                                  gettext_noop ("wait for incoming messages"),
+                                  &opt_follow),
+
+    GNUNET_GETOPT_option_string ('g',
+                                 "gns",
+                                 "GNS_NAME",
+                                 gettext_noop ("GNS name"),
+                                 &opt_gns),
+
+    GNUNET_GETOPT_option_string ('i',
+                                 "peer",
+                                 "PEER_ID",
+                                 gettext_noop ("peer ID for --guest-enter"),
+                                 &opt_peer),
+
+    GNUNET_GETOPT_option_string ('k',
+                                 "name",
+                                 "VAR_NAME",
+                                 gettext_noop ("name (key) to query from 
state"),
+                                 &opt_name),
+
+    GNUNET_GETOPT_option_string ('m',
+                                 "method",
+                                 "METHOD_NAME",
+                                 gettext_noop ("method name"),
+                                 &opt_method),
+
+    GNUNET_GETOPT_option_ulong ('n',
+                                    "limit",
+                                    NULL,
+                                    gettext_noop ("number of messages to 
replay from history"),
+                                    &opt_limit),
+
+    GNUNET_GETOPT_option_string ('p',
+                                 "place",
+                                 "PUBKEY",
+                                 gettext_noop ("key address of place"),
+                                 &opt_place),
+
+    GNUNET_GETOPT_option_ulong ('s',
+                                    "start",
+                                    NULL,
+                                    gettext_noop ("start message ID for 
history replay"),
+                                    &opt_start),
+
+    GNUNET_GETOPT_option_flag ('w',
+                                  "welcome",
+                                  gettext_noop ("respond to entry requests by 
admitting all guests"),
+                                  &opt_welcome),
+
+    GNUNET_GETOPT_option_ulong ('u',
+                                    "until",
+                                    NULL,
+                                    gettext_noop ("end message ID for history 
replay"),
+                                    &opt_until),
+
+    GNUNET_GETOPT_option_flag ('y',
+                                  "deny",
+                                  gettext_noop ("respond to entry requests by 
refusing all guests"),
+                                  &opt_deny),
+
+    GNUNET_GETOPT_OPTION_END
+  };
+
+  if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
+    return 2;
+
+  const char *help =
+    _ ("gnunet-social - Interact with the social service: enter/leave, 
send/receive messages, access history and state.\n");
+  const char *usage =
+    "gnunet-social [--status]\n"
+    "\n"
+    "gnunet-social --host-enter --ego <NAME or PUBKEY> [--follow] [--welcome | 
--deny]\n"
+    "gnunet-social --host-reconnect --place <PUBKEY> [--follow] [--welcome | 
--deny]\n"
+    "gnunet-social --host-leave --place <PUBKEY>\n"
+    "gnunet-social --host-assign --place <PUBKEY> --name <NAME> --data 
<VALUE>\n"
+// FIXME: some state ops not implemented yet (no hurry)
+//  "gnunet-social --host-augment --place <PUBKEY> --name <NAME> --data 
<VALUE>\n"
+//  "gnunet-social --host-diminish --place <PUBKEY> --name <NAME> --data 
<VALUE>\n"
+//  "gnunet-social --host-set --place <PUBKEY> --name <NAME> --data <VALUE>\n"
+    "gnunet-social --host-announce --place <PUBKEY> --method <METHOD_NAME> 
--data <MESSAGE_BODY>\n"
+    "\n"
+    "gnunet-social --guest-enter --place <PUBKEY> --peer <PEERID> --ego <NAME 
or PUBKEY> [--follow]\n"
+    "gnunet-social --guest-enter --gns <GNS_NAME> --ego <NAME or PUBKEY> 
[--follow]\n"
+    "gnunet-social --guest-reconnect --place <PUBKEY> [--follow]\n"
+    "gnunet-social --guest-leave --place <PUBKEY>\n"
+    "gnunet-social --guest-talk --place <PUBKEY> --method <METHOD_NAME> --data 
<MESSAGE_BODY>\n"
+    "\n"
+    "gnunet-social --replay --place <PUBKEY> --start <MSGID> --until <MSGID>  
[--method <METHOD_PREFIX>]\n"
+    "gnunet-social --replay-latest --place <PUBKEY> --limit <MSG_LIMIT> 
[--method <METHOD_PREFIX>]\n"
+    "\n"
+    "gnunet-social --look-at --place <PUBKEY> --name <FULL_NAME>\n"
+    "gnunet-social --look-for --place <PUBKEY> --name <NAME_PREFIX>\n";
+
+  res = GNUNET_PROGRAM_run (argc, argv, help, usage, options, &run, NULL);
+
+  GNUNET_free ((void *) argv);
+
+  if (GNUNET_OK == res)
+    return ret;
+  else
+    return 1;
+}
diff --git a/src/social/social.conf.in b/src/social/social.conf.in
new file mode 100644
index 0000000..3fe754c
--- /dev/null
+++ b/src/social/social.conf.in
@@ -0,0 +1,15 @@
+[social]
+START_ON_DEMAND = @START_ON_DEMAND@
+BINARY = gnunet-service-social
+RUN_PER_USER = YES
+
+UNIXPATH = $GNUNET_USER_RUNTIME_DIR/gnunet-service-social.sock
+UNIX_MATCH_UID = YES
+UNIX_MATCH_GID = YES
+
address@hidden@PORT = 2116
+HOSTNAME = localhost
+ACCEPT_FROM = 127.0.0.1;
+ACCEPT_FROM6 = ::1;
+
+DATA_HOME = $GNUNET_DATA_HOME/social
diff --git a/src/social/social.h b/src/social/social.h
new file mode 100644
index 0000000..73f73f6
--- /dev/null
+++ b/src/social/social.h
@@ -0,0 +1,292 @@
+/*
+ * This file is part of GNUnet
+ * Copyright (C) 2013 GNUnet e.V.
+ *
+ * GNUnet is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * GNUnet 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
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+     SPDX-License-Identifier: AGPL3.0-or-later
+ */
+
+/**
+ * @file social/social.h
+ * @brief Common type definitions for the Social service and API.
+ * @author Gabor X Toth
+ */
+
+#ifndef SOCIAL_H
+#define SOCIAL_H
+
+#include "platform.h"
+#include "gnunet_social_service.h"
+
+enum MessageState
+{
+  MSG_STATE_START    = 0,
+  MSG_STATE_HEADER   = 1,
+  MSG_STATE_METHOD   = 2,
+  MSG_STATE_MODIFIER = 3,
+  MSG_STATE_MOD_CONT = 4,
+  MSG_STATE_DATA     = 5,
+  MSG_STATE_END      = 6,
+  MSG_STATE_CANCEL   = 7,
+  MSG_STATE_ERROR    = 8,
+};
+
+
+GNUNET_NETWORK_STRUCT_BEGIN
+
+/**** library -> service ****/
+
+
+struct AppConnectRequest
+{
+  /**
+   * Type: GNUNET_MESSAGE_TYPE_SOCIAL_APP_CONNECT
+   */
+  struct GNUNET_MessageHeader header;
+
+  /* Followed by char *app_id */
+};
+
+
+struct AppDetachRequest
+{
+  /**
+   * Type: GNUNET_MESSAGE_TYPE_SOCIAL_APP_DETACH
+   */
+  struct GNUNET_MessageHeader header;
+
+  /**
+   * Public key of place.
+   */
+  struct GNUNET_CRYPTO_EddsaPublicKey place_pub_key;
+
+  /**
+   * Public key of ego.
+   */
+  struct GNUNET_CRYPTO_EcdsaPublicKey ego_pub_key;
+
+  /**
+   * Operation ID.
+   */
+  uint64_t op_id GNUNET_PACKED;
+};
+
+
+struct MsgProcRequest
+{
+  /**
+   * Type: GNUNET_MESSAGE_TYPE_SOCIAL_MSG_PROC_SET
+   */
+  struct GNUNET_MessageHeader header;
+
+  /**
+   * @see enum GNUNET_SOCIAL_MsgProcFlags
+   */
+  uint32_t flags;
+
+  /* Followed by char *method_prefix */
+};
+
+
+struct HostEnterRequest
+{
+  /**
+   * Type: GNUNET_MESSAGE_TYPE_SOCIAL_HOST_ENTER
+   */
+  struct GNUNET_MessageHeader header;
+
+  uint32_t policy GNUNET_PACKED;
+
+  struct GNUNET_CRYPTO_EcdsaPublicKey ego_pub_key;
+
+  struct GNUNET_CRYPTO_EddsaPublicKey place_pub_key;
+
+  struct GNUNET_CRYPTO_EddsaPrivateKey place_key;
+
+  /* Followed by char *app_id */
+};
+
+
+struct GuestEnterRequest
+{
+  /**
+   * Type: GNUNET_MESSAGE_TYPE_SOCIAL_GUEST_ENTER
+   */
+  struct GNUNET_MessageHeader header;
+
+  uint32_t relay_count GNUNET_PACKED;
+
+  struct GNUNET_CRYPTO_EcdsaPublicKey ego_pub_key;
+
+  struct GNUNET_CRYPTO_EddsaPublicKey place_pub_key;
+
+  struct GNUNET_PeerIdentity origin;
+
+  uint32_t flags GNUNET_PACKED;
+
+  /* Followed by char *app_id */
+  /* Followed by struct GNUNET_PeerIdentity relays[relay_count] */
+  /* Followed by struct GNUNET_MessageHeader *join_msg */
+};
+
+
+/** Compatible parts of HostEnterRequest and GuestEnterRequest */
+struct PlaceEnterRequest
+{
+  struct GNUNET_MessageHeader header;
+
+  uint32_t reserved GNUNET_PACKED;
+
+  struct GNUNET_CRYPTO_EcdsaPublicKey ego_pub_key;
+
+  struct GNUNET_CRYPTO_EddsaPublicKey place_pub_key;
+};
+
+
+struct EgoPlacePublicKey
+{
+  struct GNUNET_CRYPTO_EcdsaPublicKey ego_pub_key;
+  struct GNUNET_CRYPTO_EddsaPublicKey place_pub_key;
+};
+
+
+struct GuestEnterByNameRequest
+{
+  /**
+   * Type: GNUNET_MESSAGE_TYPE_SOCIAL_GUEST_ENTER_BY_NAME
+   */
+  struct GNUNET_MessageHeader header;
+
+  struct GNUNET_CRYPTO_EcdsaPublicKey ego_pub_key;
+
+  /* Followed by char *app_id */
+  /* Followed by char *gns_name */
+  /* Followed by char *password */
+  /* Followed by struct GNUNET_MessageHeader *join_msg */
+};
+
+
+struct ZoneAddPlaceRequest
+{
+  struct GNUNET_MessageHeader header;
+
+  uint32_t relay_count GNUNET_PACKED;
+
+  /**
+   * Operation ID.
+   */
+  uint64_t op_id;
+
+  /**
+   * Expiration time: absolute value in us.
+   */
+  uint64_t expiration_time;
+
+  struct GNUNET_CRYPTO_EcdsaPublicKey ego_pub_key;
+
+  struct GNUNET_CRYPTO_EddsaPublicKey place_pub_key;
+
+  struct GNUNET_PeerIdentity origin;
+
+  /* Followed by const char *name */
+  /* Followed by const char *password */
+  /* Followed by  struct GNUNET_PeerIdentity *relays[relay_count] */
+};
+
+
+struct ZoneAddNymRequest
+{
+  struct GNUNET_MessageHeader header;
+
+  /**
+   * Operation ID.
+   */
+  uint64_t op_id;
+
+  /**
+   * Expiration time: absolute value in us.
+   */
+  uint64_t expiration_time;
+
+  struct GNUNET_CRYPTO_EcdsaPublicKey ego_pub_key;
+
+  struct GNUNET_CRYPTO_EcdsaPublicKey nym_pub_key;
+
+  /* Followed by const char *name */
+};
+
+
+/**** service -> library ****/
+
+
+struct AppEgoMessage
+{
+  /**
+   * Type: GNUNET_MESSAGE_TYPE_SOCIAL_APP_EGO
+   */
+  struct GNUNET_MessageHeader header;
+
+  /**
+   * Public key of ego.
+   */
+  struct GNUNET_CRYPTO_EcdsaPublicKey ego_pub_key;
+
+  /* Followed by char *name */
+};
+
+
+struct AppPlaceMessage
+{
+  /**
+   * Type: GNUNET_MESSAGE_TYPE_SOCIAL_APP_PLACE
+   */
+  struct GNUNET_MessageHeader header;
+
+  struct GNUNET_CRYPTO_EcdsaPublicKey ego_pub_key;
+
+  struct GNUNET_CRYPTO_EddsaPublicKey place_pub_key;
+
+  uint8_t is_host;
+
+  uint8_t place_state;
+};
+
+
+struct HostEnterAck {
+  /**
+   * Type: GNUNET_MESSAGE_TYPE_SOCIAL_HOST_ENTER_ACK
+   */
+  struct GNUNET_MessageHeader header;
+
+  /**
+   * Status code for the operation.
+   */
+  uint32_t result_code GNUNET_PACKED;
+
+  /**
+   * Last message ID sent to the channel.
+   */
+  uint64_t max_message_id GNUNET_PACKED;
+
+  /**
+   * Public key of the place.
+   */
+  struct GNUNET_CRYPTO_EddsaPublicKey place_pub_key;
+};
+
+
+GNUNET_NETWORK_STRUCT_END
+
+#endif
diff --git a/src/social/social_api.c b/src/social/social_api.c
new file mode 100644
index 0000000..9b96580
--- /dev/null
+++ b/src/social/social_api.c
@@ -0,0 +1,2827 @@
+/*
+ * This file is part of GNUnet
+ * Copyright (C) 2013 GNUnet e.V.
+ *
+ * GNUnet is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * GNUnet 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
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+     SPDX-License-Identifier: AGPL3.0-or-later
+ */
+
+/**
+ * @author Gabor X Toth
+ *
+ * @file
+ * Social service; implements social interactions using the PSYC service.
+ */
+
+#include <inttypes.h>
+#include <string.h>
+
+#include "platform.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_psyc_service.h"
+#include "gnunet_psyc_util_lib.h"
+#include "gnunet_social_service.h"
+#include "social.h"
+
+#define LOG(kind,...) GNUNET_log_from (kind, "social-api",__VA_ARGS__)
+
+/**
+ * Handle for an ego.
+ */
+struct GNUNET_SOCIAL_Ego
+{
+  struct GNUNET_CRYPTO_EcdsaPublicKey pub_key;
+  struct GNUNET_HashCode pub_key_hash;
+  char *name;
+};
+
+
+/**
+ * Handle for a pseudonym of another user in the network.
+ */
+struct GNUNET_SOCIAL_Nym
+{
+  struct GNUNET_CRYPTO_EcdsaPublicKey pub_key;
+  struct GNUNET_HashCode pub_key_hash;
+};
+
+
+/**
+ * Handle for an application.
+ */
+struct GNUNET_SOCIAL_App
+{
+  /**
+   * Configuration to use.
+   */
+  const struct GNUNET_CONFIGURATION_Handle *cfg;
+
+  /**
+   * Client connection to the service.
+   */
+  struct GNUNET_MQ_Handle *mq;
+
+  /**
+   * Message to send on connect.
+   */
+  struct GNUNET_MQ_Envelope *connect_env;
+
+  /**
+   * Time to wait until we try to reconnect on failure.
+   */
+  struct GNUNET_TIME_Relative reconnect_delay;
+
+  /**
+   * Task for reconnecting when the listener fails.
+   */
+  struct GNUNET_SCHEDULER_Task *reconnect_task;
+
+  /**
+   * Async operations.
+   */
+  struct GNUNET_OP_Handle *op;
+
+  /**
+   * Function called after disconnected from the service.
+   */
+  GNUNET_ContinuationCallback disconnect_cb;
+
+  /**
+   * Closure for @a disconnect_cb.
+   */
+  void *disconnect_cls;
+
+  /**
+   * Application ID.
+   */
+  char *id;
+
+  /**
+   * Hash map of all egos.
+   * pub_key_hash -> struct GNUNET_SOCIAL_Ego *
+   */
+  struct GNUNET_CONTAINER_MultiHashMap *egos;
+
+  GNUNET_SOCIAL_AppEgoCallback ego_cb;
+  GNUNET_SOCIAL_AppHostPlaceCallback host_cb;
+  GNUNET_SOCIAL_AppGuestPlaceCallback guest_cb;
+  GNUNET_SOCIAL_AppConnectedCallback connected_cb;
+  void *cb_cls;
+};
+
+
+struct GNUNET_SOCIAL_HostConnection
+{
+  struct GNUNET_SOCIAL_App *app;
+
+  struct AppPlaceMessage plc_msg;
+};
+
+
+struct GNUNET_SOCIAL_GuestConnection
+{
+  struct GNUNET_SOCIAL_App *app;
+
+  struct AppPlaceMessage plc_msg;
+};
+
+
+/**
+ * Handle for a place where social interactions happen.
+ */
+struct GNUNET_SOCIAL_Place
+{
+  /**
+   * Configuration to use.
+   */
+  const struct GNUNET_CONFIGURATION_Handle *cfg;
+
+  /**
+   * Client connection to the service.
+   */
+  struct GNUNET_MQ_Handle *mq;
+
+  /**
+   * Message to send on connect.
+   */
+  struct GNUNET_MQ_Envelope *connect_env;
+
+  /**
+   * Time to wait until we try to reconnect on failure.
+   */
+  struct GNUNET_TIME_Relative reconnect_delay;
+
+  /**
+   * Task for reconnecting when the listener fails.
+   */
+  struct GNUNET_SCHEDULER_Task *reconnect_task;
+
+  /**
+   * Async operations.
+   */
+  struct GNUNET_OP_Handle *op;
+
+  /**
+   * Transmission handle.
+   */
+  struct GNUNET_PSYC_TransmitHandle *tmit;
+
+  /**
+   * Slicer for processing incoming messages.
+   */
+  struct GNUNET_PSYC_Slicer *slicer;
+
+  // FIXME: do we need is_disconnecing like on the psyc and multicast APIs?
+  /**
+   * Function called after disconnected from the service.
+   */
+  GNUNET_ContinuationCallback disconnect_cb;
+
+  /**
+   * Closure for @a disconnect_cb.
+   */
+  void *disconnect_cls;
+
+  /**
+   * Public key of the place.
+   */
+  struct GNUNET_CRYPTO_EddsaPublicKey pub_key;
+
+  /**
+   * Public key of the ego.
+   */
+  struct GNUNET_CRYPTO_EcdsaPublicKey ego_pub_key;
+
+  /**
+   * Does this place belong to a host (#GNUNET_YES) or guest (#GNUNET_NO)?
+   */
+  uint8_t is_host;
+};
+
+
+/**
+ * Host handle for a place that we entered.
+ */
+struct GNUNET_SOCIAL_Host
+{
+  struct GNUNET_SOCIAL_Place plc;
+
+  /**
+   * Slicer for processing incoming messages from guests.
+   */
+  struct GNUNET_PSYC_Slicer *slicer;
+
+  GNUNET_SOCIAL_HostEnterCallback enter_cb;
+
+  GNUNET_SOCIAL_AnswerDoorCallback answer_door_cb;
+
+  GNUNET_SOCIAL_FarewellCallback farewell_cb;
+
+  /**
+   * Closure for callbacks.
+   */
+  void *cb_cls;
+
+  struct GNUNET_SOCIAL_Nym *notice_place_leave_nym;
+  struct GNUNET_PSYC_Environment *notice_place_leave_env;
+};
+
+
+/**
+ * Guest handle for place that we entered.
+ */
+struct GNUNET_SOCIAL_Guest
+{
+  struct GNUNET_SOCIAL_Place plc;
+
+  GNUNET_SOCIAL_GuestEnterCallback enter_cb;
+
+  GNUNET_SOCIAL_EntryDecisionCallback entry_dcsn_cb;
+
+  /**
+   * Closure for callbacks.
+   */
+  void *cb_cls;
+};
+
+
+/**
+ * Hash map of all nyms.
+ * pub_key_hash -> struct GNUNET_SOCIAL_Nym *
+ */
+struct GNUNET_CONTAINER_MultiHashMap *nyms;
+
+
+/**
+ * Handle for an announcement request.
+ */
+struct GNUNET_SOCIAL_Announcement
+{
+
+};
+
+
+/**
+ * A talk request.
+ */
+struct GNUNET_SOCIAL_TalkRequest
+{
+
+};
+
+
+/**
+ * A history lesson.
+ */
+struct GNUNET_SOCIAL_HistoryRequest
+{
+  /**
+   * Place.
+   */
+  struct GNUNET_SOCIAL_Place *plc;
+
+  /**
+   * Operation ID.
+   */
+  uint64_t op_id;
+
+  /**
+   * Slicer for processing incoming messages.
+   */
+  struct GNUNET_PSYC_Slicer *slicer;
+
+  /**
+   * Function to call when the operation finished.
+   */
+  GNUNET_ResultCallback result_cb;
+
+  /**
+   * Closure for @a result_cb.
+   */
+  void *cls;
+};
+
+
+struct GNUNET_SOCIAL_LookHandle
+{
+  /**
+   * Place.
+   */
+  struct GNUNET_SOCIAL_Place *plc;
+
+  /**
+   * Operation ID.
+   */
+  uint64_t op_id;
+
+  /**
+   * State variable result callback.
+   */
+  GNUNET_PSYC_StateVarCallback var_cb;
+
+  /**
+   * Function to call when the operation finished.
+   */
+  GNUNET_ResultCallback result_cb;
+
+  /**
+   * Name of current modifier being received.
+   */
+  char *mod_name;
+
+  /**
+   * Size of current modifier value being received.
+   */
+  size_t mod_value_size;
+
+  /**
+   * Remaining size of current modifier value still to be received.
+   */
+  size_t mod_value_remaining;
+
+  /**
+   * Closure for @a result_cb.
+   */
+  void *cls;
+};
+
+
+struct ZoneAddPlaceHandle
+{
+  GNUNET_ResultCallback result_cb;
+  void *result_cls;
+};
+
+
+struct ZoneAddNymHandle
+{
+  GNUNET_ResultCallback result_cb;
+  void *result_cls;
+};
+
+
+/*** CLEANUP / DISCONNECT ***/
+
+
+static void
+host_cleanup (struct GNUNET_SOCIAL_Host *hst)
+{
+  if (NULL != hst->slicer)
+  {
+    GNUNET_PSYC_slicer_destroy (hst->slicer);
+    hst->slicer = NULL;
+  }
+  GNUNET_free (hst);
+}
+
+
+static void
+guest_cleanup (struct GNUNET_SOCIAL_Guest *gst)
+{
+  GNUNET_free (gst);
+}
+
+
+static void
+place_cleanup (struct GNUNET_SOCIAL_Place *plc)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "cleaning up place %p\n",
+              plc);
+  if (NULL != plc->tmit)
+  {
+    GNUNET_PSYC_transmit_destroy (plc->tmit);
+    plc->tmit = NULL;
+  }
+  if (NULL != plc->connect_env)
+  {
+    GNUNET_MQ_discard (plc->connect_env);
+    plc->connect_env = NULL;
+  }
+  if (NULL != plc->mq)
+  {
+    GNUNET_MQ_destroy (plc->mq);
+    plc->mq = NULL;
+  }
+  if (NULL != plc->disconnect_cb)
+  {
+    plc->disconnect_cb (plc->disconnect_cls);
+    plc->disconnect_cb = NULL;
+  }
+
+  (GNUNET_YES == plc->is_host)
+    ? host_cleanup ((struct GNUNET_SOCIAL_Host *) plc)
+    : guest_cleanup ((struct GNUNET_SOCIAL_Guest *) plc);
+}
+
+
+static void
+place_disconnect (struct GNUNET_SOCIAL_Place *plc)
+{
+  place_cleanup (plc);
+}
+
+
+/*** NYM ***/
+
+static struct GNUNET_SOCIAL_Nym *
+nym_get_or_create (const struct GNUNET_CRYPTO_EcdsaPublicKey *pub_key)
+{
+  struct GNUNET_SOCIAL_Nym *nym = NULL;
+  struct GNUNET_HashCode pub_key_hash;
+
+  if (NULL == pub_key)
+    return NULL;
+
+  GNUNET_CRYPTO_hash (pub_key, sizeof (*pub_key), &pub_key_hash);
+
+  if (NULL == nyms)
+    nyms = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES);
+  else
+    nym = GNUNET_CONTAINER_multihashmap_get (nyms, &pub_key_hash);
+
+  if (NULL == nym)
+  {
+    nym = GNUNET_new (struct GNUNET_SOCIAL_Nym);
+    nym->pub_key = *pub_key;
+    nym->pub_key_hash = pub_key_hash;
+    GNUNET_CONTAINER_multihashmap_put (nyms, &nym->pub_key_hash, nym,
+                                       
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
+  }
+  return nym;
+}
+
+
+static void
+nym_destroy (struct GNUNET_SOCIAL_Nym *nym)
+{
+  GNUNET_CONTAINER_multihashmap_remove (nyms, &nym->pub_key_hash, nym);
+  GNUNET_free (nym);
+}
+
+
+/*** MESSAGE HANDLERS ***/
+
+/** _notice_place_leave from guests */
+
+static void
+host_recv_notice_place_leave_method (void *cls,
+                                     const struct GNUNET_PSYC_MessageHeader 
*msg,
+                                     const struct GNUNET_PSYC_MessageMethod 
*meth,
+                                     uint64_t message_id,
+                                     const char *method_name)
+{
+  struct GNUNET_SOCIAL_Host *hst = cls;
+
+  if (0 == memcmp (&(struct GNUNET_CRYPTO_EcdsaPublicKey) {},
+                   &msg->slave_pub_key, sizeof (msg->slave_pub_key)))
+    return;
+
+  struct GNUNET_SOCIAL_Nym *nym = nym_get_or_create (&msg->slave_pub_key);
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Host received method for message ID %" PRIu64 " from nym %s: 
%s\n",
+              message_id, GNUNET_h2s (&nym->pub_key_hash), method_name);
+
+  hst->notice_place_leave_nym = (struct GNUNET_SOCIAL_Nym *) nym;
+  hst->notice_place_leave_env = GNUNET_PSYC_env_create ();
+
+  char *str = GNUNET_CRYPTO_ecdsa_public_key_to_string 
(&hst->notice_place_leave_nym->pub_key);
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "_notice_place_leave: got method from nym %s (%s).\n",
+              GNUNET_h2s (&hst->notice_place_leave_nym->pub_key_hash), str);
+  GNUNET_free (str);
+}
+
+
+static void
+host_recv_notice_place_leave_modifier (void *cls,
+                                       const struct GNUNET_PSYC_MessageHeader 
*msg,
+                                       const struct GNUNET_MessageHeader *pmsg,
+                                       uint64_t message_id,
+                                       enum GNUNET_PSYC_Operator oper,
+                                       const char *name,
+                                       const void *value,
+                                       uint16_t value_size,
+                                       uint16_t full_value_size)
+{
+  struct GNUNET_SOCIAL_Host *hst = cls;
+  if (NULL == hst->notice_place_leave_env)
+    return;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Host received modifier for _notice_place_leave message with ID 
%" PRIu64 ":\n"
+              "%c%s: %.*s\n",
+              message_id, oper, name, value_size, (const char *) value);
+
+  /* skip _nym, it's added later in eom() */
+  if (0 == memcmp (name, "_nym", sizeof ("_nym"))
+      || 0 == memcmp (name, "_nym_", sizeof ("_nym_") - 1))
+    return;
+
+  GNUNET_PSYC_env_add (hst->notice_place_leave_env,
+                       GNUNET_PSYC_OP_SET, name, value, value_size);
+}
+
+
+static void
+host_recv_notice_place_leave_eom (void *cls,
+                                  const struct GNUNET_PSYC_MessageHeader *msg,
+                                  const struct GNUNET_MessageHeader *pmsg,
+                                  uint64_t message_id,
+                                  uint8_t is_cancelled)
+{
+  struct GNUNET_SOCIAL_Host *hst = cls;
+  if (NULL == hst->notice_place_leave_env)
+    return;
+
+  char *str = GNUNET_CRYPTO_ecdsa_public_key_to_string 
(&hst->notice_place_leave_nym->pub_key);
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "_notice_place_leave: got EOM from nym %s (%s).\n",
+              GNUNET_h2s (&hst->notice_place_leave_nym->pub_key_hash), str);
+  GNUNET_free (str);
+
+  if (GNUNET_YES != is_cancelled)
+  {
+    if (NULL != hst->farewell_cb)
+      hst->farewell_cb (hst->cb_cls, hst->notice_place_leave_nym,
+                        hst->notice_place_leave_env);
+    /* announce leaving guest to place */
+    GNUNET_PSYC_env_add (hst->notice_place_leave_env, GNUNET_PSYC_OP_SET,
+                         "_nym", hst->notice_place_leave_nym,
+                         sizeof (*hst->notice_place_leave_nym));
+    GNUNET_SOCIAL_host_announce (hst, "_notice_place_leave",
+                                 hst->notice_place_leave_env,
+                                 NULL, NULL, GNUNET_SOCIAL_ANNOUNCE_NONE);
+    nym_destroy (hst->notice_place_leave_nym);
+  }
+  GNUNET_PSYC_env_destroy (hst->notice_place_leave_env);
+  hst->notice_place_leave_env = NULL;
+}
+
+
+/*** PLACE ***/
+
+
+static int
+check_place_result (void *cls,
+                    const struct GNUNET_OperationResultMessage *res)
+{
+  uint16_t size = ntohs (res->header.size);
+  if (size < sizeof (*res))
+  { /* Error, message too small. */
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+  return GNUNET_OK;
+}
+
+
+static void
+handle_place_result (void *cls,
+                     const struct GNUNET_OperationResultMessage *res)
+{
+  struct GNUNET_SOCIAL_Place *plc = cls;
+
+  uint16_t size = ntohs (res->header.size);
+  uint16_t data_size = size - sizeof (*res);
+  const char *data = (0 < data_size) ? (const char *) &res[1] : NULL;
+
+  GNUNET_OP_result (plc->op, GNUNET_ntohll (res->op_id),
+                    GNUNET_ntohll (res->result_code),
+                    data, data_size, NULL);
+}
+
+
+static int
+check_app_result (void *cls,
+                  const struct GNUNET_OperationResultMessage *res)
+{
+  uint16_t size = ntohs (res->header.size);
+  if (size < sizeof (*res))
+  { /* Error, message too small. */
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+  return GNUNET_OK;
+}
+
+
+static void
+handle_app_result (void *cls,
+                   const struct GNUNET_OperationResultMessage *res)
+{
+  struct GNUNET_SOCIAL_App *app = cls;
+
+  uint16_t size = ntohs (res->header.size);
+  uint16_t data_size = size - sizeof (*res);
+  const char *data = (0 < data_size) ? (const char *) &res[1] : NULL;
+
+  GNUNET_OP_result (app->op, GNUNET_ntohll (res->op_id),
+                    GNUNET_ntohll (res->result_code),
+                    data, data_size, NULL);
+}
+
+
+static void
+op_recv_history_result (void *cls, int64_t result,
+                        const void *err_msg, uint16_t err_msg_size)
+{
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Received history replay result: %" PRId64 ".\n", result);
+
+  struct GNUNET_SOCIAL_HistoryRequest *hist = cls;
+
+  if (NULL != hist->result_cb)
+    hist->result_cb (hist->cls, result, err_msg, err_msg_size);
+
+  GNUNET_free (hist);
+}
+
+
+static void
+op_recv_state_result (void *cls, int64_t result,
+                      const void *err_msg, uint16_t err_msg_size)
+{
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Received state request result: %" PRId64 ".\n", result);
+
+  struct GNUNET_SOCIAL_LookHandle *look = cls;
+
+  if (NULL != look->result_cb)
+    look->result_cb (look->cls, result, err_msg, err_msg_size);
+
+  GNUNET_free (look);
+}
+
+
+static int
+check_place_history_result (void *cls,
+                            const struct GNUNET_OperationResultMessage *res)
+{
+  struct GNUNET_PSYC_MessageHeader *
+    pmsg = (struct GNUNET_PSYC_MessageHeader *) GNUNET_MQ_extract_nested_mh 
(res);
+  uint16_t size = ntohs (res->header.size);
+
+  if (NULL == pmsg || size < sizeof (*res) + sizeof (*pmsg))
+  { /* Error, message too small. */
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+  return GNUNET_OK;
+}
+
+
+static void
+handle_place_history_result (void *cls,
+                             const struct GNUNET_OperationResultMessage *res)
+{
+  struct GNUNET_SOCIAL_Place *plc = cls;
+  struct GNUNET_PSYC_MessageHeader *
+    pmsg = (struct GNUNET_PSYC_MessageHeader *) GNUNET_MQ_extract_nested_mh 
(res);
+
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "%p Received historic fragment for message #%" PRIu64 ".\n",
+       plc, GNUNET_ntohll (pmsg->message_id));
+
+  GNUNET_ResultCallback result_cb = NULL;
+  struct GNUNET_SOCIAL_HistoryRequest *hist = NULL;
+
+  if (GNUNET_YES != GNUNET_OP_get (plc->op,
+                                   GNUNET_ntohll (res->op_id),
+                                   &result_cb, (void *) &hist, NULL))
+  { /* Operation not found. */
+    LOG (GNUNET_ERROR_TYPE_WARNING,
+         "%p Replay operation not found for historic fragment of message #%"
+         PRIu64 ".\n",
+         plc, GNUNET_ntohll (pmsg->message_id));
+    return;
+  }
+
+  GNUNET_PSYC_slicer_message (hist->slicer,
+                              (const struct GNUNET_PSYC_MessageHeader *) pmsg);
+}
+
+
+static int
+check_place_state_result (void *cls,
+                          const struct GNUNET_OperationResultMessage *res)
+{
+  const struct GNUNET_MessageHeader *mod = GNUNET_MQ_extract_nested_mh (res);
+  if (NULL == mod)
+  {
+    GNUNET_break_op (0);
+    LOG (GNUNET_ERROR_TYPE_WARNING,
+         "Invalid modifier in state result\n");
+    return GNUNET_SYSERR;
+  }
+
+  uint16_t size = ntohs (res->header.size);
+  uint16_t mod_size = ntohs (mod->size);
+  if (size - sizeof (*res) != mod_size)
+  {
+    GNUNET_break_op (0);
+    LOG (GNUNET_ERROR_TYPE_WARNING,
+         "Invalid modifier size in state result: %u - %u != %u\n",
+         ntohs (res->header.size), sizeof (*res), mod_size);
+    return GNUNET_SYSERR;
+  }
+  return GNUNET_OK;
+}
+
+
+static void
+handle_place_state_result (void *cls,
+                           const struct GNUNET_OperationResultMessage *res)
+{
+  struct GNUNET_SOCIAL_Place *plc = cls;
+
+  GNUNET_ResultCallback result_cb = NULL;
+  struct GNUNET_SOCIAL_LookHandle *look = NULL;
+
+  if (GNUNET_YES != GNUNET_OP_get (plc->op,
+                                   GNUNET_ntohll (res->op_id),
+                                   &result_cb, (void *) &look, NULL))
+  { /* Operation not found. */
+    return;
+  }
+
+  const struct GNUNET_MessageHeader *mod = GNUNET_MQ_extract_nested_mh (res);
+  uint16_t mod_size = ntohs (mod->size);
+
+  switch (ntohs (mod->type))
+  {
+  case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MODIFIER:
+  {
+    const struct GNUNET_PSYC_MessageModifier *
+      pmod = (const struct GNUNET_PSYC_MessageModifier *) mod;
+
+    const char *name = (const char *) &pmod[1];
+    uint16_t name_size = ntohs (pmod->name_size);
+    if (0 == name_size
+        || mod_size - sizeof (*pmod) < name_size
+        || '\0' != name[name_size - 1])
+    {
+      GNUNET_break_op (0);
+      LOG (GNUNET_ERROR_TYPE_WARNING,
+           "Invalid modifier name in state result\n");
+      return;
+    }
+    look->mod_value_size = ntohs (pmod->value_size);
+    look->var_cb (look->cls, mod, name, name + name_size,
+                  mod_size - sizeof (*mod) - name_size,
+                  look->mod_value_size);
+    if (look->mod_value_size > mod_size - sizeof (*mod) - name_size)
+    {
+        look->mod_value_remaining = look->mod_value_size;
+        look->mod_name = GNUNET_malloc (name_size);
+        GNUNET_memcpy (look->mod_name, name, name_size);
+    }
+    break;
+  }
+
+  case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MOD_CONT:
+    look->var_cb (look->cls, mod, look->mod_name, (const char *) &mod[1],
+                  mod_size - sizeof (*mod), look->mod_value_size);
+    look->mod_value_remaining -= mod_size - sizeof (*mod);
+    if (0 == look->mod_value_remaining)
+    {
+        GNUNET_free (look->mod_name);
+    }
+    break;
+  }
+}
+
+
+static void
+handle_place_message_ack (void *cls,
+                          const struct GNUNET_MessageHeader *msg)
+{
+  struct GNUNET_SOCIAL_Place *plc = cls;
+
+  GNUNET_PSYC_transmit_got_ack (plc->tmit);
+}
+
+
+static int
+check_place_message (void *cls,
+                     const struct GNUNET_PSYC_MessageHeader *pmsg)
+{
+  return GNUNET_OK;
+}
+
+
+static void
+handle_place_message (void *cls,
+                      const struct GNUNET_PSYC_MessageHeader *pmsg)
+{
+  struct GNUNET_SOCIAL_Place *plc = cls;
+
+  GNUNET_PSYC_slicer_message (plc->slicer, pmsg);
+}
+
+
+static int
+check_host_message (void *cls,
+                    const struct GNUNET_PSYC_MessageHeader *pmsg)
+{
+  return GNUNET_OK;
+}
+
+
+static void
+handle_host_message (void *cls,
+                     const struct GNUNET_PSYC_MessageHeader *pmsg)
+{
+  struct GNUNET_SOCIAL_Host *hst = cls;
+
+  GNUNET_PSYC_slicer_message (hst->slicer, pmsg);
+  GNUNET_PSYC_slicer_message (hst->plc.slicer, pmsg);
+}
+
+
+static void
+handle_host_enter_ack (void *cls,
+                       const struct HostEnterAck *hack)
+{
+  struct GNUNET_SOCIAL_Host *hst = cls;
+
+  hst->plc.pub_key = hack->place_pub_key;
+
+  int32_t result = ntohl (hack->result_code);
+  if (NULL != hst->enter_cb)
+    hst->enter_cb (hst->cb_cls, result, &hack->place_pub_key,
+                   GNUNET_ntohll (hack->max_message_id));
+}
+
+
+static int
+check_host_enter_request (void *cls,
+                          const struct GNUNET_PSYC_JoinRequestMessage *req)
+{
+  return GNUNET_OK;
+}
+
+
+static void
+handle_host_enter_request (void *cls,
+                           const struct GNUNET_PSYC_JoinRequestMessage *req)
+{
+  struct GNUNET_SOCIAL_Host *hst = cls;
+
+  if (NULL == hst->answer_door_cb)
+     return;
+
+  const char *method_name = NULL;
+  struct GNUNET_PSYC_Environment *env = NULL;
+  struct GNUNET_PSYC_MessageHeader *entry_pmsg = NULL;
+  const void *data = NULL;
+  uint16_t data_size = 0;
+  char *str;
+  const struct GNUNET_PSYC_Message *join_msg = NULL;
+
+  do
+  {
+    if (sizeof (*req) + sizeof (*join_msg) <= ntohs (req->header.size))
+    {
+      join_msg = (struct GNUNET_PSYC_Message *) GNUNET_MQ_extract_nested_mh 
(req);
+      LOG (GNUNET_ERROR_TYPE_DEBUG,
+           "Received join_msg of type %u and size %u.\n",
+           ntohs (join_msg->header.type), ntohs (join_msg->header.size));
+
+      env = GNUNET_PSYC_env_create ();
+      entry_pmsg = GNUNET_PSYC_message_header_create_from_psyc (join_msg);
+      if (GNUNET_OK != GNUNET_PSYC_message_parse (entry_pmsg, &method_name, 
env,
+                                                  &data, &data_size))
+      {
+        GNUNET_break_op (0);
+        str = GNUNET_CRYPTO_ecdsa_public_key_to_string (&req->slave_pub_key);
+        LOG (GNUNET_ERROR_TYPE_WARNING,
+             "Ignoring invalid entry request from nym %s.\n",
+             str);
+        GNUNET_free (str);
+        break;
+      }
+    }
+
+    struct GNUNET_SOCIAL_Nym *nym = nym_get_or_create (&req->slave_pub_key);
+    hst->answer_door_cb (hst->cb_cls, nym, method_name, env,
+                         data, data_size);
+  } while (0);
+
+  if (NULL != env)
+    GNUNET_PSYC_env_destroy (env);
+  if (NULL != entry_pmsg)
+    GNUNET_free (entry_pmsg);
+}
+
+
+static void
+handle_guest_enter_ack (void *cls,
+                        const struct GNUNET_PSYC_CountersResultMessage *cres)
+{
+  struct GNUNET_SOCIAL_Guest *gst = cls;
+
+  int32_t result = ntohl (cres->result_code);
+  if (NULL != gst->enter_cb)
+    gst->enter_cb (gst->cb_cls, result, &gst->plc.pub_key,
+                   GNUNET_ntohll (cres->max_message_id));
+}
+
+
+static int
+check_guest_enter_decision (void *cls,
+                            const struct GNUNET_PSYC_JoinDecisionMessage *dcsn)
+{
+  return GNUNET_OK;
+}
+
+
+static void
+handle_guest_enter_decision (void *cls,
+                             const struct GNUNET_PSYC_JoinDecisionMessage 
*dcsn)
+{
+  struct GNUNET_SOCIAL_Guest *gst = cls;
+
+  struct GNUNET_PSYC_Message *pmsg = NULL;
+  if (ntohs (dcsn->header.size) > sizeof (*dcsn))
+    pmsg = (struct GNUNET_PSYC_Message *) GNUNET_MQ_extract_nested_mh (dcsn);
+
+  if (NULL != gst->entry_dcsn_cb)
+    gst->entry_dcsn_cb (gst->cb_cls, ntohl (dcsn->is_admitted), pmsg);
+}
+
+
+static int
+check_app_ego (void *cls,
+               const struct AppEgoMessage *emsg)
+{
+  return GNUNET_OK;
+}
+
+
+static void
+handle_app_ego (void *cls,
+                const struct AppEgoMessage *emsg)
+{
+  struct GNUNET_SOCIAL_App *app = cls;
+
+  uint16_t name_size = ntohs (emsg->header.size) - sizeof (*emsg);
+
+  struct GNUNET_HashCode ego_pub_hash;
+  GNUNET_CRYPTO_hash (&emsg->ego_pub_key, sizeof (emsg->ego_pub_key),
+                      &ego_pub_hash);
+
+  struct GNUNET_SOCIAL_Ego *
+    ego = GNUNET_CONTAINER_multihashmap_get (app->egos, &ego_pub_hash);
+  if (NULL == ego)
+  {
+    ego = GNUNET_malloc (sizeof (*ego));
+    ego->pub_key = emsg->ego_pub_key;
+    ego->name = GNUNET_malloc (name_size);
+    GNUNET_memcpy (ego->name, &emsg[1], name_size);
+  }
+  else
+  {
+    ego->name = GNUNET_realloc (ego->name, name_size);
+    GNUNET_memcpy (ego->name, &emsg[1], name_size);
+  }
+
+  GNUNET_CONTAINER_multihashmap_put (app->egos, &ego_pub_hash, ego,
+                                     
GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE);
+
+  if (NULL != app->ego_cb)
+    app->ego_cb (app->cb_cls, ego, &ego->pub_key, ego->name);
+}
+
+
+static void
+handle_app_ego_end (void *cls,
+                    const struct GNUNET_MessageHeader *msg)
+{
+  //struct GNUNET_SOCIAL_App *app = cls;
+}
+
+
+static int
+check_app_place (void *cls,
+                 const struct AppPlaceMessage *pmsg)
+{
+  return GNUNET_OK;
+}
+
+
+static void
+handle_app_place (void *cls,
+                  const struct AppPlaceMessage *pmsg)
+{
+  struct GNUNET_SOCIAL_App *app = cls;
+
+  if ((GNUNET_YES == pmsg->is_host && NULL == app->host_cb)
+      || (GNUNET_NO == pmsg->is_host && NULL == app->guest_cb))
+    return;
+
+  struct GNUNET_HashCode ego_pub_hash;
+  GNUNET_CRYPTO_hash (&pmsg->ego_pub_key, sizeof (pmsg->ego_pub_key),
+                      &ego_pub_hash);
+  struct GNUNET_SOCIAL_Ego *
+    ego = GNUNET_CONTAINER_multihashmap_get (app->egos, &ego_pub_hash);
+  if (NULL == ego)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failure to obtain ego %s.\n",
+               GNUNET_h2s (&ego_pub_hash));
+    GNUNET_break (0);
+    return;
+  }
+
+  if (GNUNET_YES == pmsg->is_host)
+  {
+    if (NULL != app->host_cb) {
+      struct GNUNET_SOCIAL_HostConnection *hconn = GNUNET_malloc (sizeof 
(*hconn));
+      hconn->app = app;
+      hconn->plc_msg = *pmsg;
+      app->host_cb (app->cb_cls, hconn, ego, &pmsg->place_pub_key, 
pmsg->place_state);
+      GNUNET_free (hconn);
+    }
+  }
+  else if (NULL != app->guest_cb)
+  {
+    struct GNUNET_SOCIAL_GuestConnection *gconn = GNUNET_malloc (sizeof 
(*gconn));
+    gconn->app = app;
+    gconn->plc_msg = *pmsg;
+    app->guest_cb (app->cb_cls, gconn, ego, &pmsg->place_pub_key, 
pmsg->place_state);
+    GNUNET_free (gconn);
+  }
+}
+
+
+static void
+handle_app_place_end (void *cls,
+                      const struct GNUNET_MessageHeader *msg)
+{
+  struct GNUNET_SOCIAL_App *app = cls;
+
+  if (NULL != app->connected_cb)
+    app->connected_cb (app->cb_cls);
+}
+
+
+/**
+ * Handler for a #GNUNET_MESSAGE_TYPE_SOCIAL_PLACE_LEAVE_ACK message received
+ * from the social service.
+ *
+ * @param cls the place of type `struct GNUNET_SOCIAL_Place`
+ * @param msg the message received from the service
+ */
+static void
+handle_place_leave_ack (void *cls,
+                        const struct GNUNET_MessageHeader *msg)
+{
+  struct GNUNET_SOCIAL_Place *plc = cls;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%s left place %p\n",
+              plc->is_host ? "host" : "guest", 
+              plc);
+  place_disconnect (plc);  
+}
+
+
+/*** HOST ***/
+
+
+static void
+host_connect (struct GNUNET_SOCIAL_Host *hst);
+
+
+static void
+host_reconnect (void *cls)
+{
+  host_connect (cls);
+}
+
+
+/**
+ * Host client disconnected from service.
+ *
+ * Reconnect after backoff period.
+ */
+static void
+host_disconnected (void *cls, enum GNUNET_MQ_Error error)
+{
+  struct GNUNET_SOCIAL_Host *hst = cls;
+  struct GNUNET_SOCIAL_Place *plc = &hst->plc;
+
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Host client disconnected (%d), re-connecting\n",
+       (int) error);
+  if (NULL != plc->tmit)
+  {
+    GNUNET_PSYC_transmit_destroy (plc->tmit);
+    plc->tmit = NULL;
+  }
+  if (NULL != plc->mq)
+  {
+    GNUNET_MQ_destroy (plc->mq);
+    plc->mq = NULL;
+  }
+
+  plc->reconnect_task = GNUNET_SCHEDULER_add_delayed (plc->reconnect_delay,
+                                                      host_reconnect,
+                                                      hst);
+  plc->reconnect_delay = GNUNET_TIME_STD_BACKOFF (plc->reconnect_delay);
+}
+
+
+static void
+host_connect (struct GNUNET_SOCIAL_Host *hst)
+{
+  struct GNUNET_SOCIAL_Place *plc = &hst->plc;
+
+  struct GNUNET_MQ_MessageHandler handlers[] = {
+    GNUNET_MQ_hd_fixed_size (host_enter_ack,
+                             GNUNET_MESSAGE_TYPE_SOCIAL_HOST_ENTER_ACK,
+                             struct HostEnterAck,
+                             hst),
+    GNUNET_MQ_hd_fixed_size (place_leave_ack,
+                             GNUNET_MESSAGE_TYPE_SOCIAL_PLACE_LEAVE_ACK,
+                             struct GNUNET_MessageHeader,
+                             plc),
+    GNUNET_MQ_hd_var_size (host_enter_request,
+                           GNUNET_MESSAGE_TYPE_PSYC_JOIN_REQUEST,
+                           struct GNUNET_PSYC_JoinRequestMessage,
+                           hst),
+    GNUNET_MQ_hd_var_size (host_message,
+                           GNUNET_MESSAGE_TYPE_PSYC_MESSAGE,
+                           struct GNUNET_PSYC_MessageHeader,
+                           hst),
+    GNUNET_MQ_hd_fixed_size (place_message_ack,
+                             GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_ACK,
+                             struct GNUNET_MessageHeader,
+                             plc),
+    GNUNET_MQ_hd_var_size (place_history_result,
+                           GNUNET_MESSAGE_TYPE_PSYC_HISTORY_RESULT,
+                           struct GNUNET_OperationResultMessage,
+                           plc),
+    GNUNET_MQ_hd_var_size (place_state_result,
+                           GNUNET_MESSAGE_TYPE_PSYC_STATE_RESULT,
+                           struct GNUNET_OperationResultMessage,
+                           plc),
+    GNUNET_MQ_hd_var_size (place_result,
+                           GNUNET_MESSAGE_TYPE_PSYC_RESULT_CODE,
+                           struct GNUNET_OperationResultMessage,
+                           plc),
+    GNUNET_MQ_handler_end ()
+  };
+
+  plc->mq = GNUNET_CLIENT_connect (plc->cfg, "social",
+                                   handlers, host_disconnected, hst);
+  GNUNET_assert (NULL != plc->mq);
+  plc->tmit = GNUNET_PSYC_transmit_create (plc->mq);
+
+  GNUNET_MQ_send_copy (plc->mq, plc->connect_env);
+}
+
+
+/**
+ * Enter a place as host.
+ *
+ * A place is created upon first entering, and it is active until permanently
+ * left using GNUNET_SOCIAL_host_leave().
+ *
+ * @param app
+ *        Application handle.
+ * @param ego
+ *        Identity of the host.
+ * @param policy
+ *        Policy specifying entry and history restrictions for the place.
+ * @param slicer
+ *        Slicer to handle incoming messages.
+ * @param enter_cb
+ *        Function called when the place is entered and ready to use.
+ * @param answer_door_cb
+ *        Function to handle new nyms that want to enter.
+ * @param farewell_cb
+ *        Function to handle departing nyms.
+ * @param cls
+ *        Closure for the callbacks.
+ *
+ * @return Handle for the host. This handle contains the pubkey.
+ */
+struct GNUNET_SOCIAL_Host *
+GNUNET_SOCIAL_host_enter (const struct GNUNET_SOCIAL_App *app,
+                          const struct GNUNET_SOCIAL_Ego *ego,
+                          enum GNUNET_PSYC_Policy policy,
+                          struct GNUNET_PSYC_Slicer *slicer,
+                          GNUNET_SOCIAL_HostEnterCallback enter_cb,
+                          GNUNET_SOCIAL_AnswerDoorCallback answer_door_cb,
+                          GNUNET_SOCIAL_FarewellCallback farewell_cb,
+                          void *cls)
+{
+  struct GNUNET_SOCIAL_Host *hst = GNUNET_malloc (sizeof (*hst));
+  struct GNUNET_SOCIAL_Place *plc = &hst->plc;
+
+  plc->cfg = app->cfg;
+  plc->is_host = GNUNET_YES;
+  plc->slicer = slicer;
+
+  hst->enter_cb = enter_cb;
+  hst->answer_door_cb = answer_door_cb;
+  hst->farewell_cb = farewell_cb;
+  hst->cb_cls = cls;
+
+  plc->op = GNUNET_OP_create ();
+
+  hst->slicer = GNUNET_PSYC_slicer_create ();
+  GNUNET_PSYC_slicer_method_add (hst->slicer, "_notice_place_leave", NULL,
+                                 host_recv_notice_place_leave_method,
+                                 host_recv_notice_place_leave_modifier,
+                                 NULL, host_recv_notice_place_leave_eom, hst);
+
+  uint16_t app_id_size = strlen (app->id) + 1;
+  struct HostEnterRequest *hreq;
+  plc->connect_env = GNUNET_MQ_msg_extra (hreq, app_id_size,
+                                          
GNUNET_MESSAGE_TYPE_SOCIAL_HOST_ENTER);
+  hreq->policy = policy;
+  hreq->ego_pub_key = ego->pub_key;
+  GNUNET_memcpy (&hreq[1], app->id, app_id_size);
+
+  host_connect (hst);
+  return hst;
+}
+
+
+/**
+ * Reconnect to an already entered place as host.
+ *
+ * @param hconn
+ *        Host connection handle.
+ *        @see GNUNET_SOCIAL_app_connect() & 
GNUNET_SOCIAL_AppHostPlaceCallback()
+ * @param slicer
+ *        Slicer to handle incoming messages.
+ * @param enter_cb
+ *        Function called when the place is entered and ready to use.
+ * @param answer_door_cb
+ *        Function to handle new nyms that want to enter.
+ * @param farewell_cb
+ *        Function to handle departing nyms.
+ * @param cls
+ *        Closure for the callbacks.
+ *
+ * @return Handle for the host.
+ */
+ struct GNUNET_SOCIAL_Host *
+GNUNET_SOCIAL_host_enter_reconnect (struct GNUNET_SOCIAL_HostConnection *hconn,
+                                    struct GNUNET_PSYC_Slicer *slicer,
+                                    GNUNET_SOCIAL_HostEnterCallback enter_cb,
+                                    GNUNET_SOCIAL_AnswerDoorCallback 
answer_door_cb,
+                                    GNUNET_SOCIAL_FarewellCallback farewell_cb,
+                                    void *cls)
+{
+  struct GNUNET_SOCIAL_Host *hst = GNUNET_malloc (sizeof (*hst));
+  struct GNUNET_SOCIAL_Place *plc = &hst->plc;
+
+  hst->enter_cb = enter_cb;
+  hst->answer_door_cb = answer_door_cb;
+  hst->farewell_cb = farewell_cb;
+  hst->cb_cls = cls;
+
+  plc->cfg = hconn->app->cfg;
+  plc->is_host = GNUNET_YES;
+  plc->slicer = slicer;
+  plc->pub_key = hconn->plc_msg.place_pub_key;
+  plc->ego_pub_key = hconn->plc_msg.ego_pub_key;
+
+  plc->op = GNUNET_OP_create ();
+
+  hst->slicer = GNUNET_PSYC_slicer_create ();
+  GNUNET_PSYC_slicer_method_add (hst->slicer, "_notice_place_leave", NULL,
+                                 host_recv_notice_place_leave_method,
+                                 host_recv_notice_place_leave_modifier,
+                                 NULL, host_recv_notice_place_leave_eom, hst);
+
+  size_t app_id_size = strlen (hconn->app->id) + 1;
+  struct HostEnterRequest *hreq;
+  plc->connect_env = GNUNET_MQ_msg_extra (hreq, app_id_size,
+                                          
GNUNET_MESSAGE_TYPE_SOCIAL_HOST_ENTER);
+  hreq->place_pub_key = hconn->plc_msg.place_pub_key;
+  hreq->ego_pub_key = hconn->plc_msg.ego_pub_key;
+  GNUNET_memcpy (&hreq[1], hconn->app->id, app_id_size);
+
+  host_connect (hst);
+  return hst;
+}
+
+
+/**
+ * Decision whether to admit @a nym into the place or refuse entry.
+ *
+ * @param hst
+ *        Host of the place.
+ * @param nym
+ *        Handle for the entity that wanted to enter.
+ * @param is_admitted
+ *        #GNUNET_YES    if @a nym is admitted,
+ *        #GNUNET_NO     if @a nym is refused entry,
+ *        #GNUNET_SYSERR if we cannot answer the request.
+ * @param method_name
+ *        Method name for the rejection message.
+ * @param env
+ *        Environment containing variables for the message, or NULL.
+ * @param data
+ *        Data for the rejection message to send back.
+ * @param data_size
+ *        Number of bytes in @a data for method.
+ * @return #GNUNET_OK on success,
+ *         #GNUNET_SYSERR if the message is too large.
+ */
+int
+GNUNET_SOCIAL_host_entry_decision (struct GNUNET_SOCIAL_Host *hst,
+                                   struct GNUNET_SOCIAL_Nym *nym,
+                                   int is_admitted,
+                                   const struct GNUNET_PSYC_Message 
*entry_resp)
+{
+  struct GNUNET_SOCIAL_Place *plc = &hst->plc;
+  struct GNUNET_PSYC_JoinDecisionMessage *dcsn;
+  uint16_t entry_resp_size
+    = (NULL != entry_resp) ? ntohs (entry_resp->header.size) : 0;
+
+  if (GNUNET_MULTICAST_FRAGMENT_MAX_PAYLOAD < sizeof (*dcsn) + entry_resp_size)
+    return GNUNET_SYSERR;
+
+  struct GNUNET_MQ_Envelope *
+    env = GNUNET_MQ_msg_extra (dcsn, entry_resp_size,
+                               GNUNET_MESSAGE_TYPE_PSYC_JOIN_DECISION);
+  dcsn->is_admitted = htonl (is_admitted);
+  dcsn->slave_pub_key = nym->pub_key;
+
+  if (0 < entry_resp_size)
+    GNUNET_memcpy (&dcsn[1], entry_resp, entry_resp_size);
+
+  GNUNET_MQ_send (plc->mq, env);
+  return GNUNET_OK;
+}
+
+
+/**
+ * Throw @a nym out of the place.
+ *
+ * The @a nym reference will remain valid until the
+ * #GNUNET_SOCIAL_FarewellCallback is invoked,
+ * which should be very soon after this call.
+ *
+ * @param host
+ *        Host of the place.
+ * @param nym
+ *        Handle for the entity to be ejected.
+ * @param env
+ *        Environment for the message or NULL.
+ */
+void
+GNUNET_SOCIAL_host_eject (struct GNUNET_SOCIAL_Host *hst,
+                          const struct GNUNET_SOCIAL_Nym *nym,
+                          struct GNUNET_PSYC_Environment *e)
+{
+  struct GNUNET_PSYC_Environment *env = e;
+  if (NULL == env)
+    env = GNUNET_PSYC_env_create ();
+  GNUNET_PSYC_env_add (env, GNUNET_PSYC_OP_SET,
+                       "_nym", &nym->pub_key, sizeof (nym->pub_key));
+  GNUNET_SOCIAL_host_announce (hst, "_notice_place_leave", env, NULL, NULL,
+                               GNUNET_SOCIAL_ANNOUNCE_NONE);
+  if (NULL == e)
+    GNUNET_PSYC_env_destroy (env);
+}
+
+
+/**
+ * Get the public key of @a ego.
+ *
+ * @param ego
+ *        Ego.
+ *
+ * @return Public key of ego.
+ */
+const struct GNUNET_CRYPTO_EcdsaPublicKey *
+GNUNET_SOCIAL_ego_get_pub_key (const struct GNUNET_SOCIAL_Ego *ego)
+{
+  return &ego->pub_key;
+}
+
+
+/**
+ * Get the hash of the public key of @a ego.
+ *
+ * @param ego
+ *        Ego.
+ *
+ * @return Hash of the public key of @a ego.
+ */
+const struct GNUNET_HashCode *
+GNUNET_SOCIAL_ego_get_pub_key_hash (const struct GNUNET_SOCIAL_Ego *ego)
+{
+  return &ego->pub_key_hash;
+}
+
+
+/**
+ * Get the name of @a ego.
+ *
+ * @param ego
+ *        Ego.
+ *
+ * @return Public key of @a ego.
+ */
+const char *
+GNUNET_SOCIAL_ego_get_name (const struct GNUNET_SOCIAL_Ego *ego)
+{
+  return ego->name;
+}
+
+
+/**
+ * Get the public key of @a nym.
+ *
+ * Suitable, for example, to be used with GNUNET_SOCIAL_zone_add_nym().
+ *
+ * @param nym
+ *        Pseudonym.
+ *
+ * @return Public key of @a nym.
+ */
+const struct GNUNET_CRYPTO_EcdsaPublicKey *
+GNUNET_SOCIAL_nym_get_pub_key (const struct GNUNET_SOCIAL_Nym *nym)
+{
+  return &nym->pub_key;
+}
+
+
+/**
+ * Get the hash of the public key of @a nym.
+ *
+ * @param nym
+ *        Pseudonym.
+ *
+ * @return Hash of the public key of @a nym.
+ */
+const struct GNUNET_HashCode *
+GNUNET_SOCIAL_nym_get_pub_key_hash (const struct GNUNET_SOCIAL_Nym *nym)
+{
+  return &nym->pub_key_hash;
+}
+
+
+/**
+ * Send a message to all nyms that are present in the place.
+ *
+ * This function is restricted to the host.  Nyms can only send requests
+ * to the host who can decide to relay it to everyone in the place.
+ *
+ * @param host  Host of the place.
+ * @param method_name Method to use for the announcement.
+ * @param env  Environment containing variables for the message and operations
+ *          on objects of the place.  Can be NULL.
+ * @param notify Function to call to get the payload of the announcement.
+ * @param notify_cls Closure for @a notify.
+ * @param flags Flags for this announcement.
+ *
+ * @return NULL on error (announcement already in progress?).
+ */
+struct GNUNET_SOCIAL_Announcement *
+GNUNET_SOCIAL_host_announce (struct GNUNET_SOCIAL_Host *hst,
+                             const char *method_name,
+                             const struct GNUNET_PSYC_Environment *env,
+                             GNUNET_PSYC_TransmitNotifyData notify_data,
+                             void *notify_data_cls,
+                             enum GNUNET_SOCIAL_AnnounceFlags flags)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "PSYC_transmit_message for host, method: %s\n",
+              method_name);
+  if (GNUNET_OK ==
+      GNUNET_PSYC_transmit_message (hst->plc.tmit, method_name, env,
+                                    NULL, notify_data, notify_data_cls, flags))
+    return (struct GNUNET_SOCIAL_Announcement *) hst->plc.tmit;
+  else
+    return NULL;
+}
+
+
+/**
+ * Resume transmitting announcement.
+ *
+ * @param a
+ *        The announcement to resume.
+ */
+void
+GNUNET_SOCIAL_host_announce_resume (struct GNUNET_SOCIAL_Announcement *a)
+{
+  GNUNET_PSYC_transmit_resume ((struct GNUNET_PSYC_TransmitHandle *) a);
+}
+
+
+/**
+ * Cancel announcement.
+ *
+ * @param a
+ *        The announcement to cancel.
+ */
+void
+GNUNET_SOCIAL_host_announce_cancel (struct GNUNET_SOCIAL_Announcement *a)
+{
+  GNUNET_PSYC_transmit_cancel ((struct GNUNET_PSYC_TransmitHandle *) a);
+}
+
+
+/**
+ * Obtain handle for a hosted place.
+ *
+ * The returned handle can be used to access the place API.
+ *
+ * @param host  Handle for the host.
+ *
+ * @return Handle for the hosted place, valid as long as @a host is valid.
+ */
+struct GNUNET_SOCIAL_Place *
+GNUNET_SOCIAL_host_get_place (struct GNUNET_SOCIAL_Host *hst)
+{
+  return &hst->plc;
+}
+
+
+/**
+ * Disconnect from a home.
+ *
+ * Invalidates host handle.
+ *
+ * @param hst
+ *        The host to disconnect.
+ */
+void
+GNUNET_SOCIAL_host_disconnect (struct GNUNET_SOCIAL_Host *hst,
+                               GNUNET_ContinuationCallback disconnect_cb,
+                               void *cls)
+{
+  struct GNUNET_SOCIAL_Place *plc = &hst->plc;
+
+  plc->disconnect_cb = disconnect_cb;
+  plc->disconnect_cls = cls;
+  place_disconnect (plc);
+}
+
+
+/**
+ * Stop hosting the home.
+ *
+ * Sends a _notice_place_closing announcement to the home.
+ * Invalidates host handle.
+ *
+ * @param hst
+ *        The host leaving.
+ * @param env
+ *        Environment for the message or NULL.
+ *        _nym is set to @e nym regardless whether an @e env is provided.
+ * @param disconnect_cb
+ *        Function called after the host left the place
+ *        and disconnected from the social service.
+ * @param cls
+ *        Closure for @a disconnect_cb.
+ */
+void
+GNUNET_SOCIAL_host_leave (struct GNUNET_SOCIAL_Host *hst,
+                          const struct GNUNET_PSYC_Environment *env,
+                          GNUNET_ContinuationCallback disconnect_cb,
+                          void *cls)
+{
+  struct GNUNET_MQ_Envelope *envelope;
+
+  GNUNET_SOCIAL_host_announce (hst, "_notice_place_closing", env, NULL, NULL,
+                               GNUNET_SOCIAL_ANNOUNCE_NONE);
+  hst->plc.disconnect_cb = disconnect_cb;
+  hst->plc.disconnect_cls = cls;
+  envelope = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_SOCIAL_PLACE_LEAVE);
+  GNUNET_MQ_send (hst->plc.mq,
+                  envelope);
+}
+
+
+/*** GUEST ***/
+
+
+static void
+guest_connect (struct GNUNET_SOCIAL_Guest *gst);
+
+
+static void
+guest_reconnect (void *cls)
+{
+  guest_connect (cls);
+}
+
+
+/**
+ * Guest client disconnected from service.
+ *
+ * Reconnect after backoff period.
+ */
+static void
+guest_disconnected (void *cls, enum GNUNET_MQ_Error error)
+{
+  struct GNUNET_SOCIAL_Guest *gst = cls;
+  struct GNUNET_SOCIAL_Place *plc = &gst->plc;
+
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Guest client disconnected (%d), re-connecting\n",
+       (int) error);
+  if (NULL != plc->tmit)
+  {
+    GNUNET_PSYC_transmit_destroy (plc->tmit);
+    plc->tmit = NULL;
+  }
+  if (NULL != plc->mq)
+  {
+    GNUNET_MQ_destroy (plc->mq);
+    plc->mq = NULL;
+  }
+
+  plc->reconnect_task = GNUNET_SCHEDULER_add_delayed (plc->reconnect_delay,
+                                                      guest_reconnect,
+                                                      gst);
+  plc->reconnect_delay = GNUNET_TIME_STD_BACKOFF (plc->reconnect_delay);
+}
+
+
+static void
+guest_connect (struct GNUNET_SOCIAL_Guest *gst)
+{
+  struct GNUNET_SOCIAL_Place *plc = &gst->plc;
+
+  struct GNUNET_MQ_MessageHandler handlers[] = {
+    GNUNET_MQ_hd_fixed_size (guest_enter_ack,
+                             GNUNET_MESSAGE_TYPE_SOCIAL_GUEST_ENTER_ACK,
+                             struct GNUNET_PSYC_CountersResultMessage,
+                             gst),
+    GNUNET_MQ_hd_fixed_size (place_leave_ack,
+                             GNUNET_MESSAGE_TYPE_SOCIAL_PLACE_LEAVE_ACK,
+                             struct GNUNET_MessageHeader,
+                             plc),
+    GNUNET_MQ_hd_var_size (guest_enter_decision,
+                           GNUNET_MESSAGE_TYPE_PSYC_JOIN_DECISION,
+                           struct GNUNET_PSYC_JoinDecisionMessage,
+                           gst),
+    GNUNET_MQ_hd_var_size (place_message,
+                           GNUNET_MESSAGE_TYPE_PSYC_MESSAGE,
+                           struct GNUNET_PSYC_MessageHeader,
+                           plc),
+    GNUNET_MQ_hd_fixed_size (place_message_ack,
+                             GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_ACK,
+                             struct GNUNET_MessageHeader,
+                             plc),
+    GNUNET_MQ_hd_var_size (place_history_result,
+                           GNUNET_MESSAGE_TYPE_PSYC_HISTORY_RESULT,
+                           struct GNUNET_OperationResultMessage,
+                           plc),
+    GNUNET_MQ_hd_var_size (place_state_result,
+                           GNUNET_MESSAGE_TYPE_PSYC_STATE_RESULT,
+                           struct GNUNET_OperationResultMessage,
+                           plc),
+    GNUNET_MQ_hd_var_size (place_result,
+                           GNUNET_MESSAGE_TYPE_PSYC_RESULT_CODE,
+                           struct GNUNET_OperationResultMessage,
+                           plc),
+    GNUNET_MQ_handler_end ()
+  };
+
+  plc->mq = GNUNET_CLIENT_connect (plc->cfg, "social",
+                                   handlers, guest_disconnected, gst);
+  GNUNET_assert (NULL != plc->mq);
+  plc->tmit = GNUNET_PSYC_transmit_create (plc->mq);
+
+  GNUNET_MQ_send_copy (plc->mq, plc->connect_env);
+}
+
+
+static struct GNUNET_MQ_Envelope *
+guest_enter_request_create (const char *app_id,
+                            const struct GNUNET_CRYPTO_EcdsaPublicKey 
*ego_pub_key,
+                            const struct GNUNET_CRYPTO_EddsaPublicKey 
*place_pub_key,
+                            const struct GNUNET_PeerIdentity *origin,
+                            size_t relay_count,
+                            const struct GNUNET_PeerIdentity *relays,
+                            const struct GNUNET_PSYC_Message *join_msg)
+{
+  uint16_t app_id_size = strlen (app_id) + 1;
+  uint16_t join_msg_size = ntohs (join_msg->header.size);
+  uint16_t relay_size = relay_count * sizeof (*relays);
+
+  struct GuestEnterRequest *greq;
+  struct GNUNET_MQ_Envelope *
+    env = GNUNET_MQ_msg_extra (greq, app_id_size + relay_size + join_msg_size,
+                               GNUNET_MESSAGE_TYPE_SOCIAL_GUEST_ENTER);
+  greq->place_pub_key = *place_pub_key;
+  greq->ego_pub_key = *ego_pub_key;
+  greq->origin = *origin;
+  greq->relay_count = htonl (relay_count);
+
+  char *p = (char *) &greq[1];
+  GNUNET_memcpy (p, app_id, app_id_size);
+  p += app_id_size;
+
+  if (0 < relay_size)
+  {
+    GNUNET_memcpy (p, relays, relay_size);
+    p += relay_size;
+  }
+
+  GNUNET_memcpy (p, join_msg, join_msg_size);
+  return env;
+}
+
+
+/**
+ * Request entry to a place as a guest.
+ *
+ * @param app
+ *        Application handle.
+ * @param ego
+ *        Identity of the guest.
+ * @param place_pub_key
+ *        Public key of the place to enter.
+ * @param flags
+ *        Flags for the entry.
+ * @param origin
+ *        Peer identity of the origin of the underlying multicast group.
+ * @param relay_count
+ *        Number of elements in the @a relays array.
+ * @param relays
+ *        Relays for the underlying multicast group.
+ * @param method_name
+ *        Method name for the message.
+ * @param env
+ *        Environment containing variables for the message, or NULL.
+ * @param data
+ *        Payload for the message to give to the enter callback.
+ * @param data_size
+ *        Number of bytes in @a data.
+ * @param slicer
+ *        Slicer to use for processing incoming requests from guests.
+ *
+ * @return NULL on errors, otherwise handle for the guest.
+ */
+struct GNUNET_SOCIAL_Guest *
+GNUNET_SOCIAL_guest_enter (const struct GNUNET_SOCIAL_App *app,
+                           const struct GNUNET_SOCIAL_Ego *ego,
+                           const struct GNUNET_CRYPTO_EddsaPublicKey 
*place_pub_key,
+                           enum GNUNET_PSYC_SlaveJoinFlags flags,
+                           const struct GNUNET_PeerIdentity *origin,
+                           uint32_t relay_count,
+                           const struct GNUNET_PeerIdentity *relays,
+                           const struct GNUNET_PSYC_Message *entry_msg,
+                           struct GNUNET_PSYC_Slicer *slicer,
+                           GNUNET_SOCIAL_GuestEnterCallback local_enter_cb,
+                           GNUNET_SOCIAL_EntryDecisionCallback entry_dcsn_cb,
+                           void *cls)
+{
+  struct GNUNET_SOCIAL_Guest *gst = GNUNET_malloc (sizeof (*gst));
+  struct GNUNET_SOCIAL_Place *plc = &gst->plc;
+
+  plc->ego_pub_key = ego->pub_key;
+  plc->pub_key = *place_pub_key;
+  plc->cfg = app->cfg;
+  plc->is_host = GNUNET_NO;
+  plc->slicer = slicer;
+
+  plc->op = GNUNET_OP_create ();
+
+  plc->connect_env
+    = guest_enter_request_create (app->id, &ego->pub_key, &plc->pub_key,
+                                  origin, relay_count, relays, entry_msg);
+
+  gst->enter_cb = local_enter_cb;
+  gst->entry_dcsn_cb = entry_dcsn_cb;
+  gst->cb_cls = cls;
+
+  guest_connect (gst);
+  return gst;
+}
+
+
+/**
+ * Request entry to a place by name as a guest.
+ *
+ * @param app
+ *        Application handle.
+ * @param ego
+ *        Identity of the guest.
+ * @param gns_name
+ *        GNS name of the place to enter.  Either in the form of
+ *        'room.friend.gnu', or 'NYMPUBKEY.zkey'.  This latter case refers to
+ *        the 'PLACE' record of the empty label ("+") in the GNS zone with the
+ *        nym's public key 'NYMPUBKEY', and can be used to request entry to a
+ *        pseudonym's place directly.
+ * @param password
+ *        Password to decrypt the record, or NULL for cleartext records.
+ * @param join_msg
+ *        Entry request message or NULL.
+ * @param slicer
+ *        Slicer to use for processing incoming requests from guests.
+ * @param local_enter_cb
+ *        Called upon connection established to the social service.
+ * @param entry_decision_cb
+ *        Called upon receiving entry decision.
+ *
+ * @return NULL on errors, otherwise handle for the guest.
+ */
+struct GNUNET_SOCIAL_Guest *
+GNUNET_SOCIAL_guest_enter_by_name (const struct GNUNET_SOCIAL_App *app,
+                                   const struct GNUNET_SOCIAL_Ego *ego,
+                                   const char *gns_name,
+                                   const char *password,
+                                   const struct GNUNET_PSYC_Message *join_msg,
+                                   struct GNUNET_PSYC_Slicer *slicer,
+                                   GNUNET_SOCIAL_GuestEnterCallback 
local_enter_cb,
+                                   GNUNET_SOCIAL_EntryDecisionCallback 
entry_decision_cb,
+                                   void *cls)
+{
+  struct GNUNET_SOCIAL_Guest *gst = GNUNET_malloc (sizeof (*gst));
+  struct GNUNET_SOCIAL_Place *plc = &gst->plc;
+
+  if (NULL == password)
+    password = "";
+
+  uint16_t app_id_size = strlen (app->id) + 1;
+  uint16_t gns_name_size = strlen (gns_name) + 1;
+  uint16_t password_size = strlen (password) + 1;
+
+  uint16_t join_msg_size = 0;
+  if (NULL != join_msg)
+    join_msg_size = ntohs (join_msg->header.size);
+
+  struct GuestEnterByNameRequest *greq;
+  plc->connect_env
+    = GNUNET_MQ_msg_extra (greq, app_id_size + gns_name_size
+                           + password_size + join_msg_size,
+                           GNUNET_MESSAGE_TYPE_SOCIAL_GUEST_ENTER_BY_NAME);
+
+  greq->ego_pub_key = ego->pub_key;
+
+  char *p = (char *) &greq[1];
+  GNUNET_memcpy (p, app->id, app_id_size);
+  p += app_id_size;
+  GNUNET_memcpy (p, gns_name, gns_name_size);
+  p += gns_name_size;
+  GNUNET_memcpy (p, password, password_size);
+  p += password_size;
+  if (NULL != join_msg)
+    GNUNET_memcpy (p, join_msg, join_msg_size);
+
+  plc->ego_pub_key = ego->pub_key;
+  plc->cfg = app->cfg;
+  plc->is_host = GNUNET_NO;
+  plc->slicer = slicer;
+
+  plc->op = GNUNET_OP_create ();
+
+  gst->enter_cb = local_enter_cb;
+  gst->entry_dcsn_cb = entry_decision_cb;
+  gst->cb_cls = cls;
+
+  guest_connect (gst);
+  return gst;
+}
+
+
+struct ReconnectContext
+{
+  struct GNUNET_SOCIAL_Guest *guest;
+  int *result;
+  int64_t *max_message_id;
+  GNUNET_SOCIAL_GuestEnterCallback enter_cb;
+  void *enter_cls;
+};
+
+
+static void
+guest_enter_reconnect_cb (void *cls,
+                          int result,
+                          const struct GNUNET_CRYPTO_EddsaPublicKey 
*place_pub_key,
+                          uint64_t max_message_id)
+{
+  struct ReconnectContext *reconnect_ctx = cls;
+
+  GNUNET_assert (NULL != reconnect_ctx);
+  reconnect_ctx->result = GNUNET_new (int);
+  *(reconnect_ctx->result) = result; 
+  reconnect_ctx->max_message_id = GNUNET_new (int64_t);
+  *(reconnect_ctx->max_message_id) = max_message_id;
+}
+
+
+static void
+guest_entry_dcsn_reconnect_cb (void *cls,
+                               int is_admitted,
+                               const struct GNUNET_PSYC_Message *entry_resp)
+{
+  struct ReconnectContext *reconnect_ctx = cls;
+  struct GNUNET_SOCIAL_Guest *gst = reconnect_ctx->guest;
+
+  GNUNET_assert (NULL != reconnect_ctx);
+  GNUNET_assert (NULL != reconnect_ctx->result);
+  GNUNET_assert (NULL != reconnect_ctx->max_message_id);
+  if (GNUNET_YES != is_admitted)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Guest was rejected after calling "
+                "GNUNET_SOCIAL_guest_enter_reconnect ()\n");
+  }
+  else if (NULL != reconnect_ctx->enter_cb)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "guest reconnected!\n");
+    reconnect_ctx->enter_cb (reconnect_ctx->enter_cls,
+                             *(reconnect_ctx->result),
+                             &gst->plc.pub_key,
+                             *(reconnect_ctx->max_message_id));
+  }
+  GNUNET_free (reconnect_ctx->result);
+  GNUNET_free (reconnect_ctx->max_message_id);
+  GNUNET_free (reconnect_ctx);
+}
+
+
+/**
+ * Reconnect to an already entered place as guest.
+ *
+ * @param gconn
+ *        Guest connection handle.
+ *        @see GNUNET_SOCIAL_app_connect() & 
GNUNET_SOCIAL_AppGuestPlaceCallback()
+ * @param flags
+ *        Flags for the entry.
+ * @param slicer
+ *        Slicer to use for processing incoming requests from guests.
+ * @param enter_cb
+ *        Called upon re-entering is complete.
+ * @param entry_decision_cb
+ *        Called upon receiving entry decision.
+ *
+ * @return NULL on errors, otherwise handle for the guest.
+ */
+struct GNUNET_SOCIAL_Guest *
+GNUNET_SOCIAL_guest_enter_reconnect (struct GNUNET_SOCIAL_GuestConnection 
*gconn,
+                                     enum GNUNET_PSYC_SlaveJoinFlags flags,
+                                     struct GNUNET_PSYC_Slicer *slicer,
+                                     GNUNET_SOCIAL_GuestEnterCallback enter_cb,
+                                     void *cls)
+{
+  struct GNUNET_SOCIAL_Guest *gst = GNUNET_malloc (sizeof (*gst));
+  struct GNUNET_SOCIAL_Place *plc = &gst->plc;
+  struct ReconnectContext *reconnect_ctx;
+
+  uint16_t app_id_size = strlen (gconn->app->id) + 1;
+  struct GuestEnterRequest *greq;
+  plc->connect_env
+    = GNUNET_MQ_msg_extra (greq, app_id_size,
+                           GNUNET_MESSAGE_TYPE_SOCIAL_GUEST_ENTER);
+  greq->ego_pub_key = gconn->plc_msg.ego_pub_key;
+  greq->place_pub_key = gconn->plc_msg.place_pub_key;
+  greq->flags = htonl (flags);
+
+  GNUNET_memcpy (&greq[1], gconn->app->id, app_id_size);
+
+  plc->cfg = gconn->app->cfg;
+  plc->is_host = GNUNET_NO;
+  plc->slicer = slicer;
+  plc->pub_key = gconn->plc_msg.place_pub_key;
+  plc->ego_pub_key = gconn->plc_msg.ego_pub_key;
+
+  reconnect_ctx = GNUNET_new (struct ReconnectContext);
+  reconnect_ctx->guest = gst;
+  reconnect_ctx->enter_cb = enter_cb;
+  reconnect_ctx->enter_cls = cls;
+
+  plc->op = GNUNET_OP_create ();
+  gst->enter_cb = &guest_enter_reconnect_cb;
+  gst->entry_dcsn_cb = &guest_entry_dcsn_reconnect_cb;
+  gst->cb_cls = reconnect_ctx;
+
+  guest_connect (gst);
+  return gst;
+}
+
+
+/**
+ * Talk to the host of the place.
+ *
+ * @param place
+ *        Place where we want to talk to the host.
+ * @param method_name
+ *        Method to invoke on the host.
+ * @param env
+ *        Environment containing variables for the message, or NULL.
+ * @param notify_data
+ *        Function to use to get the payload for the method.
+ * @param notify_data_cls
+ *        Closure for @a notify_data.
+ * @param flags
+ *        Flags for the message being sent.
+ *
+ * @return NULL if we are already trying to talk to the host,
+ *         otherwise handle to cancel the request.
+ */
+struct GNUNET_SOCIAL_TalkRequest *
+GNUNET_SOCIAL_guest_talk (struct GNUNET_SOCIAL_Guest *gst,
+                          const char *method_name,
+                          const struct GNUNET_PSYC_Environment *env,
+                          GNUNET_PSYC_TransmitNotifyData notify_data,
+                          void *notify_data_cls,
+                          enum GNUNET_SOCIAL_TalkFlags flags)
+{
+  struct GNUNET_SOCIAL_Place *plc = &gst->plc;
+  GNUNET_assert (NULL != plc->tmit);
+
+  if (GNUNET_OK ==
+      GNUNET_PSYC_transmit_message (plc->tmit, method_name, env,
+                                    NULL, notify_data, notify_data_cls, flags))
+    return (struct GNUNET_SOCIAL_TalkRequest *) plc->tmit;
+  else
+    return NULL;
+}
+
+
+/**
+ * Resume talking to the host of the place.
+ *
+ * @param tr
+ *        Talk request to resume.
+ */
+void
+GNUNET_SOCIAL_guest_talk_resume (struct GNUNET_SOCIAL_TalkRequest *tr)
+{
+  GNUNET_PSYC_transmit_resume ((struct GNUNET_PSYC_TransmitHandle *) tr);
+}
+
+
+/**
+ * Cancel talking to the host of the place.
+ *
+ * @param tr
+ *        Talk request to cancel.
+ */
+void
+GNUNET_SOCIAL_guest_talk_cancel (struct GNUNET_SOCIAL_TalkRequest *tr)
+{
+  GNUNET_PSYC_transmit_cancel ( (struct GNUNET_PSYC_TransmitHandle *) tr);
+}
+
+
+/**
+ * Disconnect from a place.
+ *
+ * Invalidates guest handle.
+ *
+ * @param gst
+ *        The guest to disconnect.
+ */
+void
+GNUNET_SOCIAL_guest_disconnect (struct GNUNET_SOCIAL_Guest *gst,
+                                GNUNET_ContinuationCallback disconnect_cb,
+                                void *cls)
+{
+  struct GNUNET_SOCIAL_Place *plc = &gst->plc;
+
+  plc->disconnect_cb = disconnect_cb;
+  plc->disconnect_cls = cls;
+  place_disconnect (plc);
+}
+
+
+/**
+ * Leave a place temporarily or permanently.
+ *
+ * Notifies the owner of the place about leaving, and destroys the place 
handle.
+ *
+ * @param place
+ *        Place to leave.
+ * @param keep_active
+ *        Keep place active after last application disconnected.
+ *        #GNUNET_YES or #GNUNET_NO
+ * @param env
+ *        Optional environment for the leave message if @a keep_active
+ *        is #GNUNET_NO.  NULL if not needed.
+ * @param leave_cb
+ *        Called upon disconnecting from the social service.
+ */
+void
+GNUNET_SOCIAL_guest_leave (struct GNUNET_SOCIAL_Guest *gst,
+                           struct GNUNET_PSYC_Environment *env,
+                           GNUNET_ContinuationCallback disconnect_cb,
+                           void *cls)
+{
+  struct GNUNET_MQ_Envelope *envelope;
+
+  GNUNET_SOCIAL_guest_talk (gst, "_notice_place_leave", env, NULL, NULL,
+                            GNUNET_SOCIAL_TALK_NONE);
+  gst->plc.disconnect_cb = disconnect_cb;
+  gst->plc.disconnect_cls = cls;
+  envelope = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_SOCIAL_PLACE_LEAVE);
+  GNUNET_MQ_send (gst->plc.mq,
+                  envelope);
+}
+
+
+/**
+ * Obtain handle for a place entered as guest.
+ *
+ * The returned handle can be used to access the place API.
+ *
+ * @param guest  Handle for the guest.
+ *
+ * @return Handle for the place, valid as long as @a guest is valid.
+ */
+struct GNUNET_SOCIAL_Place *
+GNUNET_SOCIAL_guest_get_place (struct GNUNET_SOCIAL_Guest *gst)
+{
+  return &gst->plc;
+}
+
+
+/**
+ * Obtain the public key of a place.
+ *
+ * @param plc
+ *        Place.
+ *
+ * @return Public key of the place.
+ */
+const struct GNUNET_CRYPTO_EddsaPublicKey *
+GNUNET_SOCIAL_place_get_pub_key (const struct GNUNET_SOCIAL_Place *plc)
+{
+  return &plc->pub_key;
+}
+
+
+/**
+ * Set message processing @a flags for a @a method_prefix.
+ *
+ * @param plc
+ *        Place.
+ * @param method_prefix
+ *        Method prefix @a flags apply to.
+ * @param flags
+ *        The flags that apply to a matching @a method_prefix.
+ */
+void
+GNUNET_SOCIAL_place_msg_proc_set (struct GNUNET_SOCIAL_Place *plc,
+                                  const char *method_prefix,
+                                  enum GNUNET_SOCIAL_MsgProcFlags flags)
+{
+  GNUNET_assert (NULL != method_prefix);
+  struct MsgProcRequest *mpreq;
+  uint16_t method_size = strnlen (method_prefix,
+                                  GNUNET_MAX_MESSAGE_SIZE
+                                  - sizeof (*mpreq)) + 1;
+  GNUNET_assert ('\0' == method_prefix[method_size - 1]);
+
+  struct GNUNET_MQ_Envelope *
+    env = GNUNET_MQ_msg_extra (mpreq, method_size,
+                               GNUNET_MESSAGE_TYPE_SOCIAL_MSG_PROC_SET);
+  mpreq->flags = htonl (flags);
+  GNUNET_memcpy (&mpreq[1], method_prefix, method_size);
+
+  GNUNET_MQ_send (plc->mq, env);
+}
+
+
+/**
+ * Clear all message processing flags previously set for this place.
+ */
+void
+GNUNET_SOCIAL_place_msg_proc_clear (struct GNUNET_SOCIAL_Place *plc)
+{
+  struct GNUNET_MessageHeader *req;
+  struct GNUNET_MQ_Envelope *
+    env = GNUNET_MQ_msg (req, GNUNET_MESSAGE_TYPE_SOCIAL_MSG_PROC_CLEAR);
+
+  GNUNET_MQ_send (plc->mq, env);
+}
+
+
+static struct GNUNET_SOCIAL_HistoryRequest *
+place_history_replay (struct GNUNET_SOCIAL_Place *plc,
+                      uint64_t start_message_id,
+                      uint64_t end_message_id,
+                      uint64_t message_limit,
+                      const char *method_prefix,
+                      uint32_t flags,
+                      struct GNUNET_PSYC_Slicer *slicer,
+                      GNUNET_ResultCallback result_cb,
+                      void *cls)
+{
+  struct GNUNET_PSYC_HistoryRequestMessage *req;
+  struct GNUNET_SOCIAL_HistoryRequest *hist = GNUNET_malloc (sizeof (*hist));
+  hist->plc = plc;
+  hist->slicer = slicer;
+  hist->result_cb = result_cb;
+  hist->cls = cls;
+  hist->op_id = GNUNET_OP_add (plc->op, op_recv_history_result, hist, NULL);
+
+  GNUNET_assert (NULL != method_prefix);
+  uint16_t method_size = strnlen (method_prefix,
+                                  GNUNET_MAX_MESSAGE_SIZE
+                                  - sizeof (*req)) + 1;
+  GNUNET_assert ('\0' == method_prefix[method_size - 1]);
+
+  struct GNUNET_MQ_Envelope *
+    env = GNUNET_MQ_msg_extra (req, method_size,
+                               GNUNET_MESSAGE_TYPE_PSYC_HISTORY_REPLAY);
+  req->start_message_id = GNUNET_htonll (start_message_id);
+  req->end_message_id = GNUNET_htonll (end_message_id);
+  req->message_limit = GNUNET_htonll (message_limit);
+  req->flags = htonl (flags);
+  req->op_id = GNUNET_htonll (hist->op_id);
+  GNUNET_memcpy (&req[1], method_prefix, method_size);
+
+  GNUNET_MQ_send (plc->mq, env);
+  return hist;
+}
+
+
+/**
+ * Learn about the history of a place.
+ *
+ * Messages are returned through the @a slicer function
+ * and have the #GNUNET_PSYC_MESSAGE_HISTORIC flag set.
+ *
+ * @param place
+ *        Place we want to learn more about.
+ * @param start_message_id
+ *        First historic message we are interested in.
+ * @param end_message_id
+ *        Last historic message we are interested in (inclusive).
+ * @param method_prefix
+ *        Only retrieve messages with this method prefix.
+ * @param flags
+ *        OR'ed GNUNET_PSYC_HistoryReplayFlags
+ * @param slicer
+ *        Slicer to use for retrieved messages.
+ *        Can be the same as the slicer of the place.
+ * @param result_cb
+ *        Function called after all messages retrieved.
+ *        NULL if not needed.
+ * @param cls Closure for @a result_cb.
+ */
+struct GNUNET_SOCIAL_HistoryRequest *
+GNUNET_SOCIAL_place_history_replay (struct GNUNET_SOCIAL_Place *plc,
+                                    uint64_t start_message_id,
+                                    uint64_t end_message_id,
+                                    const char *method_prefix,
+                                    uint32_t flags,
+                                    struct GNUNET_PSYC_Slicer *slicer,
+                                    GNUNET_ResultCallback result_cb,
+                                    void *cls)
+{
+  return place_history_replay (plc, start_message_id, end_message_id, 0,
+                               method_prefix, flags, slicer, result_cb, cls);
+}
+
+
+/**
+ * Learn about the history of a place.
+ *
+ * Sends messages through the slicer function of the place where
+ * start_message_id <= message_id <= end_message_id.
+ * The messages will have the #GNUNET_PSYC_MESSAGE_HISTORIC flag set.
+ *
+ * To get the latest message, use 0 for both the start and end message ID.
+ *
+ * @param place
+ *        Place we want to learn more about.
+ * @param message_limit
+ *        Maximum number of historic messages we are interested in.
+ * @param method_prefix
+ *        Only retrieve messages with this method prefix.
+ * @param flags
+ *        OR'ed GNUNET_PSYC_HistoryReplayFlags
+ * @param result_cb
+ *        Function called after all messages retrieved.
+ *        NULL if not needed.
+ * @param cls Closure for @a result_cb.
+ */
+struct GNUNET_SOCIAL_HistoryRequest *
+GNUNET_SOCIAL_place_history_replay_latest (struct GNUNET_SOCIAL_Place *plc,
+                                           uint64_t message_limit,
+                                           const char *method_prefix,
+                                           uint32_t flags,
+                                           struct GNUNET_PSYC_Slicer *slicer,
+                                           GNUNET_ResultCallback result_cb,
+                                           void *cls)
+{
+  return place_history_replay (plc, 0, 0, message_limit, method_prefix, flags,
+                               slicer, result_cb, cls);
+}
+
+
+/**
+ * Cancel learning about the history of a place.
+ *
+ * @param hist
+ *        History lesson to cancel.
+ */
+void
+GNUNET_SOCIAL_place_history_replay_cancel (struct GNUNET_SOCIAL_HistoryRequest 
*hist)
+{
+  GNUNET_OP_remove (hist->plc->op, hist->op_id);
+  GNUNET_free (hist);
+}
+
+
+/**
+ * Request matching state variables.
+ */
+static struct GNUNET_SOCIAL_LookHandle *
+place_state_get (struct GNUNET_SOCIAL_Place *plc,
+                 uint16_t type, const char *name,
+                 GNUNET_PSYC_StateVarCallback var_cb,
+                 GNUNET_ResultCallback result_cb, void *cls)
+{
+  struct GNUNET_PSYC_StateRequestMessage *req;
+  struct GNUNET_SOCIAL_LookHandle *look = GNUNET_malloc (sizeof (*look));
+  look->plc = plc;
+  look->var_cb = var_cb;
+  look->result_cb = result_cb;
+  look->cls = cls;
+  look->op_id = GNUNET_OP_add (plc->op, &op_recv_state_result, look, NULL);
+
+  GNUNET_assert (NULL != name);
+  size_t name_size = strnlen (name, GNUNET_MAX_MESSAGE_SIZE
+                              - sizeof (*req)) + 1;
+  struct GNUNET_MQ_Envelope *
+    env = GNUNET_MQ_msg_extra (req, name_size, type);
+  req->op_id = GNUNET_htonll (look->op_id);
+  GNUNET_memcpy (&req[1], name, name_size);
+
+  GNUNET_MQ_send (plc->mq, env);
+  return look;
+}
+
+
+/**
+ * Look at a particular object in the place.
+ *
+ * The best matching object is returned (its name might be less specific than
+ * what was requested).
+ *
+ * @param place
+ *        The place where to look.
+ * @param full_name
+ *        Full name of the object.
+ * @param value_size
+ *        Set to the size of the returned value.
+ *
+ * @return NULL if there is no such object at this place.
+ */
+struct GNUNET_SOCIAL_LookHandle *
+GNUNET_SOCIAL_place_look_at (struct GNUNET_SOCIAL_Place *plc,
+                             const char *full_name,
+                             GNUNET_PSYC_StateVarCallback var_cb,
+                             GNUNET_ResultCallback result_cb,
+                             void *cls)
+{
+  return place_state_get (plc, GNUNET_MESSAGE_TYPE_PSYC_STATE_GET,
+                          full_name, var_cb, result_cb, cls);
+}
+
+
+/**
+ * Look for objects in the place with a matching name prefix.
+ *
+ * @param place
+ *        The place where to look.
+ * @param name_prefix
+ *        Look at objects with names beginning with this value.
+ * @param var_cb
+ *        Function to call for each object found.
+ * @param cls
+ *        Closure for callback function.
+ *
+ * @return Handle that can be used to stop looking at objects.
+ */
+struct GNUNET_SOCIAL_LookHandle *
+GNUNET_SOCIAL_place_look_for (struct GNUNET_SOCIAL_Place *plc,
+                              const char *name_prefix,
+                              GNUNET_PSYC_StateVarCallback var_cb,
+                              GNUNET_ResultCallback result_cb,
+                              void *cls)
+{
+  return place_state_get (plc, GNUNET_MESSAGE_TYPE_PSYC_STATE_GET_PREFIX,
+                          name_prefix, var_cb, result_cb, cls);
+}
+
+
+/**
+ * Cancel a state request operation.
+ *
+ * @param sr
+ *        Handle for the operation to cancel.
+ */
+void
+GNUNET_SOCIAL_place_look_cancel (struct GNUNET_SOCIAL_LookHandle *look)
+{
+  GNUNET_OP_remove (look->plc->op, look->op_id);
+  GNUNET_free (look);
+}
+
+
+static void
+op_recv_zone_add_place_result (void *cls, int64_t result,
+                               const void *err_msg, uint16_t err_msg_size)
+{
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Received zone add place result: %" PRId64 ".\n", result);
+
+  struct ZoneAddPlaceHandle *add_plc = cls;
+  if (NULL != add_plc->result_cb)
+    add_plc->result_cb (add_plc->result_cls, result, err_msg, err_msg_size);
+
+  GNUNET_free (add_plc);
+}
+
+
+/**
+ * Advertise @e place in the GNS zone of @e ego.
+ *
+ * @param app
+ *        Application handle.
+ * @param ego
+ *        Ego.
+ * @param place_pub_key
+ *        Public key of place to add.
+ * @param name
+ *        The name for the PLACE record to put in the zone.
+ * @param password
+ *        Password used to encrypt the record or NULL to keep it cleartext.
+ * @param relay_count
+ *        Number of elements in the @a relays array.
+ * @param relays
+ *        List of relays to put in the PLACE record to advertise
+ *        as entry points to the place in addition to the origin.
+ * @param expiration_time
+ *        Expiration time of the record, use 0 to remove the record.
+ * @param result_cb
+ *        Function called with the result of the operation.
+ * @param result_cls
+ *        Closure for @a result_cb
+ *
+ * @return #GNUNET_OK if the request was sent,
+ *         #GNUNET_SYSERR on error, e.g. the name/password is too long.
+ */
+int
+GNUNET_SOCIAL_zone_add_place (const struct GNUNET_SOCIAL_App *app,
+                              const struct GNUNET_SOCIAL_Ego *ego,
+                              const char *name,
+                              const char *password,
+                              const struct GNUNET_CRYPTO_EddsaPublicKey 
*place_pub_key,
+                              const struct GNUNET_PeerIdentity *origin,
+                              uint32_t relay_count,
+                              const struct GNUNET_PeerIdentity *relays,
+                              struct GNUNET_TIME_Absolute expiration_time,
+                              GNUNET_ResultCallback result_cb,
+                              void *result_cls)
+{
+  struct ZoneAddPlaceRequest *preq;
+  size_t name_size = strlen (name) + 1;
+  size_t password_size = strlen (password) + 1;
+  size_t relay_size = relay_count * sizeof (*relays);
+  size_t payload_size = name_size + password_size + relay_size;
+
+  if (GNUNET_MAX_MESSAGE_SIZE < sizeof (*preq) + payload_size)
+    return GNUNET_SYSERR;
+
+  struct GNUNET_MQ_Envelope *
+    env = GNUNET_MQ_msg_extra (preq, payload_size,
+                               GNUNET_MESSAGE_TYPE_SOCIAL_ZONE_ADD_PLACE);
+  preq->expiration_time = GNUNET_htonll (expiration_time.abs_value_us);
+  preq->ego_pub_key = ego->pub_key;
+  preq->place_pub_key = *place_pub_key;
+  preq->origin = *origin;
+  preq->relay_count = htonl (relay_count);
+
+  char *p = (char *) &preq[1];
+  GNUNET_memcpy (p, name, name_size);
+  p += name_size;
+  GNUNET_memcpy (p, password, password_size);
+  p += password_size;
+  GNUNET_memcpy (p, relays, relay_size);
+
+  struct ZoneAddPlaceHandle * add_plc = GNUNET_malloc (sizeof (*add_plc));
+  add_plc->result_cb = result_cb;
+  add_plc->result_cls = result_cls;
+
+  preq->op_id = GNUNET_htonll (GNUNET_OP_add (app->op,
+                                              op_recv_zone_add_place_result,
+                                              add_plc, NULL));
+
+  GNUNET_MQ_send (app->mq, env);
+  return GNUNET_OK;
+}
+
+
+static void
+op_recv_zone_add_nym_result (void *cls, int64_t result,
+                             const void *err_msg, uint16_t err_msg_size)
+{
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "Received zone add nym result: %" PRId64 ".\n", result);
+
+  struct ZoneAddNymHandle *add_nym = cls;
+  if (NULL != add_nym->result_cb)
+    add_nym->result_cb (add_nym->result_cls, result, err_msg, err_msg_size);
+
+  GNUNET_free (add_nym);
+}
+
+
+/**
+ * Add nym to the GNS zone of @e ego.
+ *
+ * @param cfg
+ *        Configuration.
+ * @param ego
+ *        Ego.
+ * @param name
+ *        The name for the PKEY record to put in the zone.
+ * @param nym_pub_key
+ *        Public key of nym to add.
+ * @param expiration_time
+ *        Expiration time of the record, use 0 to remove the record.
+ * @param result_cb
+ *        Function called with the result of the operation.
+ * @param result_cls
+ *        Closure for @a result_cb
+ *
+ * @return #GNUNET_OK if the request was sent,
+ *         #GNUNET_SYSERR on error, e.g. the name is too long.
+ */
+int
+GNUNET_SOCIAL_zone_add_nym (const struct GNUNET_SOCIAL_App *app,
+                            const struct GNUNET_SOCIAL_Ego *ego,
+                            const char *name,
+                            const struct GNUNET_CRYPTO_EcdsaPublicKey 
*nym_pub_key,
+                            struct GNUNET_TIME_Absolute expiration_time,
+                            GNUNET_ResultCallback result_cb,
+                            void *result_cls)
+{
+  struct ZoneAddNymRequest *nreq;
+
+  size_t name_size = strlen (name) + 1;
+  if (GNUNET_MAX_MESSAGE_SIZE < sizeof (*nreq) + name_size)
+    return GNUNET_SYSERR;
+
+  struct GNUNET_MQ_Envelope *
+    env = GNUNET_MQ_msg_extra (nreq, name_size,
+                               GNUNET_MESSAGE_TYPE_SOCIAL_ZONE_ADD_NYM);
+  nreq->expiration_time = GNUNET_htonll (expiration_time.abs_value_us);
+  nreq->ego_pub_key = ego->pub_key;
+  nreq->nym_pub_key = *nym_pub_key;
+  GNUNET_memcpy (&nreq[1], name, name_size);
+
+  struct ZoneAddNymHandle *add_nym = GNUNET_malloc (sizeof (*add_nym));
+  add_nym->result_cb = result_cb;
+  add_nym->result_cls = result_cls;
+
+  nreq->op_id = GNUNET_htonll (GNUNET_OP_add (app->op,
+                                              op_recv_zone_add_nym_result,
+                                              add_nym, NULL));
+
+  GNUNET_MQ_send (app->mq, env);
+  return GNUNET_OK;
+}
+
+
+/*** APP ***/
+
+
+static void
+app_connect (struct GNUNET_SOCIAL_App *app);
+
+
+static void
+app_reconnect (void *cls)
+{
+  app_connect (cls);
+}
+
+
+/**
+ * App client disconnected from service.
+ *
+ * Reconnect after backoff period.
+ */
+static void
+app_disconnected (void *cls, enum GNUNET_MQ_Error error)
+{
+  struct GNUNET_SOCIAL_App *app = cls;
+
+  LOG (GNUNET_ERROR_TYPE_DEBUG,
+       "App client disconnected (%d), re-connecting\n",
+       (int) error);
+  if (NULL != app->mq)
+  {
+    GNUNET_MQ_destroy (app->mq);
+    app->mq = NULL;
+  }
+
+  app->reconnect_task = GNUNET_SCHEDULER_add_delayed (app->reconnect_delay,
+                                                      app_reconnect,
+                                                      app);
+  app->reconnect_delay = GNUNET_TIME_STD_BACKOFF (app->reconnect_delay);
+}
+
+
+static void
+app_connect (struct GNUNET_SOCIAL_App *app)
+{
+  struct GNUNET_MQ_MessageHandler handlers[] = {
+    GNUNET_MQ_hd_var_size (app_ego,
+                           GNUNET_MESSAGE_TYPE_SOCIAL_APP_EGO,
+                           struct AppEgoMessage,
+                           app),
+    GNUNET_MQ_hd_fixed_size (app_ego_end,
+                             GNUNET_MESSAGE_TYPE_SOCIAL_APP_EGO_END,
+                             struct GNUNET_MessageHeader,
+                             app),
+    GNUNET_MQ_hd_var_size (app_place,
+                           GNUNET_MESSAGE_TYPE_SOCIAL_APP_PLACE,
+                           struct AppPlaceMessage,
+                           app),
+    GNUNET_MQ_hd_fixed_size (app_place_end,
+                             GNUNET_MESSAGE_TYPE_SOCIAL_APP_PLACE_END,
+                             struct GNUNET_MessageHeader,
+                             app),
+    GNUNET_MQ_hd_var_size (app_result,
+                           GNUNET_MESSAGE_TYPE_PSYC_RESULT_CODE,
+                           struct GNUNET_OperationResultMessage,
+                           app),
+    GNUNET_MQ_handler_end ()
+  };
+
+  app->mq = GNUNET_CLIENT_connect (app->cfg, "social",
+                                   handlers, app_disconnected, app);
+  GNUNET_assert (NULL != app->mq);
+  GNUNET_MQ_send_copy (app->mq, app->connect_env);
+}
+
+
+/**
+ * Connect application to the social service.
+ *
+ * The @host_place_cb and @guest_place_cb functions are
+ * initially called for each entered places,
+ * then later each time a new place is entered with the current application ID.
+ *
+ * @param cfg
+ *        Configuration.
+ * @param id
+ *        Application ID.
+ * @param ego_cb
+ *        Function to notify about an available ego.
+ * @param host_cb
+ *        Function to notify about a place entered as host.
+ * @param guest_cb
+ *        Function to notify about a place entered as guest.
+ * @param cls
+ *        Closure for the callbacks.
+ *
+ * @return Handle that can be used to stop listening.
+ */
+struct GNUNET_SOCIAL_App *
+GNUNET_SOCIAL_app_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
+                           const char *id,
+                           GNUNET_SOCIAL_AppEgoCallback ego_cb,
+                           GNUNET_SOCIAL_AppHostPlaceCallback host_cb,
+                           GNUNET_SOCIAL_AppGuestPlaceCallback guest_cb,
+                           GNUNET_SOCIAL_AppConnectedCallback connected_cb,
+                           void *cls)
+{
+  uint16_t app_id_size = strnlen (id, GNUNET_SOCIAL_APP_MAX_ID_SIZE);
+  if (GNUNET_SOCIAL_APP_MAX_ID_SIZE == app_id_size)
+    return NULL;
+  app_id_size++;
+
+  struct GNUNET_SOCIAL_App *app = GNUNET_malloc (sizeof *app);
+  app->cfg = cfg;
+  app->ego_cb = ego_cb;
+  app->host_cb = host_cb;
+  app->guest_cb = guest_cb;
+  app->connected_cb = connected_cb;
+  app->cb_cls = cls;
+  app->egos = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO);
+  app->op = GNUNET_OP_create ();
+  app->id = GNUNET_malloc (app_id_size);
+  GNUNET_memcpy (app->id, id, app_id_size);
+
+  struct AppConnectRequest *creq;
+  app->connect_env = GNUNET_MQ_msg_extra (creq, app_id_size,
+                                          
GNUNET_MESSAGE_TYPE_SOCIAL_APP_CONNECT);
+  GNUNET_memcpy (&creq[1], app->id, app_id_size);
+
+  app_connect (app);
+  return app;
+}
+
+
+static void
+app_cleanup (struct GNUNET_SOCIAL_App *app)
+{
+  if (NULL != app->mq)
+  {
+    GNUNET_MQ_destroy (app->mq);
+    app->mq = NULL;
+  }
+  if (NULL != app->disconnect_cb)
+  {
+    app->disconnect_cb (app->disconnect_cls);
+    app->disconnect_cb = NULL;
+  }
+  GNUNET_free (app);
+}
+
+/**
+ * Disconnect application.
+ *
+ * @param app
+ *        Application handle.
+ * @param disconnect_cb
+ *        Disconnect callback.
+ * @param disconnect_cls
+ *        Disconnect closure.
+ */
+void
+GNUNET_SOCIAL_app_disconnect (struct GNUNET_SOCIAL_App *app,
+                              GNUNET_ContinuationCallback disconnect_cb,
+                              void *disconnect_cls)
+{
+  if (NULL == app) return;
+
+  app->disconnect_cb = disconnect_cb;
+  app->disconnect_cls = disconnect_cls;
+
+  if (NULL != app->mq)
+  {
+    struct GNUNET_MQ_Envelope *env = GNUNET_MQ_get_last_envelope (app->mq);
+    if (NULL != env)
+    {
+      GNUNET_MQ_notify_sent (env, (GNUNET_SCHEDULER_TaskCallback) app_cleanup, 
app);
+    }
+    else
+    {
+      app_cleanup (app);
+    }
+  }
+  else
+  {
+    app_cleanup (app);
+  }
+}
+
+
+/**
+ * Detach application from a place.
+ *
+ * Removes the place from the entered places list for this application.
+ * Note: this does not disconnect from the place.
+ *
+ * @see GNUNET_SOCIAL_host_disconnect() and GNUNET_SOCIAL_guest_disconnect()
+ *
+ * @param app
+ *        Application.
+ * @param plc
+ *        Place.
+ */
+void
+GNUNET_SOCIAL_app_detach (struct GNUNET_SOCIAL_App *app,
+                          struct GNUNET_SOCIAL_Place *plc)
+{
+  struct AppDetachRequest *dreq;
+  struct GNUNET_MQ_Envelope *
+    env = GNUNET_MQ_msg (dreq, GNUNET_MESSAGE_TYPE_SOCIAL_APP_DETACH);
+  dreq->place_pub_key = plc->pub_key;
+  dreq->ego_pub_key = plc->ego_pub_key;
+
+  GNUNET_MQ_send (app->mq, env);
+}
+
+
+/* end of social_api.c */
diff --git a/src/social/test_social.c b/src/social/test_social.c
new file mode 100644
index 0000000..feac3c5
--- /dev/null
+++ b/src/social/test_social.c
@@ -0,0 +1,1449 @@
+/*
+ * This file is part of GNUnet
+ * Copyright (C) 2013 GNUnet e.V.
+ *
+ * GNUnet is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * GNUnet 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
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+     SPDX-License-Identifier: AGPL3.0-or-later
+ */
+/**
+ * @file social/test_social.c
+ * @brief Tests for the Social API.
+ * @author Gabor X Toth
+ */
+
+#include <inttypes.h>
+
+#include "platform.h"
+#include "gnunet_crypto_lib.h"
+#include "gnunet_common.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_testing_lib.h"
+#include "gnunet_psyc_util_lib.h"
+#include "gnunet_social_service.h"
+#include "gnunet_identity_service.h"
+
+#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
+
+#define DATA2ARG(data) data, sizeof (data)
+
+/**
+ * Return value from 'main'.
+ */
+int res;
+
+struct GNUNET_SOCIAL_App *app;
+const char *app_id = "test";
+
+/**
+ * Handle for task for timeout termination.
+ */
+struct GNUNET_SCHEDULER_Task *end_badly_task;
+
+const struct GNUNET_CONFIGURATION_Handle *cfg;
+
+struct GNUNET_PeerIdentity this_peer;
+
+struct GNUNET_IDENTITY_Handle *id;
+
+const struct GNUNET_IDENTITY_Ego *identity_host_ego;
+const struct GNUNET_IDENTITY_Ego *identity_guest_ego;
+
+const struct GNUNET_SOCIAL_Ego *host_ego;
+const struct GNUNET_SOCIAL_Ego *guest_ego;
+
+const char *host_name = "Host One";
+const char *guest_name = "Guest One";
+
+struct GNUNET_CRYPTO_EddsaPrivateKey *place_key;
+struct GNUNET_CRYPTO_EcdsaPrivateKey *guest_key;
+
+struct GNUNET_CRYPTO_EddsaPublicKey place_pub_key;
+struct GNUNET_HashCode place_pub_hash;
+
+const struct GNUNET_CRYPTO_EcdsaPublicKey *guest_pub_key;
+const struct GNUNET_CRYPTO_EcdsaPublicKey *host_pub_key;
+
+struct GNUNET_PSYC_Slicer *host_slicer;
+struct GNUNET_PSYC_Slicer *guest_slicer;
+
+struct GNUNET_SOCIAL_Host *hst;
+struct GNUNET_SOCIAL_Guest *gst;
+
+struct GNUNET_SOCIAL_Place *hst_plc;
+struct GNUNET_SOCIAL_Place *gst_plc;
+
+struct GNUNET_SOCIAL_Nym *nym_eject;
+
+struct GuestEnterMessage
+{
+  struct GNUNET_PSYC_Message *msg;
+  const char *method_name;
+  struct GNUNET_PSYC_Environment *env;
+  void *data;
+  uint16_t data_size;
+} guest_enter_msg;
+
+struct TransmitClosure
+{
+  struct GNUNET_SOCIAL_Announcement *host_ann;
+  struct GNUNET_SOCIAL_TalkRequest *guest_talk;
+  struct GNUNET_PSYC_Environment *env;
+  char *data[16];
+  uint8_t data_delay[16];
+  uint8_t data_count;
+  uint8_t paused;
+  uint8_t n;
+} tmit;
+
+struct ResultClosure {
+  uint32_t n;
+} mod_foo_bar_rcls;
+
+uint8_t join_req_count;
+struct GNUNET_PSYC_Message *join_resp;
+
+uint32_t counter;
+
+uint8_t is_guest_nym_added = GNUNET_NO;
+uint8_t is_host_reconnected = GNUNET_NO;
+uint8_t is_guest_reconnected = GNUNET_NO;
+
+enum
+{
+  TEST_NONE                         =  0,
+  TEST_IDENTITIES_CREATE            =  1,
+  TEST_HOST_ENTER                   =  2,
+  TEST_GUEST_ENTER                  =  3,
+  TEST_HOST_ANSWER_DOOR_REFUSE      =  4,
+  TEST_GUEST_RECV_ENTRY_DCSN_REFUSE =  5,
+  TEST_HOST_ANSWER_DOOR_ADMIT       =  6,
+  TEST_GUEST_RECV_ENTRY_DCSN_ADMIT  =  7,
+  TEST_HOST_ANNOUNCE                       =  8,
+  TEST_HOST_ANNOUNCE_END            =  9,
+  TEST_GUEST_TALK                   = 10,
+  TEST_HOST_ANNOUNCE2                      = 11,
+  TEST_HOST_ANNOUNCE2_END           = 12,
+  TEST_GUEST_HISTORY_REPLAY         = 13,
+  TEST_GUEST_HISTORY_REPLAY_LATEST  = 14,
+  TEST_GUEST_LOOK_AT                = 15,
+  TEST_GUEST_LOOK_FOR               = 16,
+  TEST_GUEST_LEAVE                  = 17,
+  TEST_ZONE_ADD_PLACE               = 18,
+  TEST_GUEST_ENTER_BY_NAME          = 19,
+  TEST_RECONNECT                    = 20,
+  TEST_GUEST_LEAVE2                 = 21,
+  TEST_HOST_LEAVE                   = 22,
+} test;
+
+
+static void
+schedule_guest_leave (void *cls);
+
+
+static void
+host_answer_door (void *cls,
+                  struct GNUNET_SOCIAL_Nym *nym,
+                  const char *method_name,
+                  struct GNUNET_PSYC_Environment *env,
+                  const void *data,
+                  size_t data_size);
+
+static void
+host_enter ();
+
+static void
+guest_init ();
+
+static void
+guest_enter ();
+
+static void
+guest_enter_by_name ();
+
+static void
+guest_talk ();
+
+static void
+host_announce2 ();
+
+
+/**
+ * Terminate the test case (failure).
+ *
+ * @param cls NULL
+ */
+static void
+end_badly (void *cls)
+{
+  end_badly_task = NULL;
+  GNUNET_SCHEDULER_shutdown ();
+  res = 2;
+  GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+              "Test FAILED.\n");
+}
+
+
+/**
+ * Terminate the test case (failure).
+ *
+ * @param cls NULL
+ */
+static void
+end_shutdown (void *cls)
+{
+  if (NULL != id)
+  {
+    GNUNET_IDENTITY_disconnect (id);
+    id = NULL;
+  }
+
+  if (NULL != guest_slicer)
+  {
+    GNUNET_PSYC_slicer_destroy (guest_slicer);
+    guest_slicer = NULL;
+  }
+
+  if (NULL != host_slicer)
+  {
+    GNUNET_PSYC_slicer_destroy (host_slicer);
+    host_slicer = NULL;
+  }
+  if (NULL != end_badly_task)
+  {
+    GNUNET_SCHEDULER_cancel (end_badly_task);
+    end_badly_task = NULL;
+  }
+  if (NULL != gst)
+  {
+    GNUNET_SOCIAL_guest_leave (gst, NULL, NULL, NULL);
+    gst = NULL;
+    gst_plc = NULL;
+  }
+  if (NULL != hst)
+  {
+    GNUNET_SOCIAL_host_leave (hst, NULL, NULL, NULL);
+    hst = NULL;
+    hst_plc = NULL;
+  }
+  GNUNET_SOCIAL_app_disconnect (app, NULL, NULL);
+}
+
+
+/**
+ * Terminate the test case (success).
+ *
+ * @param cls NULL
+ */
+static void
+end_normally (void *cls)
+{
+  GNUNET_SCHEDULER_shutdown ();
+  res = 0;
+  GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, "Test PASSED.\n");
+}
+
+
+/**
+ * Finish the test case (successfully).
+ */
+static void
+end ()
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+              "Test #%u: Ending tests.\n", test);
+
+  if (end_badly_task != NULL)
+  {
+    GNUNET_SCHEDULER_cancel (end_badly_task);
+    end_badly_task = NULL;
+  }
+  GNUNET_SCHEDULER_add_now (&end_normally, NULL);
+}
+
+
+static void
+transmit_resume (void *cls)
+{
+  struct TransmitClosure *tmit = cls;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+             "Test #%u: Transmission resumed.\n", test);
+  if (NULL != tmit->host_ann)
+    GNUNET_SOCIAL_host_announce_resume (tmit->host_ann);
+  else
+    GNUNET_SOCIAL_guest_talk_resume (tmit->guest_talk);
+}
+
+
+static int
+notify_data (void *cls, uint16_t *data_size, void *data)
+{
+  struct TransmitClosure *tmit = cls;
+  if (NULL != tmit->env)
+  {
+    GNUNET_PSYC_env_destroy (tmit->env);
+    tmit->env = NULL;
+  }
+  if (0 == tmit->data_count)
+  {
+    *data_size = 0;
+    return GNUNET_YES;
+  }
+
+  uint16_t size = strlen (tmit->data[tmit->n]);
+  GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+              "Test #%u: Transmit notify data: %u bytes available, "
+              "processing fragment %u/%u (size %u).\n",
+              test, *data_size, tmit->n + 1, tmit->data_count, size);
+  if (*data_size < size)
+  {
+    *data_size = 0;
+    GNUNET_assert (0);
+    return GNUNET_SYSERR;
+  }
+
+  if (GNUNET_YES != tmit->paused && 0 < tmit->data_delay[tmit->n])
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+                "Test #%u: Transmission paused.\n", test);
+    tmit->paused = GNUNET_YES;
+    GNUNET_SCHEDULER_add_delayed (
+      GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
+                                     tmit->data_delay[tmit->n]),
+      &transmit_resume, tmit);
+    *data_size = 0;
+    return GNUNET_NO;
+  }
+  tmit->paused = GNUNET_NO;
+
+  *data_size = size;
+  GNUNET_memcpy (data, tmit->data[tmit->n], size);
+
+  return ++tmit->n < tmit->data_count ? GNUNET_NO : GNUNET_YES;
+}
+
+
+static void
+host_left ()
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+              "Test #%u: The host has left the place.\n", test);
+  end ();
+}
+
+
+static void
+schedule_host_leave (void *cls)
+{
+  test = TEST_HOST_LEAVE;
+  GNUNET_SOCIAL_host_leave (hst, NULL, &host_left, NULL);
+  hst = NULL;
+  hst_plc = NULL;
+}
+
+
+static void
+host_farewell2 (void *cls,
+               const struct GNUNET_SOCIAL_Nym *nym,
+               struct GNUNET_PSYC_Environment *env)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+              "Nym left the place again.\n");
+  GNUNET_SCHEDULER_add_now (&schedule_host_leave, NULL);
+}
+
+
+static void
+host_reconnected (void *cls, int result,
+                 const struct GNUNET_CRYPTO_EddsaPublicKey *home_pub_key,
+                 uint64_t max_message_id)
+{
+  place_pub_key = *home_pub_key;
+  GNUNET_CRYPTO_hash (&place_pub_key, sizeof (place_pub_key), &place_pub_hash);
+  GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+              "Test #%u: Host reconnected to place %s\n",
+              test, GNUNET_h2s (&place_pub_hash));
+
+  is_host_reconnected = GNUNET_YES;
+  if (GNUNET_YES == is_guest_reconnected)
+  {
+    GNUNET_assert (NULL != gst);
+    GNUNET_SCHEDULER_add_now (&schedule_guest_leave, NULL);
+  }
+}
+
+
+static void
+guest_reconnected (void *cls, int result,
+                   const struct GNUNET_CRYPTO_EddsaPublicKey *place_pub_key,
+                   uint64_t max_message_id)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+              "Test #%u: Guest reconnected to place: %d\n",
+              test, result);
+  GNUNET_assert (0 <= result);
+
+  is_guest_reconnected = GNUNET_YES;
+  if (GNUNET_YES == is_host_reconnected)
+  {
+    GNUNET_assert (NULL != gst);
+    GNUNET_SCHEDULER_add_now (&schedule_guest_leave, NULL);
+  }
+}
+
+
+static void
+app_connected (void *cls)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+              "Test #%u: App connected: %p\n", test, cls);
+}
+
+
+static void
+app_recv_host (void *cls,
+               struct GNUNET_SOCIAL_HostConnection *hconn,
+               struct GNUNET_SOCIAL_Ego *ego,
+               const struct GNUNET_CRYPTO_EddsaPublicKey *host_pub_key,
+               enum GNUNET_SOCIAL_AppPlaceState place_state)
+{
+  struct GNUNET_HashCode host_pub_hash;
+
+  GNUNET_CRYPTO_hash (host_pub_key,
+                      sizeof (*host_pub_key),
+                      &host_pub_hash);
+
+  GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+              "Test #%u: Got app host place notification: %s\n",
+              test,
+              GNUNET_h2s (&host_pub_hash));
+
+  if (test == TEST_RECONNECT)
+  {
+    if (0 == memcmp (&place_pub_key, host_pub_key, sizeof (*host_pub_key)))
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+                  "Test #%u: Reconnecting to host place: %s\n",
+                  test, GNUNET_h2s (&host_pub_hash));
+      hst = GNUNET_SOCIAL_host_enter_reconnect (hconn, host_slicer,
+                                                &host_reconnected,
+                                                &host_answer_door,
+                                                &host_farewell2,
+                                                NULL);
+    }
+  }
+}
+
+
+static void
+app_recv_guest (void *cls,
+                struct GNUNET_SOCIAL_GuestConnection *gconn,
+                struct GNUNET_SOCIAL_Ego *ego,
+                const struct GNUNET_CRYPTO_EddsaPublicKey *guest_pub_key,
+                enum GNUNET_SOCIAL_AppPlaceState place_state)
+{
+  struct GNUNET_HashCode guest_pub_hash;
+
+  GNUNET_CRYPTO_hash (guest_pub_key,
+                      sizeof (*guest_pub_key),
+                      &guest_pub_hash);
+
+  GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+              "Test #%u: Got app guest place notification: %s\n",
+              test, GNUNET_h2s (&guest_pub_hash));
+
+  if (test == TEST_RECONNECT)
+  {
+    if (0 == memcmp (&place_pub_key,
+                     guest_pub_key,
+                     sizeof (*guest_pub_key)))
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+                  "Test #%u: Reconnecting to guest place: %s\n",
+                  test, GNUNET_h2s (&guest_pub_hash));
+      gst = GNUNET_SOCIAL_guest_enter_reconnect (gconn,
+                                                 GNUNET_PSYC_SLAVE_JOIN_NONE,
+                                                 guest_slicer,
+                                                 &guest_reconnected,
+                                                 NULL);
+      GNUNET_assert (NULL != gst);
+    }
+  }
+}
+
+
+static void
+enter_if_ready ()
+{
+  if (NULL == host_ego || NULL == guest_ego)
+  {
+    return;
+  }
+  host_enter ();
+  guest_init ();
+}
+
+
+static void
+app_recv_ego (void *cls,
+              struct GNUNET_SOCIAL_Ego *ego,
+              const struct GNUNET_CRYPTO_EcdsaPublicKey *ego_pub_key,
+              const char *name)
+{
+  char *ego_pub_str = GNUNET_CRYPTO_ecdsa_public_key_to_string (ego_pub_key);
+  GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+              "Test #%u: Got app ego notification: %p %s %s\n",
+              test, ego, name, ego_pub_str);
+  GNUNET_free (ego_pub_str);
+
+  if (NULL != strstr (name, host_name))
+  {
+    host_ego = ego;
+    host_pub_key = ego_pub_key;
+    if (TEST_IDENTITIES_CREATE == test)
+    {
+      enter_if_ready ();
+    }
+    else
+    {
+      GNUNET_assert (TEST_RECONNECT == test);
+    }
+  }
+  else if (NULL != strstr (name, guest_name))
+  {
+    guest_ego = ego;
+    guest_pub_key = ego_pub_key;
+    if (TEST_IDENTITIES_CREATE == test)
+    {
+      enter_if_ready ();
+    }
+    else
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+                  "test = %d\n",
+                  test);
+      GNUNET_assert (TEST_RECONNECT == test);
+    }
+  }
+}
+
+
+static void
+schedule_reconnect (void *cls)
+{
+  test = TEST_RECONNECT;
+  GNUNET_SOCIAL_host_disconnect (hst, NULL, NULL);
+  GNUNET_SOCIAL_guest_disconnect (gst, NULL, NULL);
+  hst = NULL;
+  gst = NULL;
+
+  GNUNET_SOCIAL_app_disconnect (app, NULL, NULL);
+  app = GNUNET_SOCIAL_app_connect (cfg, app_id,
+                                   &app_recv_ego,
+                                   &app_recv_host,
+                                   &app_recv_guest,
+                                   &app_connected,
+                                   NULL);
+}
+
+
+static void
+host_recv_zone_add_place_result (void *cls, int64_t result,
+                                 const void *data, uint16_t data_size)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+              "Test #%u: Zone add place result: %" PRId64 " (%.*s).\n",
+              test, result, data_size, (const char *) data);
+  GNUNET_assert (GNUNET_YES == result);
+
+  GNUNET_assert (GNUNET_YES == is_guest_nym_added);
+  guest_enter_by_name ();
+}
+
+
+static void
+zone_add_place ()
+{
+  test = TEST_ZONE_ADD_PLACE;
+  GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+              "Test #%u: Adding place to zone.\n", test);
+
+  GNUNET_SOCIAL_zone_add_place (app, host_ego, "home", "let.me*in!",
+                                &place_pub_key, &this_peer, 1, &this_peer,
+                                GNUNET_TIME_relative_to_absolute 
(GNUNET_TIME_UNIT_MINUTES),
+                                host_recv_zone_add_place_result, app);
+}
+
+
+static void
+host_farewell (void *cls,
+               const struct GNUNET_SOCIAL_Nym *nym,
+               struct GNUNET_PSYC_Environment *env)
+{
+  const struct GNUNET_CRYPTO_EcdsaPublicKey *
+    nym_key = GNUNET_SOCIAL_nym_get_pub_key (nym);
+
+  char *str = GNUNET_CRYPTO_ecdsa_public_key_to_string (nym_key);
+  GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+              "Test #%u: Farewell: nym %s (%s) has left the place.\n",
+              test, GNUNET_h2s (GNUNET_SOCIAL_nym_get_pub_key_hash (nym)), 
str);
+  GNUNET_free (str);
+  GNUNET_assert (1 == GNUNET_PSYC_env_get_count (env));
+  if (0 != memcmp (guest_pub_key, nym_key, sizeof (*nym_key)))
+  {
+    str = GNUNET_CRYPTO_ecdsa_public_key_to_string (guest_pub_key);
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Test #%u: Farewell: nym does not match guest: %s\n",
+                test, str);
+    GNUNET_free (str);
+    GNUNET_assert (0);
+  }
+  zone_add_place ();
+}
+
+
+static void
+guest_left (void *cls)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+              "Test #%u: The guest has left the place.\n", test);
+}
+
+
+static void
+guest_leave ()
+{
+  if (test < TEST_RECONNECT)
+    test = TEST_GUEST_LEAVE;
+  else
+    test = TEST_GUEST_LEAVE2;
+
+  struct GNUNET_PSYC_Environment *env = GNUNET_PSYC_env_create ();
+  GNUNET_PSYC_env_add (env, GNUNET_PSYC_OP_SET,
+                       "_notice_place_leave", DATA2ARG ("Leaving."));
+  GNUNET_SOCIAL_guest_leave (gst, env, &guest_left, NULL);
+  GNUNET_PSYC_env_destroy (env);
+  gst = NULL;
+  gst_plc = NULL;
+}
+
+
+static void
+schedule_guest_leave (void *cls)
+{
+  guest_leave ();
+}
+
+
+static void
+guest_look_for_result (void *cls,
+                      int64_t result_code,
+                      const void *data,
+                      uint16_t data_size)
+{
+  struct ResultClosure *rcls = cls;
+  GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+              "Test #%u: guest_look_for_result: %" PRId64 "\n",
+              test, result_code);
+  GNUNET_assert (GNUNET_OK == result_code);
+  GNUNET_assert (6 == rcls->n);
+  GNUNET_free (rcls);
+  GNUNET_SCHEDULER_add_now (&schedule_guest_leave, NULL);
+}
+
+
+static void
+guest_look_for_var (void *cls,
+                   const struct GNUNET_MessageHeader *mod,
+                   const char *name,
+                   const void *value,
+                   uint32_t value_size,
+                   uint32_t full_value_size)
+{
+  struct ResultClosure *rcls = cls;
+  rcls->n++;
+  GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+              "Test #%u: guest_look_for_var: %s\n%.*s\n",
+              test, name, value_size, (const char *) value);
+}
+
+
+static void
+guest_look_for ()
+{
+  test = TEST_GUEST_LOOK_FOR;
+  struct ResultClosure *rcls = GNUNET_malloc (sizeof (*rcls));
+  GNUNET_SOCIAL_place_look_for (gst_plc, "_foo", guest_look_for_var, 
guest_look_for_result, rcls);
+}
+
+
+static void
+guest_look_at_result (void *cls, int64_t result_code,
+                      const void *data, uint16_t data_size)
+{
+  struct ResultClosure *rcls = cls;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+              "Test #%u: guest_look_at_result: %" PRId64 "\n",
+              test, result_code);
+  GNUNET_assert (GNUNET_OK == result_code);
+  GNUNET_assert (1 == rcls->n);
+  GNUNET_free (rcls);
+  guest_look_for ();
+}
+
+
+static void
+guest_look_at_var (void *cls,
+                   const struct GNUNET_MessageHeader *mod,
+                   const char *name,
+                   const void *value,
+                   uint32_t value_size,
+                   uint32_t full_value_size)
+{
+  struct ResultClosure *rcls = cls;
+  rcls->n++;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+              "Test #%u: guest_look_at_var: %s\n%.*s\n",
+              test ,name, value_size, (const char *) value);
+}
+
+
+static void
+guest_look_at ()
+{
+  test = TEST_GUEST_LOOK_AT;
+  struct ResultClosure *rcls = GNUNET_malloc (sizeof (*rcls));
+  GNUNET_SOCIAL_place_look_at (gst_plc, "_foo_bar", guest_look_at_var, 
guest_look_at_result, rcls);
+}
+
+
+static void
+guest_recv_history_replay_latest_result (void *cls, int64_t result,
+                                         const void *data, uint16_t data_size)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+              "Test #%u: Guest received latest history replay result "
+              "(%" PRIu32 " messages, %" PRId64 " fragments):\n"
+              "%.*s\n",
+              test, counter, result, data_size, (const char *) data);
+  //GNUNET_assert (2 == counter); /* message count */
+  //GNUNET_assert (7 == result); /* fragment count */
+
+  guest_look_at ();
+}
+
+
+static void
+guest_history_replay_latest ()
+{
+  test = TEST_GUEST_HISTORY_REPLAY_LATEST;
+  counter = 0;
+  GNUNET_SOCIAL_place_history_replay_latest (gst_plc, 3, "",
+                                             GNUNET_PSYC_HISTORY_REPLAY_LOCAL,
+                                             guest_slicer,
+                                             
&guest_recv_history_replay_latest_result,
+                                             NULL);
+}
+
+
+static void
+guest_recv_history_replay_result (void *cls, int64_t result,
+                                  const void *data, uint16_t data_size)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+              "Test #%u: Guest received history replay result: %" PRId64 "\n"
+              "%.*s\n",
+              test, result, data_size, (const char *) data);
+//  GNUNET_assert (2 == counter); /* message count */
+//  GNUNET_assert (7 == result); /* fragment count */
+
+  guest_history_replay_latest ();
+}
+
+
+static void
+guest_history_replay ()
+{
+  test = TEST_GUEST_HISTORY_REPLAY;
+  counter = 0;
+  GNUNET_SOCIAL_place_history_replay (gst_plc, 1, 3, "",
+                                      GNUNET_PSYC_HISTORY_REPLAY_LOCAL,
+                                      guest_slicer,
+                                      &guest_recv_history_replay_result,
+                                      NULL);
+}
+
+
+static void
+guest_recv_method (void *cls,
+                   const struct GNUNET_PSYC_MessageHeader *msg,
+                   const struct GNUNET_PSYC_MessageMethod *meth,
+                   uint64_t message_id,
+                   const char *method_name)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+              "Test #%u: Guest received method for message ID %" PRIu64 ":\n"
+              "%s (flags: %x)\n",
+              test, message_id, method_name, ntohl (meth->flags));
+  /** @todo FIXME: check message */
+}
+
+
+static void
+guest_recv_modifier (void *cls,
+                     const struct GNUNET_PSYC_MessageHeader *msg,
+                     const struct GNUNET_MessageHeader *pmsg,
+                     uint64_t message_id,
+                     enum GNUNET_PSYC_Operator oper,
+                     const char *name,
+                     const void *value,
+                     uint16_t value_size,
+                     uint16_t full_value_size)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+              "Test #%u: Guest received modifier for message ID %" PRIu64 ":\n"
+              "%c%s: %.*s (size: %u)\n",
+              test, message_id, oper, name, value_size, (const char *) value, 
value_size);
+  /** @todo FIXME: check modifier */
+}
+
+static void
+guest_recv_mod_foo_bar (void *cls,
+                        const struct GNUNET_PSYC_MessageHeader *msg,
+                        const struct GNUNET_MessageHeader *pmsg,
+                        uint64_t message_id,
+                        enum GNUNET_PSYC_Operator oper,
+                        const char *name,
+                        const void *value,
+                        uint16_t value_size,
+                        uint16_t full_value_size)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+              "Test #%u: Guest received modifier matching _foo_bar for message 
ID %" PRIu64 ":\n"
+              "%c%s: %.*s (size: %u)\n",
+              test, message_id, oper, name, value_size, (const char *) value, 
value_size);
+  struct ResultClosure *rc = cls;
+  rc->n++;
+  /** @todo FIXME: check modifier */
+}
+
+
+static void
+guest_recv_data (void *cls,
+                 const struct GNUNET_PSYC_MessageHeader *msg,
+                 const struct GNUNET_MessageHeader *pmsg,
+                 uint64_t message_id,
+                 const void *data,
+                 uint16_t data_size)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+              "Test #%u: Guest received data for message ID %" PRIu64 ":\n"
+              "%.*s\n",
+              test, message_id, data_size, (const char *) data);
+  /** @todo FIXME: check data */
+}
+
+
+static void
+guest_recv_eom (void *cls,
+                const struct GNUNET_PSYC_MessageHeader *msg,
+                const struct GNUNET_MessageHeader *pmsg,
+                uint64_t message_id,
+                uint8_t is_cancelled)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+              "Test #%u: Guest received end of message ID %" PRIu64
+              ", cancelled: %u\n",
+              test, message_id, is_cancelled);
+
+  switch (test)
+  {
+  case TEST_HOST_ANNOUNCE:
+    test = TEST_HOST_ANNOUNCE_END;
+    break;
+
+  case TEST_HOST_ANNOUNCE_END:
+    guest_talk ();
+    break;
+
+  case TEST_HOST_ANNOUNCE2:
+    test = TEST_HOST_ANNOUNCE2_END;
+    break;
+
+  case TEST_HOST_ANNOUNCE2_END:
+    guest_history_replay ();
+    break;
+
+  case TEST_GUEST_HISTORY_REPLAY:
+  case TEST_GUEST_HISTORY_REPLAY_LATEST:
+    counter++;
+    break;
+
+  default:
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "invalid test: %d\n", test);
+    GNUNET_assert (0);
+  }
+}
+
+
+static void
+host_recv_method (void *cls,
+                  const struct GNUNET_PSYC_MessageHeader *msg,
+                  const struct GNUNET_PSYC_MessageMethod *meth,
+                  uint64_t message_id,
+                  const char *method_name)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+              "Test #%u: Host received method for message ID %" PRIu64 ":\n"
+              "%s\n",
+              test, message_id, method_name);
+  /** @todo FIXME: check message */
+}
+
+
+static void
+host_recv_modifier (void *cls,
+                    const struct GNUNET_PSYC_MessageHeader *msg,
+                    const struct GNUNET_MessageHeader *pmsg,
+                    uint64_t message_id,
+                    enum GNUNET_PSYC_Operator oper,
+                    const char *name,
+                    const void *value,
+                    uint16_t value_size,
+                    uint16_t full_value_size)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+              "Test #%u: Host received modifier for message ID %" PRIu64 ":\n"
+              "%c%s: %.*s\n",
+              test, message_id, oper, name, value_size, (const char *) value);
+}
+
+
+static void
+host_recv_data (void *cls,
+                const struct GNUNET_PSYC_MessageHeader *msg,
+                const struct GNUNET_MessageHeader *pmsg,
+                uint64_t message_id,
+                const void *data,
+                uint16_t data_size)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+              "Test #%u: Host received data for message ID %" PRIu64 ":\n"
+              "%.*s\n",
+              test, message_id, data_size, (const char *) data);
+}
+
+
+static void
+host_recv_eom (void *cls,
+               const struct GNUNET_PSYC_MessageHeader *msg,
+               const struct GNUNET_MessageHeader *pmsg,
+               uint64_t message_id,
+               uint8_t is_cancelled)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+              "Test #%u: Host received end of message ID %" PRIu64
+              ", cancelled: %u\n",
+              test, message_id, is_cancelled);
+
+  switch (test)
+  {
+  case TEST_HOST_ANNOUNCE:
+    test = TEST_HOST_ANNOUNCE_END;
+    break;
+
+  case TEST_HOST_ANNOUNCE_END:
+    guest_talk ();
+    break;
+
+  case TEST_HOST_ANNOUNCE2:
+    test = TEST_HOST_ANNOUNCE2_END;
+    break;
+
+  case TEST_HOST_ANNOUNCE2_END:
+    guest_history_replay ();
+    break;
+
+  case TEST_GUEST_TALK:
+    host_announce2 ();
+    break;
+
+  default:
+    if (TEST_GUEST_LEAVE <= test)
+      break;
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Invalid test: #%u\n", test);
+    GNUNET_assert (0);
+  }
+}
+
+
+static void
+guest_talk ()
+{
+  test = TEST_GUEST_TALK;
+
+  tmit = (struct TransmitClosure) {};
+  tmit.env = GNUNET_PSYC_env_create ();
+  GNUNET_PSYC_env_add (tmit.env, GNUNET_PSYC_OP_ASSIGN,
+                       "_bar_foo", DATA2ARG ("one two three"));
+  GNUNET_PSYC_env_add (tmit.env, GNUNET_PSYC_OP_ASSIGN,
+                       "_bar_baz", DATA2ARG ("four five"));
+  tmit.data[0] = "zzz xxx yyy ";
+  tmit.data[1] = "zyx wvu tsr qpo.\n";
+  tmit.data_delay[1] = 1;
+  tmit.data[2] = "testing ten nine eight.\n";
+  tmit.data_count = 3;
+
+  tmit.guest_talk
+    = GNUNET_SOCIAL_guest_talk (gst, "_converse_guest", tmit.env,
+                                &notify_data, &tmit,
+                                GNUNET_SOCIAL_TALK_NONE);
+}
+
+
+static void
+host_announce ()
+{
+  test = TEST_HOST_ANNOUNCE;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+              "Test #%u: Host announcement.\n", test);
+
+  tmit = (struct TransmitClosure) {};
+  tmit.env = GNUNET_PSYC_env_create ();
+  GNUNET_PSYC_env_add (tmit.env, GNUNET_PSYC_OP_ASSIGN,
+                       "_foo", DATA2ARG ("bar baz"));
+  GNUNET_PSYC_env_add (tmit.env, GNUNET_PSYC_OP_ASSIGN,
+                       "_foo_bar", DATA2ARG ("foo bar"));
+  GNUNET_PSYC_env_add (tmit.env, GNUNET_PSYC_OP_ASSIGN,
+                       "_foo_bar_baz", DATA2ARG ("foo bar baz"));
+  tmit.data[0] = "aaa bbb ccc ";
+  tmit.data[1] = "abc def ghi jkl.\n";
+  tmit.data_delay[1] = 1;
+  tmit.data[2] = "testing one two three ";
+  tmit.data[3] = "four five.\n";
+  tmit.data_count = 4;
+
+  tmit.host_ann
+    = GNUNET_SOCIAL_host_announce (hst, "_converse_host", tmit.env,
+                                   &notify_data, &tmit,
+                                   GNUNET_SOCIAL_ANNOUNCE_NONE);
+}
+
+
+static void
+host_announce2 ()
+{
+  GNUNET_assert (2 == mod_foo_bar_rcls.n);
+  GNUNET_PSYC_slicer_modifier_remove (guest_slicer, "_foo_bar",
+                                      guest_recv_mod_foo_bar);
+
+  test = TEST_HOST_ANNOUNCE2;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+              "Test #%u: Host announcement 2.\n", test);
+
+  tmit = (struct TransmitClosure) {};
+  tmit.env = GNUNET_PSYC_env_create ();
+  GNUNET_PSYC_env_add (tmit.env, GNUNET_PSYC_OP_ASSIGN,
+                       "_foo2", DATA2ARG ("BAR BAZ"));
+  GNUNET_PSYC_env_add (tmit.env, GNUNET_PSYC_OP_ASSIGN,
+                       "_foo2_bar", DATA2ARG ("FOO BAR"));
+  GNUNET_PSYC_env_add (tmit.env, GNUNET_PSYC_OP_ASSIGN,
+                       "_foo2_bar_baz", DATA2ARG ("FOO BAR BAZ"));
+  tmit.data[0] = "AAA BBB CCC ";
+  tmit.data[1] = "ABC DEF GHI JKL.\n";
+  tmit.data[2] = "TESTING ONE TWO THREE.\n";
+  tmit.data_count = 3;
+
+  tmit.host_ann
+    = GNUNET_SOCIAL_host_announce (hst, "_converse_host_two", tmit.env,
+                                   &notify_data, &tmit,
+                                   GNUNET_SOCIAL_ANNOUNCE_NONE);
+}
+
+
+static void
+guest_recv_entry_decision (void *cls,
+                           int is_admitted,
+                           const struct GNUNET_PSYC_Message *entry_msg)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+              "Test #%u: Guest received entry decision (try %u): %d.\n",
+              test, join_req_count, is_admitted);
+
+  if (NULL != entry_msg)
+  {
+    struct GNUNET_PSYC_Environment *env = GNUNET_PSYC_env_create ();
+    const char *method_name = NULL;
+    const void *data = NULL;
+    uint16_t data_size = 0;
+    struct GNUNET_PSYC_MessageHeader *
+      pmsg = GNUNET_PSYC_message_header_create_from_psyc (entry_msg);
+    GNUNET_PSYC_message_parse (pmsg, &method_name, env, &data, &data_size);
+    GNUNET_free (pmsg);
+
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "%s\n%.*s\n",
+                method_name, data_size, (const char *) data);
+    /** @todo FIXME: check response message */
+  }
+
+  switch (test)
+  {
+  case TEST_GUEST_RECV_ENTRY_DCSN_REFUSE:
+    GNUNET_assert (GNUNET_NO == is_admitted);
+    test = TEST_HOST_ANSWER_DOOR_ADMIT;
+    GNUNET_SOCIAL_guest_disconnect (gst, &guest_enter, NULL);
+    break;
+
+  case TEST_GUEST_RECV_ENTRY_DCSN_ADMIT:
+    GNUNET_assert (GNUNET_YES == is_admitted);
+    host_announce ();
+    break;
+
+  case TEST_GUEST_ENTER_BY_NAME:
+    GNUNET_SCHEDULER_add_now (&schedule_reconnect, NULL);
+    break;
+
+  default:
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "invalid test: %d\n", test);
+    GNUNET_assert (0);
+  }
+}
+
+
+static void
+host_answer_door (void *cls,
+                  struct GNUNET_SOCIAL_Nym *nym,
+                  const char *method_name,
+                  struct GNUNET_PSYC_Environment *env,
+                  const void *data,
+                  size_t data_size)
+{
+  join_req_count++;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+              "Test #%u: Host received entry request from guest (try %u).\n",
+              (uint8_t) test, join_req_count);
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "%s\n%.*s\n",
+              method_name, (int) data_size, (const char *) data);
+
+  switch (test)
+  {
+  case TEST_HOST_ANSWER_DOOR_REFUSE:
+    test = TEST_GUEST_RECV_ENTRY_DCSN_REFUSE;
+    join_resp = GNUNET_PSYC_message_create ("_notice_place_refuse", env,
+                                            DATA2ARG ("Go away!"));
+    GNUNET_SOCIAL_host_entry_decision (hst, nym, GNUNET_NO, join_resp);
+    break;
+
+  case TEST_HOST_ANSWER_DOOR_ADMIT:
+    test = TEST_GUEST_RECV_ENTRY_DCSN_ADMIT;
+    // fall through
+
+  case TEST_GUEST_ENTER_BY_NAME:
+   join_resp = GNUNET_PSYC_message_create ("_notice_place_admit", env,
+                                            DATA2ARG ("Welcome, nym!"));
+    GNUNET_SOCIAL_host_entry_decision (hst, nym, GNUNET_YES, join_resp);
+    break;
+
+  default:
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Invalid test: #%u\n", test);
+    GNUNET_assert (0);
+  }
+}
+
+
+static void
+guest_recv_local_enter (void *cls, int result,
+                        const struct GNUNET_CRYPTO_EddsaPublicKey 
*place_pub_key,
+                        uint64_t max_message_id)
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+              "Test #%u: Guest entered local place: %d\n",
+              test, result);
+  GNUNET_assert (GNUNET_OK == result);
+}
+
+
+static void
+guest_enter ()
+{
+  GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+              "Test #%u: Entering place as guest.\n", test);
+
+  struct GuestEnterMessage *emsg = &guest_enter_msg;
+
+  emsg->method_name = "_request_enter";
+  emsg->env = GNUNET_PSYC_env_create ();
+  GNUNET_PSYC_env_add (emsg->env, GNUNET_PSYC_OP_ASSIGN,
+                       "_abc", "abc def", 7);
+  GNUNET_PSYC_env_add (emsg->env, GNUNET_PSYC_OP_ASSIGN,
+                       "_abc_def", "abc def ghi", 11);
+  emsg->data = "let me in";
+  emsg->data_size = strlen (emsg->data) + 1;
+  emsg->msg = GNUNET_PSYC_message_create (emsg->method_name, emsg->env,
+                                          emsg->data, emsg->data_size);
+
+  gst = GNUNET_SOCIAL_guest_enter (app, guest_ego, &place_pub_key,
+                                   GNUNET_PSYC_SLAVE_JOIN_NONE,
+                                   &this_peer, 0, NULL, emsg->msg, 
guest_slicer,
+                                   guest_recv_local_enter,
+                                   guest_recv_entry_decision, NULL);
+  gst_plc = GNUNET_SOCIAL_guest_get_place (gst);
+
+  GNUNET_SOCIAL_place_msg_proc_set (gst_plc, "_converse",
+                                    GNUNET_SOCIAL_MSG_PROC_SAVE);
+}
+
+
+static void
+guest_enter_by_name ()
+{
+  test = TEST_GUEST_ENTER_BY_NAME;
+  GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+              "Test #%u: Entering place by name as guest.\n", test);
+
+  struct GuestEnterMessage *emsg = &guest_enter_msg;
+
+  emsg->method_name = "_request_enter";
+  emsg->env = GNUNET_PSYC_env_create ();
+  GNUNET_PSYC_env_add (emsg->env, GNUNET_PSYC_OP_ASSIGN,
+                       "_abc", "abc def", 7);
+  GNUNET_PSYC_env_add (emsg->env, GNUNET_PSYC_OP_ASSIGN,
+                       "_abc_def", "abc def ghi", 11);
+  emsg->data = "let me in";
+  emsg->data_size = strlen (emsg->data) + 1;
+  emsg->msg = GNUNET_PSYC_message_create (emsg->method_name, emsg->env,
+                                          emsg->data, emsg->data_size);
+
+  gst = GNUNET_SOCIAL_guest_enter_by_name (app, guest_ego,
+                                           "home.host.gnu", "let.me*in!",
+                                           emsg->msg, guest_slicer,
+                                           guest_recv_local_enter,
+                                           guest_recv_entry_decision, NULL);
+  gst_plc = GNUNET_SOCIAL_guest_get_place (gst);
+}
+
+
+static void
+app_recv_zone_add_nym_result (void *cls, int64_t result,
+                              const void *data, uint16_t data_size)
+{
+  GNUNET_assert (GNUNET_YES == result);
+  is_guest_nym_added = GNUNET_YES;
+}
+
+
+static void
+guest_init ()
+{
+  guest_pub_key = GNUNET_SOCIAL_ego_get_pub_key (guest_ego);
+
+  guest_slicer = GNUNET_PSYC_slicer_create ();
+  GNUNET_PSYC_slicer_method_add (guest_slicer, "", NULL,
+                                 guest_recv_method, guest_recv_modifier,
+                                 guest_recv_data, guest_recv_eom, NULL);
+  GNUNET_PSYC_slicer_modifier_add (guest_slicer, "_foo_bar",
+                                   guest_recv_mod_foo_bar, &mod_foo_bar_rcls);
+  test = TEST_HOST_ANSWER_DOOR_REFUSE;
+
+  GNUNET_SOCIAL_zone_add_nym (app, guest_ego, "host", host_pub_key,
+                              GNUNET_TIME_relative_to_absolute 
(GNUNET_TIME_UNIT_MINUTES),
+                              app_recv_zone_add_nym_result, NULL);
+}
+
+
+static void
+id_host_created (void *cls, const char *emsg)
+{
+  if (NULL != emsg)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Test #%u: Could not create host identity: %s\n",
+                test, emsg);
+#if ! DEBUG_TEST_SOCIAL
+    GNUNET_assert (0);
+#endif
+  }
+
+}
+
+
+static void
+id_guest_created (void *cls, const char *emsg)
+{
+  if (NULL != emsg)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Test #%u: Could not create guest identity: %s\n",
+                test, emsg);
+#if ! DEBUG_TEST_SOCIAL
+    GNUNET_assert (0);
+#endif
+  }
+  //if (NULL != guest_ego)
+  //  guest_init ();
+}
+
+
+static void
+host_entered (void *cls, int result,
+              const struct GNUNET_CRYPTO_EddsaPublicKey *home_pub_key,
+              uint64_t max_message_id)
+{
+  place_pub_key = *home_pub_key;
+  GNUNET_CRYPTO_hash (&place_pub_key, sizeof (place_pub_key), &place_pub_hash);
+  GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+              "Test #%u: Host entered place %s\n",
+              test, GNUNET_h2s (&place_pub_hash));
+  guest_enter ();
+}
+
+
+static void
+host_enter ()
+{
+  host_slicer = GNUNET_PSYC_slicer_create ();
+  GNUNET_PSYC_slicer_method_add (host_slicer, "", NULL,
+                                 host_recv_method, host_recv_modifier,
+                                 host_recv_data, host_recv_eom, NULL);
+
+  GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+              "Test #%u: Entering place as host.\n", test);
+  test = TEST_HOST_ENTER;
+  hst = GNUNET_SOCIAL_host_enter (app, host_ego,
+                                  GNUNET_PSYC_CHANNEL_PRIVATE,
+                                  host_slicer, host_entered,
+                                  host_answer_door, host_farewell, NULL);
+  hst_plc = GNUNET_SOCIAL_host_get_place (hst);
+
+  GNUNET_SOCIAL_place_msg_proc_set (hst_plc, "_converse",
+                                    GNUNET_SOCIAL_MSG_PROC_RELAY);
+}
+
+
+static void
+start_app_if_ready ()
+{
+  if (NULL == identity_host_ego || NULL == identity_guest_ego)
+  {
+    return;
+  }
+  GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+              "starting app...\n");
+  app = GNUNET_SOCIAL_app_connect (cfg,
+                                   app_id,
+                                   app_recv_ego,
+                                   app_recv_host,
+                                   app_recv_guest,
+                                   app_connected,
+                                   NULL);
+}
+
+
+static void
+identity_ego_cb (void *cls, struct GNUNET_IDENTITY_Ego *ego,
+                 void **ctx, const char *name)
+{
+  if (NULL != ego)
+  {
+    if (ego == identity_host_ego)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+                  "Host ego deleted\n");
+    }
+    else if (ego == identity_guest_ego)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+                  "Guest ego deleted\n");
+    }
+    else if (0 == strcmp (name, host_name))
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+                  "Created ego %s\n",
+                  name);
+      identity_host_ego = ego;
+      start_app_if_ready ();
+    }
+    else if (0 == strcmp (name, guest_name))
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+                  "Created guest ego %s\n",
+                  name);
+      identity_guest_ego = ego;
+      start_app_if_ready ();
+    }
+  }
+}
+
+
+/**
+ * Main function of the test, run from scheduler.
+ *
+ * @param cls NULL
+ * @param cfg configuration we use (also to connect to Social service)
+ * @param peer handle to access more of the peer (not used)
+ */
+static void
+#if DEBUG_TEST_SOCIAL
+run (void *cls, char *const *args, const char *cfgfile,
+     const struct GNUNET_CONFIGURATION_Handle *c)
+#else
+run (void *cls,
+     const struct GNUNET_CONFIGURATION_Handle *c,
+     struct GNUNET_TESTING_Peer *peer)
+#endif
+{
+  cfg = c;
+  res = 1;
+  end_badly_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT,
+                                                &end_badly, NULL);
+  GNUNET_SCHEDULER_add_shutdown (&end_shutdown,
+                                 NULL);
+  GNUNET_CRYPTO_get_peer_identity (cfg, &this_peer);
+
+  id = GNUNET_IDENTITY_connect (cfg, &identity_ego_cb, NULL);
+
+  test = TEST_IDENTITIES_CREATE;
+  GNUNET_IDENTITY_create (id, host_name, &id_host_created, NULL);
+  GNUNET_IDENTITY_create (id, guest_name, &id_guest_created, NULL);
+}
+
+
+int
+main (int argc, char *argv[])
+{
+  res = 1;
+#if DEBUG_TEST_SOCIAL
+  const struct GNUNET_GETOPT_CommandLineOption opts[] = {
+    GNUNET_GETOPT_OPTION_END
+  };
+  if (GNUNET_OK != GNUNET_PROGRAM_run (argc, argv, "test-social",
+                                       "test-social [options]",
+                                       opts, &run, NULL))
+    return 1;
+#else
+  if (0 != GNUNET_TESTING_peer_run ("test-social", "test_social.conf", &run, 
NULL))
+    return 1;
+#endif
+  return res;
+}
+
+/* end of test_social.c */
diff --git a/src/social/test_social.conf b/src/social/test_social.conf
new file mode 100644
index 0000000..bc48776
--- /dev/null
+++ b/src/social/test_social.conf
@@ -0,0 +1,19 @@
address@hidden@ ../../contrib/conf/gnunet/no_forcestart.conf
+
+[PATHS]
+GNUNET_TEST_HOME = $GNUNET_TMP/gnunet-test-social/
+
+[social]
+IMMEDIATE_START = YES
+
+[transport]
+PLUGINS = tcp
+
+[nat]
+DISABLEV6 = YES
+ENABLE_UPNP = NO
+BEHIND_NAT = NO
+ALLOW_NAT = NO
+INTERNAL_ADDRESS = 127.0.0.1
+EXTERNAL_ADDRESS = 127.0.0.1
+

-- 
To stop receiving notification emails like this one, please contact
address@hidden



reply via email to

[Prev in Thread] Current Thread [Next in Thread]