diff options
author | Bertrand Marc <beberking@gmail.com> | 2012-05-02 21:43:37 +0200 |
---|---|---|
committer | Bertrand Marc <beberking@gmail.com> | 2012-05-02 21:43:37 +0200 |
commit | 2b81464a43485fcc8ce079fafdee7b7a171835f4 (patch) | |
tree | 394774c0f735199b57d51a2d3840356317853fe1 /src/chat |
Imported Upstream version 0.9.2upstream/0.9.2
Diffstat (limited to 'src/chat')
-rw-r--r-- | src/chat/Makefile.am | 128 | ||||
-rw-r--r-- | src/chat/Makefile.in | 1062 | ||||
-rw-r--r-- | src/chat/chat.c | 822 | ||||
-rw-r--r-- | src/chat/chat.conf.in | 22 | ||||
-rw-r--r-- | src/chat/chat.h | 485 | ||||
-rw-r--r-- | src/chat/gnunet-chat.c | 674 | ||||
-rw-r--r-- | src/chat/gnunet-service-chat.c | 1737 | ||||
-rw-r--r-- | src/chat/test_chat.c | 586 | ||||
-rw-r--r-- | src/chat/test_chat_data.conf | 56 | ||||
-rw-r--r-- | src/chat/test_chat_peer1.conf | 95 | ||||
-rw-r--r-- | src/chat/test_chat_peer2.conf | 97 | ||||
-rw-r--r-- | src/chat/test_chat_peer3.conf | 96 | ||||
-rw-r--r-- | src/chat/test_chat_private.c | 654 |
13 files changed, 6514 insertions, 0 deletions
diff --git a/src/chat/Makefile.am b/src/chat/Makefile.am new file mode 100644 index 0000000..2132836 --- /dev/null +++ b/src/chat/Makefile.am @@ -0,0 +1,128 @@ +INCLUDES = -I$(top_srcdir)/src/include + +pkgcfgdir= $(pkgdatadir)/config.d/ + +pkgcfg_DATA = \ + chat.conf + +if MINGW + WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols +endif + +if USE_COVERAGE + AM_CFLAGS = -fprofile-arcs -ftest-coverage +endif + +lib_LTLIBRARIES = libgnunetchat.la + +libgnunetchat_la_SOURCES = \ + chat.c chat.h + +libgnunetchat_la_LIBADD = \ + $(top_builddir)/src/util/libgnunetutil.la + +libgnunetchat_la_LDFLAGS = \ + $(GN_LIB_LDFLAGS) $(WINFLAGS) \ + -version-info 0:0:0 + +bin_PROGRAMS = \ + gnunet-service-chat \ + gnunet-chat + +gnunet_service_chat_SOURCES = \ + gnunet-service-chat.c +gnunet_service_chat_LDADD = \ + $(top_builddir)/src/core/libgnunetcore.la \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(GN_LIBINTL) + +gnunet_chat_SOURCES = \ + gnunet-chat.c +gnunet_chat_LDADD = \ + $(top_builddir)/src/chat/libgnunetchat.la \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(GN_LIBINTL) +gnunet_chat_DEPENDENCIES = \ + libgnunetchat.la + +check_PROGRAMS = \ + test_chat \ + test_chat_acknowledgement \ + test_chat_anonymous \ + test_chat_authentication \ + test_chat_p2p \ + test_chat_acknowledgement_p2p \ + test_chat_anonymous_p2p \ + test_chat_authentication_p2p \ + test_chat_private \ + test_chat_private_p2p + +if ENABLE_TEST_RUN +TESTS = $(check_PROGRAMS) +endif + +test_chat_SOURCES = \ + test_chat.c +test_chat_LDADD = \ + $(top_builddir)/src/chat/libgnunetchat.la \ + $(top_builddir)/src/util/libgnunetutil.la + +test_chat_acknowledgement_SOURCES = \ + test_chat.c +test_chat_acknowledgement_LDADD = \ + $(top_builddir)/src/chat/libgnunetchat.la \ + $(top_builddir)/src/util/libgnunetutil.la + +test_chat_anonymous_SOURCES = \ + test_chat.c +test_chat_anonymous_LDADD = \ + $(top_builddir)/src/chat/libgnunetchat.la \ + $(top_builddir)/src/util/libgnunetutil.la + +test_chat_authentication_SOURCES = \ + test_chat.c +test_chat_authentication_LDADD = \ + $(top_builddir)/src/chat/libgnunetchat.la \ + $(top_builddir)/src/util/libgnunetutil.la + +test_chat_p2p_SOURCES = \ + test_chat.c +test_chat_p2p_LDADD = \ + $(top_builddir)/src/chat/libgnunetchat.la \ + $(top_builddir)/src/util/libgnunetutil.la + +test_chat_acknowledgement_p2p_SOURCES = \ + test_chat.c +test_chat_acknowledgement_p2p_LDADD = \ + $(top_builddir)/src/chat/libgnunetchat.la \ + $(top_builddir)/src/util/libgnunetutil.la + +test_chat_anonymous_p2p_SOURCES = \ + test_chat.c +test_chat_anonymous_p2p_LDADD = \ + $(top_builddir)/src/chat/libgnunetchat.la \ + $(top_builddir)/src/util/libgnunetutil.la + +test_chat_authentication_p2p_SOURCES = \ + test_chat.c +test_chat_authentication_p2p_LDADD = \ + $(top_builddir)/src/chat/libgnunetchat.la \ + $(top_builddir)/src/util/libgnunetutil.la + +test_chat_private_SOURCES = \ + test_chat_private.c +test_chat_private_LDADD = \ + $(top_builddir)/src/chat/libgnunetchat.la \ + $(top_builddir)/src/util/libgnunetutil.la + +test_chat_private_p2p_SOURCES = \ + test_chat_private.c +test_chat_private_p2p_LDADD = \ + $(top_builddir)/src/chat/libgnunetchat.la \ + $(top_builddir)/src/util/libgnunetutil.la + +EXTRA_DIST = \ + test_chat_data.conf \ + test_chat_peer1.conf \ + test_chat_peer2.conf \ + test_chat_peer3.conf diff --git a/src/chat/Makefile.in b/src/chat/Makefile.in new file mode 100644 index 0000000..96c673c --- /dev/null +++ b/src/chat/Makefile.in @@ -0,0 +1,1062 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# This Makefile.in 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. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + + + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +bin_PROGRAMS = gnunet-service-chat$(EXEEXT) gnunet-chat$(EXEEXT) +check_PROGRAMS = test_chat$(EXEEXT) test_chat_acknowledgement$(EXEEXT) \ + test_chat_anonymous$(EXEEXT) test_chat_authentication$(EXEEXT) \ + test_chat_p2p$(EXEEXT) test_chat_acknowledgement_p2p$(EXEEXT) \ + test_chat_anonymous_p2p$(EXEEXT) \ + test_chat_authentication_p2p$(EXEEXT) \ + test_chat_private$(EXEEXT) test_chat_private_p2p$(EXEEXT) +subdir = src/chat +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in \ + $(srcdir)/chat.conf.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/absolute-header.m4 \ + $(top_srcdir)/m4/align.m4 $(top_srcdir)/m4/argz.m4 \ + $(top_srcdir)/m4/gettext.m4 $(top_srcdir)/m4/iconv.m4 \ + $(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \ + $(top_srcdir)/m4/lib-prefix.m4 $(top_srcdir)/m4/libcurl.m4 \ + $(top_srcdir)/m4/libgcrypt.m4 $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/libunistring.m4 $(top_srcdir)/m4/ltdl.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/m4/nls.m4 $(top_srcdir)/m4/po.m4 \ + $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/acinclude.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/gnunet_config.h +CONFIG_CLEAN_FILES = chat.conf +CONFIG_CLEAN_VPATH_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(bindir)" \ + "$(DESTDIR)$(pkgcfgdir)" +LTLIBRARIES = $(lib_LTLIBRARIES) +libgnunetchat_la_DEPENDENCIES = \ + $(top_builddir)/src/util/libgnunetutil.la +am_libgnunetchat_la_OBJECTS = chat.lo +libgnunetchat_la_OBJECTS = $(am_libgnunetchat_la_OBJECTS) +AM_V_lt = $(am__v_lt_$(V)) +am__v_lt_ = $(am__v_lt_$(AM_DEFAULT_VERBOSITY)) +am__v_lt_0 = --silent +libgnunetchat_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(libgnunetchat_la_LDFLAGS) $(LDFLAGS) \ + -o $@ +PROGRAMS = $(bin_PROGRAMS) +am_gnunet_chat_OBJECTS = gnunet-chat.$(OBJEXT) +gnunet_chat_OBJECTS = $(am_gnunet_chat_OBJECTS) +am__DEPENDENCIES_1 = +am_gnunet_service_chat_OBJECTS = gnunet-service-chat.$(OBJEXT) +gnunet_service_chat_OBJECTS = $(am_gnunet_service_chat_OBJECTS) +gnunet_service_chat_DEPENDENCIES = \ + $(top_builddir)/src/core/libgnunetcore.la \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(am__DEPENDENCIES_1) +am_test_chat_OBJECTS = test_chat.$(OBJEXT) +test_chat_OBJECTS = $(am_test_chat_OBJECTS) +test_chat_DEPENDENCIES = $(top_builddir)/src/chat/libgnunetchat.la \ + $(top_builddir)/src/util/libgnunetutil.la +am_test_chat_acknowledgement_OBJECTS = test_chat.$(OBJEXT) +test_chat_acknowledgement_OBJECTS = \ + $(am_test_chat_acknowledgement_OBJECTS) +test_chat_acknowledgement_DEPENDENCIES = \ + $(top_builddir)/src/chat/libgnunetchat.la \ + $(top_builddir)/src/util/libgnunetutil.la +am_test_chat_acknowledgement_p2p_OBJECTS = test_chat.$(OBJEXT) +test_chat_acknowledgement_p2p_OBJECTS = \ + $(am_test_chat_acknowledgement_p2p_OBJECTS) +test_chat_acknowledgement_p2p_DEPENDENCIES = \ + $(top_builddir)/src/chat/libgnunetchat.la \ + $(top_builddir)/src/util/libgnunetutil.la +am_test_chat_anonymous_OBJECTS = test_chat.$(OBJEXT) +test_chat_anonymous_OBJECTS = $(am_test_chat_anonymous_OBJECTS) +test_chat_anonymous_DEPENDENCIES = \ + $(top_builddir)/src/chat/libgnunetchat.la \ + $(top_builddir)/src/util/libgnunetutil.la +am_test_chat_anonymous_p2p_OBJECTS = test_chat.$(OBJEXT) +test_chat_anonymous_p2p_OBJECTS = \ + $(am_test_chat_anonymous_p2p_OBJECTS) +test_chat_anonymous_p2p_DEPENDENCIES = \ + $(top_builddir)/src/chat/libgnunetchat.la \ + $(top_builddir)/src/util/libgnunetutil.la +am_test_chat_authentication_OBJECTS = test_chat.$(OBJEXT) +test_chat_authentication_OBJECTS = \ + $(am_test_chat_authentication_OBJECTS) +test_chat_authentication_DEPENDENCIES = \ + $(top_builddir)/src/chat/libgnunetchat.la \ + $(top_builddir)/src/util/libgnunetutil.la +am_test_chat_authentication_p2p_OBJECTS = test_chat.$(OBJEXT) +test_chat_authentication_p2p_OBJECTS = \ + $(am_test_chat_authentication_p2p_OBJECTS) +test_chat_authentication_p2p_DEPENDENCIES = \ + $(top_builddir)/src/chat/libgnunetchat.la \ + $(top_builddir)/src/util/libgnunetutil.la +am_test_chat_p2p_OBJECTS = test_chat.$(OBJEXT) +test_chat_p2p_OBJECTS = $(am_test_chat_p2p_OBJECTS) +test_chat_p2p_DEPENDENCIES = \ + $(top_builddir)/src/chat/libgnunetchat.la \ + $(top_builddir)/src/util/libgnunetutil.la +am_test_chat_private_OBJECTS = test_chat_private.$(OBJEXT) +test_chat_private_OBJECTS = $(am_test_chat_private_OBJECTS) +test_chat_private_DEPENDENCIES = \ + $(top_builddir)/src/chat/libgnunetchat.la \ + $(top_builddir)/src/util/libgnunetutil.la +am_test_chat_private_p2p_OBJECTS = test_chat_private.$(OBJEXT) +test_chat_private_p2p_OBJECTS = $(am_test_chat_private_p2p_OBJECTS) +test_chat_private_p2p_DEPENDENCIES = \ + $(top_builddir)/src/chat/libgnunetchat.la \ + $(top_builddir)/src/util/libgnunetutil.la +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_$(V)) +am__v_CC_ = $(am__v_CC_$(AM_DEFAULT_VERBOSITY)) +am__v_CC_0 = @echo " CC " $@; +AM_V_at = $(am__v_at_$(V)) +am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY)) +am__v_at_0 = @ +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_$(V)) +am__v_CCLD_ = $(am__v_CCLD_$(AM_DEFAULT_VERBOSITY)) +am__v_CCLD_0 = @echo " CCLD " $@; +AM_V_GEN = $(am__v_GEN_$(V)) +am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY)) +am__v_GEN_0 = @echo " GEN " $@; +SOURCES = $(libgnunetchat_la_SOURCES) $(gnunet_chat_SOURCES) \ + $(gnunet_service_chat_SOURCES) $(test_chat_SOURCES) \ + $(test_chat_acknowledgement_SOURCES) \ + $(test_chat_acknowledgement_p2p_SOURCES) \ + $(test_chat_anonymous_SOURCES) \ + $(test_chat_anonymous_p2p_SOURCES) \ + $(test_chat_authentication_SOURCES) \ + $(test_chat_authentication_p2p_SOURCES) \ + $(test_chat_p2p_SOURCES) $(test_chat_private_SOURCES) \ + $(test_chat_private_p2p_SOURCES) +DIST_SOURCES = $(libgnunetchat_la_SOURCES) $(gnunet_chat_SOURCES) \ + $(gnunet_service_chat_SOURCES) $(test_chat_SOURCES) \ + $(test_chat_acknowledgement_SOURCES) \ + $(test_chat_acknowledgement_p2p_SOURCES) \ + $(test_chat_anonymous_SOURCES) \ + $(test_chat_anonymous_p2p_SOURCES) \ + $(test_chat_authentication_SOURCES) \ + $(test_chat_authentication_p2p_SOURCES) \ + $(test_chat_p2p_SOURCES) $(test_chat_private_SOURCES) \ + $(test_chat_private_p2p_SOURCES) +DATA = $(pkgcfg_DATA) +ETAGS = etags +CTAGS = ctags +am__tty_colors = \ +red=; grn=; lgn=; blu=; std= +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +ARGZ_H = @ARGZ_H@ +AS = @AS@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFAULT_INTERFACE = @DEFAULT_INTERFACE@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLDIR = @DLLDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +EXT_LIBS = @EXT_LIBS@ +EXT_LIB_PATH = @EXT_LIB_PATH@ +FGREP = @FGREP@ +GMSGFMT = @GMSGFMT@ +GMSGFMT_015 = @GMSGFMT_015@ +GNUNETDNS_GROUP = @GNUNETDNS_GROUP@ +GN_DAEMON_CONFIG_DIR = @GN_DAEMON_CONFIG_DIR@ +GN_DAEMON_HOME_DIR = @GN_DAEMON_HOME_DIR@ +GN_INTLINCL = @GN_INTLINCL@ +GN_LIBINTL = @GN_LIBINTL@ +GN_LIB_LDFLAGS = @GN_LIB_LDFLAGS@ +GN_PLUGIN_LDFLAGS = @GN_PLUGIN_LDFLAGS@ +GN_USER_HOME_DIR = @GN_USER_HOME_DIR@ +GREP = @GREP@ +HAVE_LIBUNISTRING = @HAVE_LIBUNISTRING@ +INCLTDL = @INCLTDL@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INTLLIBS = @INTLLIBS@ +INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBADD_DL = @LIBADD_DL@ +LIBADD_DLD_LINK = @LIBADD_DLD_LINK@ +LIBADD_DLOPEN = @LIBADD_DLOPEN@ +LIBADD_SHL_LOAD = @LIBADD_SHL_LOAD@ +LIBCURL = @LIBCURL@ +LIBCURL_CPPFLAGS = @LIBCURL_CPPFLAGS@ +LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@ +LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@ +LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@ +LIBICONV = @LIBICONV@ +LIBINTL = @LIBINTL@ +LIBLTDL = @LIBLTDL@ +LIBOBJS = @LIBOBJS@ +LIBPREFIX = @LIBPREFIX@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIBUNISTRING = @LIBUNISTRING@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTDLDEPS = @LTDLDEPS@ +LTDLINCL = @LTDLINCL@ +LTDLOPEN = @LTDLOPEN@ +LTLIBICONV = @LTLIBICONV@ +LTLIBINTL = @LTLIBINTL@ +LTLIBOBJS = @LTLIBOBJS@ +LTLIBUNISTRING = @LTLIBUNISTRING@ +LT_CONFIG_H = @LT_CONFIG_H@ +LT_DLLOADERS = @LT_DLLOADERS@ +LT_DLPREOPEN = @LT_DLPREOPEN@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +MSGFMT = @MSGFMT@ +MSGFMT_015 = @MSGFMT_015@ +MSGMERGE = @MSGMERGE@ +MYSQL_CPPFLAGS = @MYSQL_CPPFLAGS@ +MYSQL_LDFLAGS = @MYSQL_LDFLAGS@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJC = @OBJC@ +OBJCDEPMODE = @OBJCDEPMODE@ +OBJCFLAGS = @OBJCFLAGS@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +POSTGRES_CPPFLAGS = @POSTGRES_CPPFLAGS@ +POSTGRES_LDFLAGS = @POSTGRES_LDFLAGS@ +POSUB = @POSUB@ +PYTHON = @PYTHON@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SQLITE_CPPFLAGS = @SQLITE_CPPFLAGS@ +SQLITE_LDFLAGS = @SQLITE_LDFLAGS@ +STRIP = @STRIP@ +SUDO_BINARY = @SUDO_BINARY@ +UNIXONLY = @UNIXONLY@ +USE_NLS = @USE_NLS@ +VERSION = @VERSION@ +XGETTEXT = @XGETTEXT@ +XGETTEXT_015 = @XGETTEXT_015@ +XMKMF = @XMKMF@ +X_CFLAGS = @X_CFLAGS@ +X_EXTRA_LIBS = @X_EXTRA_LIBS@ +X_LIBS = @X_LIBS@ +X_PRE_LIBS = @X_PRE_LIBS@ +_libcurl_config = @_libcurl_config@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +ac_ct_OBJC = @ac_ct_OBJC@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_target = @build_target@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +lt_ECHO = @lt_ECHO@ +ltdl_LIBOBJS = @ltdl_LIBOBJS@ +ltdl_LTLIBOBJS = @ltdl_LTLIBOBJS@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +subdirs = @subdirs@ +sys_symbol_underscore = @sys_symbol_underscore@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +INCLUDES = -I$(top_srcdir)/src/include +pkgcfgdir = $(pkgdatadir)/config.d/ +pkgcfg_DATA = \ + chat.conf + +@MINGW_TRUE@WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols +@USE_COVERAGE_TRUE@AM_CFLAGS = -fprofile-arcs -ftest-coverage +lib_LTLIBRARIES = libgnunetchat.la +libgnunetchat_la_SOURCES = \ + chat.c chat.h + +libgnunetchat_la_LIBADD = \ + $(top_builddir)/src/util/libgnunetutil.la + +libgnunetchat_la_LDFLAGS = \ + $(GN_LIB_LDFLAGS) $(WINFLAGS) \ + -version-info 0:0:0 + +gnunet_service_chat_SOURCES = \ + gnunet-service-chat.c + +gnunet_service_chat_LDADD = \ + $(top_builddir)/src/core/libgnunetcore.la \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(GN_LIBINTL) + +gnunet_chat_SOURCES = \ + gnunet-chat.c + +gnunet_chat_LDADD = \ + $(top_builddir)/src/chat/libgnunetchat.la \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(GN_LIBINTL) + +gnunet_chat_DEPENDENCIES = \ + libgnunetchat.la + +@ENABLE_TEST_RUN_TRUE@TESTS = $(check_PROGRAMS) +test_chat_SOURCES = \ + test_chat.c + +test_chat_LDADD = \ + $(top_builddir)/src/chat/libgnunetchat.la \ + $(top_builddir)/src/util/libgnunetutil.la + +test_chat_acknowledgement_SOURCES = \ + test_chat.c + +test_chat_acknowledgement_LDADD = \ + $(top_builddir)/src/chat/libgnunetchat.la \ + $(top_builddir)/src/util/libgnunetutil.la + +test_chat_anonymous_SOURCES = \ + test_chat.c + +test_chat_anonymous_LDADD = \ + $(top_builddir)/src/chat/libgnunetchat.la \ + $(top_builddir)/src/util/libgnunetutil.la + +test_chat_authentication_SOURCES = \ + test_chat.c + +test_chat_authentication_LDADD = \ + $(top_builddir)/src/chat/libgnunetchat.la \ + $(top_builddir)/src/util/libgnunetutil.la + +test_chat_p2p_SOURCES = \ + test_chat.c + +test_chat_p2p_LDADD = \ + $(top_builddir)/src/chat/libgnunetchat.la \ + $(top_builddir)/src/util/libgnunetutil.la + +test_chat_acknowledgement_p2p_SOURCES = \ + test_chat.c + +test_chat_acknowledgement_p2p_LDADD = \ + $(top_builddir)/src/chat/libgnunetchat.la \ + $(top_builddir)/src/util/libgnunetutil.la + +test_chat_anonymous_p2p_SOURCES = \ + test_chat.c + +test_chat_anonymous_p2p_LDADD = \ + $(top_builddir)/src/chat/libgnunetchat.la \ + $(top_builddir)/src/util/libgnunetutil.la + +test_chat_authentication_p2p_SOURCES = \ + test_chat.c + +test_chat_authentication_p2p_LDADD = \ + $(top_builddir)/src/chat/libgnunetchat.la \ + $(top_builddir)/src/util/libgnunetutil.la + +test_chat_private_SOURCES = \ + test_chat_private.c + +test_chat_private_LDADD = \ + $(top_builddir)/src/chat/libgnunetchat.la \ + $(top_builddir)/src/util/libgnunetutil.la + +test_chat_private_p2p_SOURCES = \ + test_chat_private.c + +test_chat_private_p2p_LDADD = \ + $(top_builddir)/src/chat/libgnunetchat.la \ + $(top_builddir)/src/util/libgnunetutil.la + +EXTRA_DIST = \ + test_chat_data.conf \ + test_chat_peer1.conf \ + test_chat_peer2.conf \ + test_chat_peer3.conf + +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/chat/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/chat/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +chat.conf: $(top_builddir)/config.status $(srcdir)/chat.conf.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +install-libLTLIBRARIES: $(lib_LTLIBRARIES) + @$(NORMAL_INSTALL) + test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)" + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ + } + +uninstall-libLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ + done + +clean-libLTLIBRARIES: + -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) + @list='$(lib_LTLIBRARIES)'; for p in $$list; do \ + dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ + test "$$dir" != "$$p" || dir=.; \ + echo "rm -f \"$${dir}/so_locations\""; \ + rm -f "$${dir}/so_locations"; \ + done +libgnunetchat.la: $(libgnunetchat_la_OBJECTS) $(libgnunetchat_la_DEPENDENCIES) + $(AM_V_CCLD)$(libgnunetchat_la_LINK) -rpath $(libdir) $(libgnunetchat_la_OBJECTS) $(libgnunetchat_la_LIBADD) $(LIBS) +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + test -z "$(bindir)" || $(MKDIR_P) "$(DESTDIR)$(bindir)" + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p || test -f $$p1; \ + then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(bindir)" && rm -f $$files + +clean-binPROGRAMS: + @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +clean-checkPROGRAMS: + @list='$(check_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list +gnunet-chat$(EXEEXT): $(gnunet_chat_OBJECTS) $(gnunet_chat_DEPENDENCIES) + @rm -f gnunet-chat$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(gnunet_chat_OBJECTS) $(gnunet_chat_LDADD) $(LIBS) +gnunet-service-chat$(EXEEXT): $(gnunet_service_chat_OBJECTS) $(gnunet_service_chat_DEPENDENCIES) + @rm -f gnunet-service-chat$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(gnunet_service_chat_OBJECTS) $(gnunet_service_chat_LDADD) $(LIBS) +test_chat$(EXEEXT): $(test_chat_OBJECTS) $(test_chat_DEPENDENCIES) + @rm -f test_chat$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_chat_OBJECTS) $(test_chat_LDADD) $(LIBS) +test_chat_acknowledgement$(EXEEXT): $(test_chat_acknowledgement_OBJECTS) $(test_chat_acknowledgement_DEPENDENCIES) + @rm -f test_chat_acknowledgement$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_chat_acknowledgement_OBJECTS) $(test_chat_acknowledgement_LDADD) $(LIBS) +test_chat_acknowledgement_p2p$(EXEEXT): $(test_chat_acknowledgement_p2p_OBJECTS) $(test_chat_acknowledgement_p2p_DEPENDENCIES) + @rm -f test_chat_acknowledgement_p2p$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_chat_acknowledgement_p2p_OBJECTS) $(test_chat_acknowledgement_p2p_LDADD) $(LIBS) +test_chat_anonymous$(EXEEXT): $(test_chat_anonymous_OBJECTS) $(test_chat_anonymous_DEPENDENCIES) + @rm -f test_chat_anonymous$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_chat_anonymous_OBJECTS) $(test_chat_anonymous_LDADD) $(LIBS) +test_chat_anonymous_p2p$(EXEEXT): $(test_chat_anonymous_p2p_OBJECTS) $(test_chat_anonymous_p2p_DEPENDENCIES) + @rm -f test_chat_anonymous_p2p$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_chat_anonymous_p2p_OBJECTS) $(test_chat_anonymous_p2p_LDADD) $(LIBS) +test_chat_authentication$(EXEEXT): $(test_chat_authentication_OBJECTS) $(test_chat_authentication_DEPENDENCIES) + @rm -f test_chat_authentication$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_chat_authentication_OBJECTS) $(test_chat_authentication_LDADD) $(LIBS) +test_chat_authentication_p2p$(EXEEXT): $(test_chat_authentication_p2p_OBJECTS) $(test_chat_authentication_p2p_DEPENDENCIES) + @rm -f test_chat_authentication_p2p$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_chat_authentication_p2p_OBJECTS) $(test_chat_authentication_p2p_LDADD) $(LIBS) +test_chat_p2p$(EXEEXT): $(test_chat_p2p_OBJECTS) $(test_chat_p2p_DEPENDENCIES) + @rm -f test_chat_p2p$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_chat_p2p_OBJECTS) $(test_chat_p2p_LDADD) $(LIBS) +test_chat_private$(EXEEXT): $(test_chat_private_OBJECTS) $(test_chat_private_DEPENDENCIES) + @rm -f test_chat_private$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_chat_private_OBJECTS) $(test_chat_private_LDADD) $(LIBS) +test_chat_private_p2p$(EXEEXT): $(test_chat_private_p2p_OBJECTS) $(test_chat_private_p2p_DEPENDENCIES) + @rm -f test_chat_private_p2p$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_chat_private_p2p_OBJECTS) $(test_chat_private_p2p_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/chat.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gnunet-chat.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gnunet-service-chat.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_chat.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_chat_private.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-pkgcfgDATA: $(pkgcfg_DATA) + @$(NORMAL_INSTALL) + test -z "$(pkgcfgdir)" || $(MKDIR_P) "$(DESTDIR)$(pkgcfgdir)" + @list='$(pkgcfg_DATA)'; test -n "$(pkgcfgdir)" || list=; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(pkgcfgdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(pkgcfgdir)" || exit $$?; \ + done + +uninstall-pkgcfgDATA: + @$(NORMAL_UNINSTALL) + @list='$(pkgcfg_DATA)'; test -n "$(pkgcfgdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + test -n "$$files" || exit 0; \ + echo " ( cd '$(DESTDIR)$(pkgcfgdir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(pkgcfgdir)" && rm -f $$files + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +check-TESTS: $(TESTS) + @failed=0; all=0; xfail=0; xpass=0; skip=0; \ + srcdir=$(srcdir); export srcdir; \ + list=' $(TESTS) '; \ + $(am__tty_colors); \ + if test -n "$$list"; then \ + for tst in $$list; do \ + if test -f ./$$tst; then dir=./; \ + elif test -f $$tst; then dir=; \ + else dir="$(srcdir)/"; fi; \ + if $(TESTS_ENVIRONMENT) $${dir}$$tst; then \ + all=`expr $$all + 1`; \ + case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$tst[\ \ ]*) \ + xpass=`expr $$xpass + 1`; \ + failed=`expr $$failed + 1`; \ + col=$$red; res=XPASS; \ + ;; \ + *) \ + col=$$grn; res=PASS; \ + ;; \ + esac; \ + elif test $$? -ne 77; then \ + all=`expr $$all + 1`; \ + case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$tst[\ \ ]*) \ + xfail=`expr $$xfail + 1`; \ + col=$$lgn; res=XFAIL; \ + ;; \ + *) \ + failed=`expr $$failed + 1`; \ + col=$$red; res=FAIL; \ + ;; \ + esac; \ + else \ + skip=`expr $$skip + 1`; \ + col=$$blu; res=SKIP; \ + fi; \ + echo "$${col}$$res$${std}: $$tst"; \ + done; \ + if test "$$all" -eq 1; then \ + tests="test"; \ + All=""; \ + else \ + tests="tests"; \ + All="All "; \ + fi; \ + if test "$$failed" -eq 0; then \ + if test "$$xfail" -eq 0; then \ + banner="$$All$$all $$tests passed"; \ + else \ + if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \ + banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \ + fi; \ + else \ + if test "$$xpass" -eq 0; then \ + banner="$$failed of $$all $$tests failed"; \ + else \ + if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \ + banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \ + fi; \ + fi; \ + dashes="$$banner"; \ + skipped=""; \ + if test "$$skip" -ne 0; then \ + if test "$$skip" -eq 1; then \ + skipped="($$skip test was not run)"; \ + else \ + skipped="($$skip tests were not run)"; \ + fi; \ + test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \ + dashes="$$skipped"; \ + fi; \ + report=""; \ + if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \ + report="Please report to $(PACKAGE_BUGREPORT)"; \ + test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \ + dashes="$$report"; \ + fi; \ + dashes=`echo "$$dashes" | sed s/./=/g`; \ + if test "$$failed" -eq 0; then \ + echo "$$grn$$dashes"; \ + else \ + echo "$$red$$dashes"; \ + fi; \ + echo "$$banner"; \ + test -z "$$skipped" || echo "$$skipped"; \ + test -z "$$report" || echo "$$report"; \ + echo "$$dashes$$std"; \ + test "$$failed" -eq 0; \ + else :; fi + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am + $(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS) + $(MAKE) $(AM_MAKEFLAGS) check-TESTS +check: check-am +all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) $(DATA) +install-binPROGRAMS: install-libLTLIBRARIES + +installdirs: + for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(bindir)" "$(DESTDIR)$(pkgcfgdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-binPROGRAMS clean-checkPROGRAMS clean-generic \ + clean-libLTLIBRARIES clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-pkgcfgDATA + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-binPROGRAMS install-libLTLIBRARIES + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-binPROGRAMS uninstall-libLTLIBRARIES \ + uninstall-pkgcfgDATA + +.MAKE: check-am install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-TESTS check-am clean \ + clean-binPROGRAMS clean-checkPROGRAMS clean-generic \ + clean-libLTLIBRARIES clean-libtool ctags distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-binPROGRAMS install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-libLTLIBRARIES install-man install-pdf \ + install-pdf-am install-pkgcfgDATA install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags uninstall uninstall-am \ + uninstall-binPROGRAMS uninstall-libLTLIBRARIES \ + uninstall-pkgcfgDATA + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/chat/chat.c b/src/chat/chat.c new file mode 100644 index 0000000..ae73b96 --- /dev/null +++ b/src/chat/chat.c @@ -0,0 +1,822 @@ +/* + This file is part of GNUnet. + (C) 2008, 2011 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 3, 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file chat/chat.c + * @brief convenience API for sending and receiving chat messages + * @author Christian Grothoff + * @author Nathan Evans + * @author Vitaly Minko + */ + +#include "platform.h" +#include "gnunet_constants.h" +#include "gnunet_protocols.h" +#include "gnunet_signatures.h" +#include "chat.h" + +#define DEBUG_CHAT GNUNET_EXTRA_LOGGING +#define NICK_IDENTITY_PREFIX ".chat_identity_" + + +/** + * Handle for a chat room. + */ +struct GNUNET_CHAT_Room +{ + struct GNUNET_CLIENT_Connection *client; + + const struct GNUNET_CONFIGURATION_Handle *cfg; + + struct GNUNET_CONTAINER_MetaData *member_info; + + char *room_name; + + struct GNUNET_CRYPTO_RsaPrivateKey *my_private_key; + + struct MemberList *members; + + int is_joined; + + GNUNET_CHAT_JoinCallback join_callback; + + void *join_callback_cls; + + GNUNET_CHAT_MessageCallback message_callback; + + void *message_callback_cls; + + GNUNET_CHAT_MemberListCallback member_list_callback; + + void *member_list_callback_cls; + + GNUNET_CHAT_MessageConfirmation confirmation_callback; + + void *confirmation_cls; + + uint32_t sequence_number; + + uint32_t msg_options; + +}; + +/** + * Linked list of members in the chat room. + */ +struct MemberList +{ + struct MemberList *next; + + /** + * Description of the member. + */ + struct GNUNET_CONTAINER_MetaData *meta; + + /** + * Member ID (pseudonym). + */ + GNUNET_HashCode id; + +}; + +/** + * Context for transmitting a send-message request. + */ +struct GNUNET_CHAT_SendMessageContext +{ + /** + * Handle for the chat room. + */ + struct GNUNET_CHAT_Room *chat_room; + + /** + * Message that we're sending. + */ + char *message; + + /** + * Options for the message. + */ + enum GNUNET_CHAT_MsgOptions options; + + /** + * Receiver of the message. NULL to send to everyone in the room. + */ + const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *receiver; + + /** + * Sequence id of the message. + */ + uint32_t sequence_number; + +}; + +/** + * Context for transmitting a confirmation receipt. + */ +struct GNUNET_CHAT_SendReceiptContext +{ + /** + * Handle for the chat room. + */ + struct GNUNET_CHAT_Room *chat_room; + + /** + * The original message that we're going to acknowledge. + */ + struct ReceiveNotificationMessage *received_msg; + +}; + +/** + * Ask client to send a join request. + */ +static int +rejoin_room (struct GNUNET_CHAT_Room *chat_room); + + +/** + * Transmit a confirmation receipt to the chat service. + * + * @param cls closure, pointer to the 'struct GNUNET_CHAT_SendReceiptContext' + * @param size number of bytes available in buf + * @param buf where the callee should write the message + * @return number of bytes written to buf + */ +static size_t +transmit_acknowledge_request (void *cls, size_t size, void *buf) +{ + struct GNUNET_CHAT_SendReceiptContext *src = cls; + struct ConfirmationReceiptMessage *receipt; + struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pub_key; + uint16_t msg_len; + size_t msg_size; + + if (NULL == buf) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Could not transmit confirmation receipt\n")); + return 0; + } +#if DEBUG_CHAT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Transmitting confirmation receipt to the service\n"); +#endif + msg_size = sizeof (struct ConfirmationReceiptMessage); + GNUNET_assert (size >= msg_size); + receipt = buf; + receipt->header.size = htons (msg_size); + receipt->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_CONFIRMATION_RECEIPT); + receipt->reserved = htonl (0); + receipt->sequence_number = src->received_msg->sequence_number; + receipt->reserved2 = htonl (0); + receipt->timestamp = GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get ()); + GNUNET_CRYPTO_rsa_key_get_public (src->chat_room->my_private_key, &pub_key); + GNUNET_CRYPTO_hash (&pub_key, + sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded), + &receipt->target); + receipt->author = src->received_msg->sender; + receipt->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_CHAT_RECEIPT); + receipt->purpose.size = + htonl (msg_size - sizeof (struct GNUNET_MessageHeader) - + sizeof (uint32_t) - sizeof (struct GNUNET_CRYPTO_RsaSignature)); + msg_len = + ntohs (src->received_msg->header.size) - + sizeof (struct ReceiveNotificationMessage); + GNUNET_CRYPTO_hash (&src->received_msg[1], msg_len, &receipt->content); + GNUNET_assert (GNUNET_OK == + GNUNET_CRYPTO_rsa_sign (src->chat_room->my_private_key, + &receipt->purpose, + &receipt->signature)); + GNUNET_free (src->received_msg); + GNUNET_free (src); + return msg_size; +} + + +/** + * Handles messages received from the service. Calls the proper client + * callback. + */ +static void +process_result (struct GNUNET_CHAT_Room *room, + const struct GNUNET_MessageHeader *reply) +{ + struct LeaveNotificationMessage *leave_msg; + struct JoinNotificationMessage *join_msg; + struct ReceiveNotificationMessage *received_msg; + struct ConfirmationReceiptMessage *receipt; + struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey; + GNUNET_HashCode id; + const GNUNET_HashCode *sender; + struct GNUNET_CONTAINER_MetaData *meta; + struct GNUNET_CHAT_SendReceiptContext *src; + struct MemberList *pos; + struct MemberList *prev; + struct GNUNET_CRYPTO_AesSessionKey key; + char decrypted_msg[MAX_MESSAGE_LENGTH]; + uint16_t size; + uint16_t meta_len; + uint16_t msg_len; + char *message_content; + + size = ntohs (reply->size); + switch (ntohs (reply->type)) + { + case GNUNET_MESSAGE_TYPE_CHAT_JOIN_NOTIFICATION: +#if DEBUG_CHAT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got a join notification\n"); +#endif + if (size < sizeof (struct JoinNotificationMessage)) + { + GNUNET_break (0); + return; + } + join_msg = (struct JoinNotificationMessage *) reply; + meta_len = size - sizeof (struct JoinNotificationMessage); + meta = + GNUNET_CONTAINER_meta_data_deserialize ((const char *) &join_msg[1], + meta_len); + if (NULL == meta) + { + GNUNET_break (0); + return; + } + pos = GNUNET_malloc (sizeof (struct MemberList)); + pos->meta = meta; + GNUNET_CRYPTO_hash (&join_msg->public_key, + sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded), + &pos->id); + GNUNET_PSEUDONYM_add (room->cfg, &pos->id, meta); + pos->next = room->members; + room->members = pos; + if (GNUNET_NO == room->is_joined) + { + GNUNET_CRYPTO_rsa_key_get_public (room->my_private_key, &pkey); + if (0 == + memcmp (&join_msg->public_key, &pkey, + sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded))) + { + room->join_callback (room->join_callback_cls); + room->is_joined = GNUNET_YES; + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("The current user must be the the first one joined\n")); + GNUNET_break (0); + return; + } + } + else + room->member_list_callback (room->member_list_callback_cls, meta, + &join_msg->public_key, + ntohl (join_msg->msg_options)); + break; + case GNUNET_MESSAGE_TYPE_CHAT_LEAVE_NOTIFICATION: +#if DEBUG_CHAT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got a leave notification\n"); +#endif + if (size < sizeof (struct LeaveNotificationMessage)) + { + GNUNET_break (0); + return; + } + leave_msg = (struct LeaveNotificationMessage *) reply; + room->member_list_callback (room->member_list_callback_cls, NULL, + &leave_msg->user, GNUNET_CHAT_MSG_OPTION_NONE); + GNUNET_CRYPTO_hash (&leave_msg->user, + sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded), + &id); + prev = NULL; + pos = room->members; + while ((NULL != pos) && + (0 != memcmp (&pos->id, &id, sizeof (GNUNET_HashCode)))) + { + prev = pos; + pos = pos->next; + } + GNUNET_assert (NULL != pos); + if (NULL == prev) + room->members = pos->next; + else + prev->next = pos->next; + GNUNET_CONTAINER_meta_data_destroy (pos->meta); + GNUNET_free (pos); + break; + case GNUNET_MESSAGE_TYPE_CHAT_MESSAGE_NOTIFICATION: +#if DEBUG_CHAT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got a message notification\n"); +#endif + if (size <= sizeof (struct ReceiveNotificationMessage)) + { + GNUNET_break (0); + return; + } + received_msg = (struct ReceiveNotificationMessage *) reply; + if (0 != (ntohl (received_msg->msg_options) & GNUNET_CHAT_MSG_ACKNOWLEDGED)) + { + src = GNUNET_malloc (sizeof (struct GNUNET_CHAT_SendReceiptContext)); + src->chat_room = room; + src->received_msg = GNUNET_memdup (received_msg, size); + GNUNET_CLIENT_notify_transmit_ready (room->client, + sizeof (struct + ConfirmationReceiptMessage), + GNUNET_CONSTANTS_SERVICE_TIMEOUT, + GNUNET_YES, + &transmit_acknowledge_request, src); + } + msg_len = size - sizeof (struct ReceiveNotificationMessage); + if (0 != (ntohl (received_msg->msg_options) & GNUNET_CHAT_MSG_PRIVATE)) + { + if (-1 == + GNUNET_CRYPTO_rsa_decrypt (room->my_private_key, + &received_msg->encrypted_key, &key, + sizeof (struct + GNUNET_CRYPTO_AesSessionKey))) + { + GNUNET_break (0); + return; + } + msg_len = + GNUNET_CRYPTO_aes_decrypt (&received_msg[1], msg_len, &key, + (const struct + GNUNET_CRYPTO_AesInitializationVector *) + INITVALUE, decrypted_msg); + message_content = decrypted_msg; + } + else + { + message_content = GNUNET_malloc (msg_len + 1); + memcpy (message_content, &received_msg[1], msg_len); + } + message_content[msg_len] = '\0'; + if (0 != (ntohl (received_msg->msg_options) & GNUNET_CHAT_MSG_ANONYMOUS)) + { + sender = NULL; + meta = NULL; + } + else + { + pos = room->members; + while ((NULL != pos) && + (0 != + memcmp (&pos->id, &received_msg->sender, + sizeof (GNUNET_HashCode)))) + pos = pos->next; + GNUNET_assert (NULL != pos); + sender = &received_msg->sender; + meta = pos->meta; + } + room->message_callback (room->message_callback_cls, room, sender, meta, + message_content, + GNUNET_TIME_absolute_ntoh (received_msg->timestamp), + ntohl (received_msg->msg_options)); + if (message_content != decrypted_msg) + GNUNET_free (message_content); + break; + case GNUNET_MESSAGE_TYPE_CHAT_CONFIRMATION_NOTIFICATION: +#if DEBUG_CHAT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got a confirmation receipt\n"); +#endif + if (size < sizeof (struct ConfirmationReceiptMessage)) + { + GNUNET_break (0); + return; + } + receipt = (struct ConfirmationReceiptMessage *) reply; + if (NULL != room->confirmation_callback) + room->confirmation_callback (room->confirmation_cls, room, + ntohl (receipt->sequence_number), + GNUNET_TIME_absolute_ntoh + (receipt->timestamp), &receipt->target); + break; + default: + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Unknown message type: '%u'\n"), + ntohs (reply->type)); + GNUNET_break_op (0); + break; + } +} + + +/** + * Listen for incoming messages on this chat room. Also, support servers going + * away/coming back (i.e. rejoin chat room to keep server state up to date). + * + * @param cls closure, pointer to the 'struct GNUNET_CHAT_Room' + * @param msg message received, NULL on timeout or fatal error + */ +static void +receive_results (void *cls, const struct GNUNET_MessageHeader *msg) +{ + struct GNUNET_CHAT_Room *chat_room = cls; + +#if DEBUG_CHAT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got a message from the service\n"); +#endif + if (0 != (GNUNET_SCHEDULER_REASON_SHUTDOWN & GNUNET_SCHEDULER_get_reason ())) + return; + if (NULL == msg) + { + GNUNET_break (0); + rejoin_room (chat_room); + return; + } + process_result (chat_room, msg); + if (NULL == chat_room->client) + return; /* fatal error */ + /* continue receiving */ + GNUNET_CLIENT_receive (chat_room->client, &receive_results, chat_room, + GNUNET_TIME_UNIT_FOREVER_REL); +} + + +/** + * Read existing private key from file or create a new one if it does not exist + * yet. + * Returns the private key on success, NULL on error. + */ +static struct GNUNET_CRYPTO_RsaPrivateKey * +init_private_key (const struct GNUNET_CONFIGURATION_Handle *cfg, + const char *nick_name) +{ + char *home; + char *keyfile; + struct GNUNET_CRYPTO_RsaPrivateKey *privKey; + +#if DEBUG_CHAT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Initializing private key\n"); +#endif + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_filename (cfg, "chat", "HOME", &home)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Configuration option `%s' in section `%s' missing\n"), + "HOME", "chat"); + return NULL; + } + GNUNET_DISK_directory_create (home); + if (GNUNET_OK != GNUNET_DISK_directory_test (home)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Failed to access chat home directory `%s'\n"), home); + GNUNET_free (home); + return NULL; + } + /* read or create private key */ + keyfile = + GNUNET_malloc (strlen (home) + strlen (NICK_IDENTITY_PREFIX) + + strlen (nick_name) + 2); + strcpy (keyfile, home); + GNUNET_free (home); + if (keyfile[strlen (keyfile) - 1] != DIR_SEPARATOR) + strcat (keyfile, DIR_SEPARATOR_STR); + strcat (keyfile, NICK_IDENTITY_PREFIX); + strcat (keyfile, nick_name); + privKey = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile); + if (NULL == privKey) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Failed to create/open key in file `%s'\n"), keyfile); + } + GNUNET_free (keyfile); + return privKey; +} + + +/** + * Transmit a join request to the chat service. + * + * @param cls closure, pointer to the 'struct GNUNET_CHAT_Room' + * @param size number of bytes available in buf + * @param buf where the callee should write the message + * @return number of bytes written to buf + */ +static size_t +transmit_join_request (void *cls, size_t size, void *buf) +{ + struct GNUNET_CHAT_Room *chat_room = cls; + struct JoinRequestMessage *join_msg; + char *room; + char *meta; + size_t room_len; + ssize_t meta_len; + size_t size_of_join; + + if (NULL == buf) + { +#if DEBUG_CHAT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Could not transmit join request, retrying...\n"); +#endif + rejoin_room (chat_room); + return 0; + } +#if DEBUG_CHAT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Transmitting join request to the service\n"); +#endif + room_len = strlen (chat_room->room_name); + meta_len = + GNUNET_CONTAINER_meta_data_get_serialized_size (chat_room->member_info); + size_of_join = sizeof (struct JoinRequestMessage) + meta_len + room_len; + GNUNET_assert (size >= size_of_join); + join_msg = buf; + join_msg->header.size = htons (size); + join_msg->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_JOIN_REQUEST); + join_msg->msg_options = htonl (chat_room->msg_options); + join_msg->room_name_len = htons (room_len); + join_msg->reserved = htons (0); + join_msg->reserved2 = htonl (0); + GNUNET_CRYPTO_rsa_key_get_public (chat_room->my_private_key, + &join_msg->public_key); + room = (char *) &join_msg[1]; + memcpy (room, chat_room->room_name, room_len); + meta = &room[room_len]; + if (GNUNET_SYSERR == + GNUNET_CONTAINER_meta_data_serialize (chat_room->member_info, &meta, + meta_len, + GNUNET_CONTAINER_META_DATA_SERIALIZE_FULL)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Could not serialize metadata\n")); + return 0; + } + GNUNET_CLIENT_receive (chat_room->client, &receive_results, chat_room, + GNUNET_TIME_UNIT_FOREVER_REL); + return size_of_join; +} + + +/** + * Ask to send a join request. + */ +static int +rejoin_room (struct GNUNET_CHAT_Room *chat_room) +{ + size_t size_of_join; + + size_of_join = + sizeof (struct JoinRequestMessage) + + GNUNET_CONTAINER_meta_data_get_serialized_size (chat_room->member_info) + + strlen (chat_room->room_name); + if (NULL == + GNUNET_CLIENT_notify_transmit_ready (chat_room->client, size_of_join, + GNUNET_CONSTANTS_SERVICE_TIMEOUT, + GNUNET_YES, &transmit_join_request, + chat_room)) + return GNUNET_SYSERR; + return GNUNET_OK; +} + + +/** + * Leave a chat room. + */ +void +GNUNET_CHAT_leave_room (struct GNUNET_CHAT_Room *chat_room) +{ + struct MemberList *pos; + +#if DEBUG_CHAT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Leaving the room '%s'\n", + chat_room->room_name); +#endif + GNUNET_CLIENT_disconnect (chat_room->client, GNUNET_NO); + GNUNET_free (chat_room->room_name); + GNUNET_CONTAINER_meta_data_destroy (chat_room->member_info); + GNUNET_CRYPTO_rsa_key_free (chat_room->my_private_key); + while (NULL != chat_room->members) + { + pos = chat_room->members; + chat_room->members = pos->next; + GNUNET_CONTAINER_meta_data_destroy (pos->meta); + GNUNET_free (pos); + } + GNUNET_free (chat_room); +} + + +/** + * Join a chat room. + * + * @param cfg configuration + * @param nick_name nickname of the user joining (used to + * determine which public key to use); + * the nickname should probably also + * be used in the member_info (as "EXTRACTOR_TITLE") + * @param member_info information about the joining member + * @param room_name name of the room + * @param msg_options message options of the joining user + * @param joinCallback function to call on successful join + * @param join_cls closure for joinCallback + * @param messageCallback which function to call if a message has + * been received? + * @param message_cls argument to callback + * @param memberCallback which function to call for join/leave notifications + * @param member_cls argument to callback + * @param confirmationCallback which function to call for confirmations (maybe NULL) + * @param confirmation_cls argument to callback + * @param me member ID (pseudonym) + * @return NULL on error + */ +struct GNUNET_CHAT_Room * +GNUNET_CHAT_join_room (const struct GNUNET_CONFIGURATION_Handle *cfg, + const char *nick_name, + struct GNUNET_CONTAINER_MetaData *member_info, + const char *room_name, + enum GNUNET_CHAT_MsgOptions msg_options, + GNUNET_CHAT_JoinCallback joinCallback, void *join_cls, + GNUNET_CHAT_MessageCallback messageCallback, + void *message_cls, + GNUNET_CHAT_MemberListCallback memberCallback, + void *member_cls, + GNUNET_CHAT_MessageConfirmation confirmationCallback, + void *confirmation_cls, GNUNET_HashCode * me) +{ + struct GNUNET_CHAT_Room *chat_room; + struct GNUNET_CRYPTO_RsaPrivateKey *priv_key; + struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pub_key; + struct GNUNET_CLIENT_Connection *client; + +#if DEBUG_CHAT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Joining the room '%s'\n", room_name); +#endif + priv_key = init_private_key (cfg, nick_name); + if (NULL == priv_key) + return NULL; + GNUNET_CRYPTO_rsa_key_get_public (priv_key, &pub_key); + GNUNET_CRYPTO_hash (&pub_key, + sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded), + me); + GNUNET_PSEUDONYM_add (cfg, me, member_info); + client = GNUNET_CLIENT_connect ("chat", cfg); + if (NULL == client) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Failed to connect to the chat service\n")); + return NULL; + } + if (NULL == joinCallback) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Undefined mandatory parameter: joinCallback\n")); + return NULL; + } + if (NULL == messageCallback) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Undefined mandatory parameter: messageCallback\n")); + return NULL; + } + if (NULL == memberCallback) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Undefined mandatory parameter: memberCallback\n")); + return NULL; + } + chat_room = GNUNET_malloc (sizeof (struct GNUNET_CHAT_Room)); + chat_room->msg_options = msg_options; + chat_room->room_name = GNUNET_strdup (room_name); + chat_room->member_info = GNUNET_CONTAINER_meta_data_duplicate (member_info); + chat_room->my_private_key = priv_key; + chat_room->is_joined = GNUNET_NO; + chat_room->join_callback = joinCallback; + chat_room->join_callback_cls = join_cls; + chat_room->message_callback = messageCallback; + chat_room->message_callback_cls = message_cls; + chat_room->member_list_callback = memberCallback; + chat_room->member_list_callback_cls = member_cls; + chat_room->confirmation_callback = confirmationCallback; + chat_room->confirmation_cls = confirmation_cls; + chat_room->cfg = cfg; + chat_room->client = client; + chat_room->members = NULL; + if (GNUNET_SYSERR == rejoin_room (chat_room)) + { + GNUNET_CHAT_leave_room (chat_room); + return NULL; + } + return chat_room; +} + + +/** + * Transmit a send-message request to the chat service. + * + * @param cls closure, pointer to the 'struct GNUNET_CHAT_SendMessageContext' + * @param size number of bytes available in buf + * @param buf where the callee should write the message + * @return number of bytes written to buf + */ +static size_t +transmit_send_request (void *cls, size_t size, void *buf) +{ + struct GNUNET_CHAT_SendMessageContext *smc = cls; + struct TransmitRequestMessage *msg_to_send; + size_t msg_size; + + if (NULL == buf) + { +#if DEBUG_CHAT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Could not transmit a chat message\n"); +#endif + return 0; + } +#if DEBUG_CHAT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Transmitting a chat message to the service\n"); +#endif + msg_size = strlen (smc->message) + sizeof (struct TransmitRequestMessage); + GNUNET_assert (size >= msg_size); + msg_to_send = buf; + msg_to_send->header.size = htons (msg_size); + msg_to_send->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_TRANSMIT_REQUEST); + msg_to_send->msg_options = htonl (smc->options); + msg_to_send->sequence_number = htonl (smc->sequence_number); + msg_to_send->timestamp = + GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get ()); + msg_to_send->reserved = htonl (0); + if (NULL == smc->receiver) + memset (&msg_to_send->target, 0, sizeof (GNUNET_HashCode)); + else + GNUNET_CRYPTO_hash (smc->receiver, + sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded), + &msg_to_send->target); + memcpy (&msg_to_send[1], smc->message, strlen (smc->message)); + /** + * Client don't encode private messages since public keys of other members are + * stored on the service side. + */ + if (smc->options & GNUNET_CHAT_MSG_AUTHENTICATED) + { + msg_to_send->purpose.purpose = + htonl (GNUNET_SIGNATURE_PURPOSE_CHAT_MESSAGE); + msg_to_send->purpose.size = + htonl (msg_size - sizeof (struct GNUNET_MessageHeader) - + sizeof (struct GNUNET_CRYPTO_RsaSignature)); + GNUNET_assert (GNUNET_OK == + GNUNET_CRYPTO_rsa_sign (smc->chat_room->my_private_key, + &msg_to_send->purpose, + &msg_to_send->signature)); + } + GNUNET_free (smc->message); + GNUNET_free (smc); + return msg_size; +} + + +/** + * Send a message. + * + * @param room handle for the chat room + * @param message message to be sent + * @param options options for the message + * @param receiver use NULL to send to everyone in the room + * @param sequence_number where to write the sequence id of the message + */ +void +GNUNET_CHAT_send_message (struct GNUNET_CHAT_Room *room, const char *message, + enum GNUNET_CHAT_MsgOptions options, + const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded + *receiver, uint32_t * sequence_number) +{ + size_t msg_size; + struct GNUNET_CHAT_SendMessageContext *smc; + +#if DEBUG_CHAT + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending a message\n"); +#endif + room->sequence_number++; + if (NULL != sequence_number) + *sequence_number = room->sequence_number; + smc = GNUNET_malloc (sizeof (struct GNUNET_CHAT_SendMessageContext)); + smc->chat_room = room; + smc->message = GNUNET_strdup (message); + smc->options = options; + smc->receiver = receiver; + smc->sequence_number = room->sequence_number; + msg_size = strlen (message) + sizeof (struct TransmitRequestMessage); + GNUNET_CLIENT_notify_transmit_ready (room->client, msg_size, + GNUNET_CONSTANTS_SERVICE_TIMEOUT, + GNUNET_YES, &transmit_send_request, smc); +} + +/* end of chat.c */ diff --git a/src/chat/chat.conf.in b/src/chat/chat.conf.in new file mode 100644 index 0000000..41fe9b4 --- /dev/null +++ b/src/chat/chat.conf.in @@ -0,0 +1,22 @@ +[chat] +AUTOSTART = YES +@UNIXONLY@ PORT = 2090 +HOSTNAME = localhost +HOME = $SERVICEHOME +CONFIG = $DEFAULTCONFIG +BINARY = gnunet-service-chat +ACCEPT_FROM = 127.0.0.1; +ACCEPT_FROM6 = ::1; +UNIXPATH = /tmp/gnunet-service-chat.sock +UNIX_MATCH_UID = NO +UNIX_MATCH_GID = YES +# DISABLE_SOCKET_FORWARDING = NO +# USERNAME = +# MAXBUF = +# TIMEOUT = +# DISABLEV6 = +# BINDTO = +# REJECT_FROM = +# REJECT_FROM6 = +# PREFIX = + diff --git a/src/chat/chat.h b/src/chat/chat.h new file mode 100644 index 0000000..5df7773 --- /dev/null +++ b/src/chat/chat.h @@ -0,0 +1,485 @@ +/* + This file is part of GNUnet + (C) 2008, 2011 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file chat/chat.h + * @brief support for chat + * @author Christian Grothoff + * @author Nathan Evans + * @author Vitaly Minko + */ + +#ifndef CHAT_H +#define CHAT_H + +#include "gnunet_chat_service.h" + +/** + * Constant IV since we generate a new session key per each message. + */ +#define INITVALUE "InitializationVectorValue" + + +/** + * Client-service messages + */ + +GNUNET_NETWORK_STRUCT_BEGIN + +/** + * Notification sent by service to client indicating that we've received a chat + * message. After this struct, the remaining bytes are the actual text message. + * If the mesasge is private, then the text is encrypted, otherwise it's + * plaintext. + */ +struct ReceiveNotificationMessage +{ + /** + * Message type will be GNUNET_MESSAGE_TYPE_CHAT_MESSAGE_NOTIFICATION + */ + struct GNUNET_MessageHeader header; + + /** + * Message options, see GNUNET_CHAT_MsgOptions. + */ + uint32_t msg_options GNUNET_PACKED; + + /** + * Sequence number of the message (unique per sender). + */ + uint32_t sequence_number GNUNET_PACKED; + + /** + * For alignment (should be zero). + */ + uint32_t reserved GNUNET_PACKED; + + /** + * Timestamp of the message. + */ + struct GNUNET_TIME_AbsoluteNBO timestamp; + + /** + * Hash of the public key of the pseudonym of the sender of the message. + * Should be all zeros for anonymous. + */ + GNUNET_HashCode sender; + + /** + * The encrypted session key. + */ + struct GNUNET_CRYPTO_RsaEncryptedData encrypted_key; + +}; + + +/** + * Request sent by client to transmit a chat message to another room members. + * After this struct, the remaining bytes are the actual message in plaintext. + * Private messages are encrypted on the service side. + */ +struct TransmitRequestMessage +{ + /** + * Message type will be GNUNET_MESSAGE_TYPE_CHAT_TRANSMIT_REQUEST + */ + struct GNUNET_MessageHeader header; + + /** + * For alignment (should be zero). + */ + uint32_t reserved GNUNET_PACKED; + + /** + * Signature confirming receipt. Signature covers everything from header + * through content. + */ + struct GNUNET_CRYPTO_RsaSignature signature; + + /** + * What is being signed and why? + */ + struct GNUNET_CRYPTO_RsaSignaturePurpose purpose; + + /** + * Desired message options, see GNUNET_CHAT_MsgOptions. + */ + uint32_t msg_options GNUNET_PACKED; + + /** + * Sequence number of the message (unique per sender). + */ + uint32_t sequence_number GNUNET_PACKED; + + /** + * Timestamp of the message. + */ + struct GNUNET_TIME_AbsoluteNBO timestamp; + + /** + * Who should receive this message? Set to all zeros for "everyone". + */ + GNUNET_HashCode target; + +}; + + +/** + * Receipt sent from a message receiver to the service to confirm delivery of + * a chat message and from the service to sender of the original message to + * acknowledge delivery. + */ +struct ConfirmationReceiptMessage +{ + /** + * Message type will be + * GNUNET_MESSAGE_TYPE_CHAT_CONFIRMATION_RECEIPT when sending from client, + * GNUNET_MESSAGE_TYPE_CHAT_CONFIRMATION_NOTIFICATION when sending to client. + */ + struct GNUNET_MessageHeader header; + + /** + * For alignment (should be zero). + */ + uint32_t reserved GNUNET_PACKED; + + /** + * Signature confirming receipt. Signature covers everything from header + * through content. + */ + struct GNUNET_CRYPTO_RsaSignature signature; + + /** + * What is being signed and why? + */ + struct GNUNET_CRYPTO_RsaSignaturePurpose purpose; + + /** + * Sequence number of the original message. + */ + uint32_t sequence_number GNUNET_PACKED; + + /** + * For alignment (should be zero). + */ + uint32_t reserved2 GNUNET_PACKED; + + /** + * Time of receipt. + */ + struct GNUNET_TIME_AbsoluteNBO timestamp; + + /** + * Who is confirming the receipt? + */ + GNUNET_HashCode target; + + /** + * Who is the author of the chat message? + */ + GNUNET_HashCode author; + + /** + * Hash of the (possibly encrypted) content. + */ + GNUNET_HashCode content; + +}; + + +/** + * Message send from client to daemon to join a chat room. + * This struct is followed by the room name and then + * the serialized ECRS meta data describing the new member. + */ +struct JoinRequestMessage +{ + /** + * Message type will be GNUNET_MESSAGE_TYPE_CHAT_JOIN_REQUEST + */ + struct GNUNET_MessageHeader header; + + /** + * Options. Set all options that this client is willing to receive. + * For example, if the client does not want to receive anonymous or + * OTR messages but is willing to generate acknowledgements and + * receive private messages, this should be set to + * GNUNET_CHAT_MSG_PRIVATE | GNUNET_CHAT_MSG_ACKNOWLEDGED. + */ + uint32_t msg_options GNUNET_PACKED; + + /** + * Length of the room name. + */ + uint16_t room_name_len GNUNET_PACKED; + + /** + * For alignment (should be zero). + */ + uint16_t reserved GNUNET_PACKED; + uint32_t reserved2 GNUNET_PACKED; + + /** + * Public key of the joining member. + */ + struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded public_key; + +}; + + +/** + * Message send by server to client to indicate joining of another room member. + * This struct is followed by the serialized ECRS MetaData describing the new + * member. + */ +struct JoinNotificationMessage +{ + /** + * Message type will be GNUNET_MESSAGE_TYPE_CHAT_JOIN_NOTIFICATION + */ + struct GNUNET_MessageHeader header; + + /** + * Options. Set to all options that the new user is willing to + * process. For example, if the client does not want to receive + * anonymous or OTR messages but is willing to generate + * acknowledgements and receive private messages, this should be set + * to GNUNET_CHAT_MSG_PRIVATE | GNUNET_CHAT_MSG_ACKNOWLEDGED. + */ + uint32_t msg_options GNUNET_PACKED; + + /** + * Public key of the new user. + */ + struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded public_key; + +}; + + +/** + * Message send by server to client to indicate leaving of another room member. + */ +struct LeaveNotificationMessage +{ + /** + * Message type will be GNUNET_MESSAGE_TYPE_CHAT_LEAVE_NOTIFICATION + */ + struct GNUNET_MessageHeader header; + + /** + * Reserved (for alignment). + */ + uint32_t reserved GNUNET_PACKED; + + /** + * Who is leaving? + */ + struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded user; + +}; + + +/** + * Peer-to-peer messages + */ + +/** + * Message send by one peer to another to indicate joining of another room + * member. This struct is followed by the room name and then the serialized + * ECRS MetaData describing the new member. + */ +struct P2PJoinNotificationMessage +{ + /** + * Message type will be GNUNET_MESSAGE_TYPE_CHAT_P2P_JOIN_NOTIFICATION + */ + struct GNUNET_MessageHeader header; + + /** + * Options. Set all options that this client is willing to receive. + * For example, if the client does not want to receive anonymous or + * OTR messages but is willing to generate acknowledgements and + * receive private messages, this should be set to + * GNUNET_CHAT_MSG_PRIVATE | GNUNET_CHAT_MSG_ACKNOWLEDGED. + */ + uint32_t msg_options GNUNET_PACKED; + + /** + * Length of the room name. + */ + uint16_t room_name_len GNUNET_PACKED; + + /** + * Reserved (should be zero). + */ + uint16_t reserved GNUNET_PACKED; + uint32_t reserved2 GNUNET_PACKED; + + /** + * Public key of the joining member. + */ + struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded public_key; + +}; + + +/** + * Message send by one peer to another to indicate leaving of another room + * member. + */ +struct P2PLeaveNotificationMessage +{ + /** + * Message type will be GNUNET_MESSAGE_TYPE_CHAT_P2P_LEAVE_NOTIFICATION + */ + struct GNUNET_MessageHeader header; + + /** + * Reserved (for alignment). + */ + uint32_t reserved GNUNET_PACKED; + + /** + * Who is leaving? + */ + struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded user; + +}; + + +/** + * Message send by one peer to another to indicate receiving of a chat message. + * This struct is followed by the room name (only if the message is anonymous) + * and then the remaining bytes are the actual text message. If the mesasge is + * private, then the text is encrypted, otherwise it's plaintext. + */ +struct P2PReceiveNotificationMessage +{ + /** + * Message type will be GNUNET_MESSAGE_TYPE_CHAT_P2P_MESSAGE_NOTIFICATION + */ + struct GNUNET_MessageHeader header; + + /** + * Message options, see GNUNET_CHAT_MsgOptions. + */ + uint32_t msg_options GNUNET_PACKED; + + /** + * Sequence number of the message (unique per sender). + */ + uint32_t sequence_number GNUNET_PACKED; + + /** + * Length of the room name. This is only used for anonymous messages. + */ + uint16_t room_name_len GNUNET_PACKED; + + /** + * Reserved (for alignment). + */ + uint16_t reserved GNUNET_PACKED; + + /** + * Timestamp of the message. + */ + struct GNUNET_TIME_AbsoluteNBO timestamp; + + /** + * Hash of the public key of the pseudonym of the sender of the message + * Should be all zeros for anonymous. + */ + GNUNET_HashCode sender; + + /** + * Who should receive this message? Set to all zeros for "everyone". + */ + GNUNET_HashCode target; + + /** + * The encrypted session key. + */ + struct GNUNET_CRYPTO_RsaEncryptedData encrypted_key; + +}; + + +/** + * Receipt sent from one peer to another to confirm delivery of a chat message. + */ +struct P2PConfirmationReceiptMessage +{ + /** + * Message type will be GNUNET_MESSAGE_TYPE_CHAT_P2P_CONFIRMATION_RECEIPT + */ + struct GNUNET_MessageHeader header; + + /** + * For alignment (should be zero). + */ + uint32_t reserved GNUNET_PACKED; + + /** + * Signature confirming receipt. Signature covers everything from header + * through content. + */ + struct GNUNET_CRYPTO_RsaSignature signature; + + /** + * What is being signed and why? + */ + struct GNUNET_CRYPTO_RsaSignaturePurpose purpose; + + /** + * Sequence number of the original message. + */ + uint32_t msg_sequence_number GNUNET_PACKED; + + /** + * Sequence number of the receipt. + */ + uint32_t sequence_number GNUNET_PACKED; + + /** + * Time of receipt. + */ + struct GNUNET_TIME_AbsoluteNBO timestamp; + + /** + * Who is confirming the receipt? + */ + GNUNET_HashCode target; + + /** + * Who is the author of the chat message? + */ + GNUNET_HashCode author; + + /** + * Hash of the (possibly encrypted) content. + */ + GNUNET_HashCode content; + +}; +GNUNET_NETWORK_STRUCT_END + +#endif + +/* end of chat.h */ diff --git a/src/chat/gnunet-chat.c b/src/chat/gnunet-chat.c new file mode 100644 index 0000000..4abc58c --- /dev/null +++ b/src/chat/gnunet-chat.c @@ -0,0 +1,674 @@ +/* + This file is part of GNUnet. + (C) 2007, 2008, 2011 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 3, 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file chat/gnunet-chat.c + * @brief Minimal chat command line tool + * @author Christian Grothoff + * @author Nathan Evans + * @author Vitaly Minko + */ + +#include "platform.h" +#include "gnunet_getopt_lib.h" +#include "gnunet_program_lib.h" +#include "gnunet_chat_service.h" +#include <fcntl.h> + +static int ret; + +static const struct GNUNET_CONFIGURATION_Handle *cfg; + +static char *nickname; + +static char *room_name; + +static struct GNUNET_CONTAINER_MetaData *meta; + +static struct GNUNET_CHAT_Room *room; + +static GNUNET_SCHEDULER_TaskIdentifier handle_cmd_task = + GNUNET_SCHEDULER_NO_TASK; + +struct ChatCommand +{ + const char *command; + int (*Action) (const char *arguments, const void *xtra); + const char *helptext; +}; + +struct UserList +{ + struct UserList *next; + struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey; + int ignored; +}; + +static struct UserList *users; + +static void +free_user_list () +{ + struct UserList *next; + + while (NULL != users) + { + next = users->next; + GNUNET_free (users); + users = next; + } +} + +static int +do_help (const char *args, const void *xtra); + + +/** + * Callback used for notification that we have joined the room. + * + * @param cls closure + * @return GNUNET_OK + */ +static int +join_cb (void *cls) +{ + FPRINTF (stdout, "%s", _("Joined\n")); + return GNUNET_OK; +} + + +/** + * Callback used for notification about incoming messages. + * + * @param cls closure, NULL + * @param room in which room was the message received? + * @param sender what is the ID of the sender? (maybe NULL) + * @param member_info information about the joining member + * @param message the message text + * @param timestamp time when the member joined + * @param options options for the message + * @return GNUNET_OK to accept the message now, GNUNET_NO to + * accept (but user is away), GNUNET_SYSERR to signal denied delivery + */ +static int +receive_cb (void *cls, struct GNUNET_CHAT_Room *room, + const GNUNET_HashCode * sender, + const struct GNUNET_CONTAINER_MetaData *member_info, + const char *message, struct GNUNET_TIME_Absolute timestamp, + enum GNUNET_CHAT_MsgOptions options) +{ + char *nick; + char *time; + const char *fmt; + + if (NULL != sender) + nick = GNUNET_PSEUDONYM_id_to_name (cfg, sender); + else + nick = GNUNET_strdup (_("anonymous")); + fmt = NULL; + switch ((int) options) + { + case GNUNET_CHAT_MSG_OPTION_NONE: + case GNUNET_CHAT_MSG_ANONYMOUS: + fmt = _("(%s) `%s' said: %s\n"); + break; + case GNUNET_CHAT_MSG_PRIVATE: + fmt = _("(%s) `%s' said to you: %s\n"); + break; + case GNUNET_CHAT_MSG_PRIVATE | GNUNET_CHAT_MSG_ANONYMOUS: + fmt = _("(%s) `%s' said to you: %s\n"); + break; + case GNUNET_CHAT_MSG_AUTHENTICATED: + fmt = _("(%s) `%s' said for sure: %s\n"); + break; + case GNUNET_CHAT_MSG_PRIVATE | GNUNET_CHAT_MSG_AUTHENTICATED: + fmt = _("(%s) `%s' said to you for sure: %s\n"); + break; + case GNUNET_CHAT_MSG_ACKNOWLEDGED: + fmt = _("(%s) `%s' was confirmed that you received: %s\n"); + break; + case GNUNET_CHAT_MSG_PRIVATE | GNUNET_CHAT_MSG_ACKNOWLEDGED: + fmt = _("(%s) `%s' was confirmed that you and only you received: %s\n"); + break; + case GNUNET_CHAT_MSG_AUTHENTICATED | GNUNET_CHAT_MSG_ACKNOWLEDGED: + fmt = _("(%s) `%s' was confirmed that you received from him or her: %s\n"); + break; + case GNUNET_CHAT_MSG_AUTHENTICATED | GNUNET_CHAT_MSG_PRIVATE | GNUNET_CHAT_MSG_ACKNOWLEDGED: + fmt = + _ + ("(%s) `%s' was confirmed that you and only you received from him or her: %s\n"); + break; + case GNUNET_CHAT_MSG_OFF_THE_RECORD: + fmt = _("(%s) `%s' said off the record: %s\n"); + break; + default: + fmt = _("(%s) <%s> said using an unknown message type: %s\n"); + break; + } + time = GNUNET_STRINGS_absolute_time_to_string (timestamp); + FPRINTF (stdout, fmt, time, nick, message); + GNUNET_free (nick); + GNUNET_free (time); + return GNUNET_OK; +} + + +/** + * Callback used for message delivery confirmations. + * + * @param cls closure, NULL + * @param room in which room was the message received? + * @param orig_seq_number sequence number of the original message + * @param timestamp when was the message received? + * @param receiver who is confirming the receipt? + * @return GNUNET_OK to continue, GNUNET_SYSERR to refuse processing further + * confirmations from anyone for this message + */ +static int +confirmation_cb (void *cls, struct GNUNET_CHAT_Room *room, + uint32_t orig_seq_number, + struct GNUNET_TIME_Absolute timestamp, + const GNUNET_HashCode * receiver) +{ + char *nick; + + nick = GNUNET_PSEUDONYM_id_to_name (cfg, receiver); + FPRINTF (stdout, _("'%s' acknowledged message #%d\n"), nick, orig_seq_number); + return GNUNET_OK; +} + + +/** + * Callback used for notification that another room member has joined or left. + * + * @param cls closure (not used) + * @param member_info will be non-null if the member is joining, NULL if he is + * leaving + * @param member_id hash of public key of the user (for unique identification) + * @param options what types of messages is this member willing to receive? + * @return GNUNET_OK + */ +static int +member_list_cb (void *cls, const struct GNUNET_CONTAINER_MetaData *member_info, + const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *member_id, + enum GNUNET_CHAT_MsgOptions options) +{ + char *nick; + GNUNET_HashCode id; + struct UserList *pos; + struct UserList *prev; + + GNUNET_CRYPTO_hash (member_id, + sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded), + &id); + nick = GNUNET_PSEUDONYM_id_to_name (cfg, &id); + FPRINTF (stdout, + member_info != + NULL ? _("`%s' entered the room\n") : _("`%s' left the room\n"), + nick); + GNUNET_free (nick); + if (NULL != member_info) + { + /* user joining */ + pos = GNUNET_malloc (sizeof (struct UserList)); + pos->next = users; + pos->pkey = *member_id; + pos->ignored = GNUNET_NO; + users = pos; + } + else + { + /* user leaving */ + prev = NULL; + pos = users; + while ((NULL != pos) && + (0 != + memcmp (&pos->pkey, member_id, + sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded)))) + { + prev = pos; + pos = pos->next; + } + if (NULL == pos) + { + GNUNET_break (0); + } + else + { + if (NULL == prev) + users = pos->next; + else + prev->next = pos->next; + GNUNET_free (pos); + } + } + return GNUNET_OK; +} + + +static int +do_join (const char *arg, const void *xtra) +{ + char *my_name; + GNUNET_HashCode me; + + if (arg[0] == '#') + arg++; /* ignore first hash */ + GNUNET_CHAT_leave_room (room); + free_user_list (); + GNUNET_free (room_name); + room_name = GNUNET_strdup (arg); + room = + GNUNET_CHAT_join_room (cfg, nickname, meta, room_name, -1, &join_cb, NULL, + &receive_cb, NULL, &member_list_cb, NULL, + &confirmation_cb, NULL, &me); + if (NULL == room) + { + FPRINTF (stdout, "%s", _("Could not change username\n")); + return GNUNET_SYSERR; + } + my_name = GNUNET_PSEUDONYM_id_to_name (cfg, &me); + FPRINTF (stdout, _("Joining room `%s' as user `%s'...\n"), room_name, + my_name); + GNUNET_free (my_name); + return GNUNET_OK; +} + + +static int +do_nick (const char *msg, const void *xtra) +{ + char *my_name; + GNUNET_HashCode me; + + GNUNET_CHAT_leave_room (room); + free_user_list (); + GNUNET_free (nickname); + GNUNET_CONTAINER_meta_data_destroy (meta); + nickname = GNUNET_strdup (msg); + meta = GNUNET_CONTAINER_meta_data_create (); + GNUNET_CONTAINER_meta_data_insert (meta, "<gnunet>", EXTRACTOR_METATYPE_TITLE, + EXTRACTOR_METAFORMAT_UTF8, "text/plain", + nickname, strlen (nickname) + 1); + room = + GNUNET_CHAT_join_room (cfg, nickname, meta, room_name, -1, &join_cb, NULL, + &receive_cb, NULL, &member_list_cb, NULL, + &confirmation_cb, NULL, &me); + if (NULL == room) + { + FPRINTF (stdout, "%s", _("Could not change username\n")); + return GNUNET_SYSERR; + } + my_name = GNUNET_PSEUDONYM_id_to_name (cfg, &me); + FPRINTF (stdout, _("Changed username to `%s'\n"), my_name); + GNUNET_free (my_name); + return GNUNET_OK; +} + + +static int +do_names (const char *msg, const void *xtra) +{ + char *name; + struct UserList *pos; + GNUNET_HashCode pid; + + FPRINTF (stdout, _("Users in room `%s': "), room_name); + pos = users; + while (NULL != pos) + { + GNUNET_CRYPTO_hash (&pos->pkey, + sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded), + &pid); + name = GNUNET_PSEUDONYM_id_to_name (cfg, &pid); + FPRINTF (stdout, "`%s' ", name); + GNUNET_free (name); + pos = pos->next; + } + FPRINTF (stdout, "%s", "\n"); + return GNUNET_OK; +} + + +static int +do_send (const char *msg, const void *xtra) +{ + uint32_t seq; + + GNUNET_CHAT_send_message (room, msg, GNUNET_CHAT_MSG_OPTION_NONE, NULL, &seq); + return GNUNET_OK; +} + + +static int +do_send_pm (const char *msg, const void *xtra) +{ + char *user; + GNUNET_HashCode uid; + GNUNET_HashCode pid; + uint32_t seq; + struct UserList *pos; + + if (NULL == strstr (msg, " ")) + { + FPRINTF (stderr, "%s", _("Syntax: /msg USERNAME MESSAGE")); + return GNUNET_OK; + } + user = GNUNET_strdup (msg); + strstr (user, " ")[0] = '\0'; + msg += strlen (user) + 1; + if (GNUNET_OK != GNUNET_PSEUDONYM_name_to_id (cfg, user, &uid)) + { + FPRINTF (stderr, _("Unknown user `%s'\n"), user); + GNUNET_free (user); + return GNUNET_OK; + } + pos = users; + while (NULL != pos) + { + GNUNET_CRYPTO_hash (&pos->pkey, + sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded), + &pid); + if (0 == memcmp (&pid, &uid, sizeof (GNUNET_HashCode))) + break; + pos = pos->next; + } + if (NULL == pos) + { + FPRINTF (stderr, _("User `%s' is currently not in the room!\n"), user); + GNUNET_free (user); + return GNUNET_OK; + } + GNUNET_CHAT_send_message (room, msg, GNUNET_CHAT_MSG_PRIVATE, &pos->pkey, + &seq); + GNUNET_free (user); + return GNUNET_OK; +} + + +static int +do_send_sig (const char *msg, const void *xtra) +{ + uint32_t seq; + + GNUNET_CHAT_send_message (room, msg, GNUNET_CHAT_MSG_AUTHENTICATED, NULL, + &seq); + return GNUNET_OK; +} + + +static int +do_send_ack (const char *msg, const void *xtra) +{ + uint32_t seq; + + GNUNET_CHAT_send_message (room, msg, GNUNET_CHAT_MSG_ACKNOWLEDGED, NULL, + &seq); + return GNUNET_OK; +} + + +static int +do_send_anonymous (const char *msg, const void *xtra) +{ + uint32_t seq; + + GNUNET_CHAT_send_message (room, msg, GNUNET_CHAT_MSG_ANONYMOUS, NULL, &seq); + return GNUNET_OK; +} + + +static int +do_quit (const char *args, const void *xtra) +{ + return GNUNET_SYSERR; +} + + +static int +do_unknown (const char *msg, const void *xtra) +{ + FPRINTF (stderr, _("Unknown command `%s'\n"), msg); + return GNUNET_OK; +} + + +/** + * List of supported IRC commands. The order matters! + */ +static struct ChatCommand commands[] = { + {"/join ", &do_join, + gettext_noop + ("Use `/join #roomname' to join a chat room. Joining a room will cause you" + " to leave the current room")}, + {"/nick ", &do_nick, + gettext_noop + ("Use `/nick nickname' to change your nickname. This will cause you to" + " leave the current room and immediately rejoin it with the new name.")}, + {"/msg ", &do_send_pm, + gettext_noop + ("Use `/msg nickname message' to send a private message to the specified" + " user")}, + {"/notice ", &do_send_pm, + gettext_noop ("The `/notice' command is an alias for `/msg'")}, + {"/query ", &do_send_pm, + gettext_noop ("The `/query' command is an alias for `/msg'")}, + {"/sig ", &do_send_sig, + gettext_noop ("Use `/sig message' to send a signed public message")}, + {"/ack ", &do_send_ack, + gettext_noop + ("Use `/ack message' to require signed acknowledgment of the message")}, + {"/anonymous ", &do_send_anonymous, + gettext_noop + ("Use `/anonymous message' to send a public anonymous message")}, + {"/anon ", &do_send_anonymous, + gettext_noop ("The `/anon' command is an alias for `/anonymous'")}, + {"/quit", &do_quit, + gettext_noop ("Use `/quit' to terminate gnunet-chat")}, + {"/leave", &do_quit, + gettext_noop ("The `/leave' command is an alias for `/quit'")}, + {"/names", &do_names, + gettext_noop + ("Use `/names' to list all of the current members in the chat room")}, + {"/help", &do_help, + gettext_noop ("Use `/help command' to get help for a specific command")}, + /* Add standard commands: + * /whois (print metadata), + * /ignore (set flag, check on receive!) */ + /* the following three commands must be last! */ + {"/", &do_unknown, NULL}, + {"", &do_send, NULL}, + {NULL, NULL, NULL}, +}; + + +static int +do_help (const char *args, const void *xtra) +{ + int i; + + i = 0; + while ((NULL != args) && (0 != strlen (args)) && + (commands[i].Action != &do_help)) + { + if (0 == strncasecmp (&args[1], &commands[i].command[1], strlen (args) - 1)) + { + FPRINTF (stdout, "%s\n", gettext (commands[i].helptext)); + return GNUNET_OK; + } + i++; + } + i = 0; + FPRINTF (stdout, "%s", "Available commands:"); + while (commands[i].Action != &do_help) + { + FPRINTF (stdout, " %s", gettext (commands[i].command)); + i++; + } + FPRINTF (stdout, "%s", "\n"); + FPRINTF (stdout, "%s\n", gettext (commands[i].helptext)); + return GNUNET_OK; +} + + +static void +do_stop_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + GNUNET_CHAT_leave_room (room); + if (handle_cmd_task != GNUNET_SCHEDULER_NO_TASK) + { + GNUNET_SCHEDULER_cancel (handle_cmd_task); + handle_cmd_task = GNUNET_SCHEDULER_NO_TASK; + } + free_user_list (); + GNUNET_CONTAINER_meta_data_destroy (meta); + GNUNET_free (room_name); + GNUNET_free (nickname); +} + + +void +handle_command (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + char message[MAX_MESSAGE_LENGTH + 1]; + int i; + + /* read message from command line and handle it */ + memset (message, 0, MAX_MESSAGE_LENGTH + 1); + if (NULL == fgets (message, MAX_MESSAGE_LENGTH, stdin)) + goto next; + if (strlen (message) == 0) + goto next; + if (message[strlen (message) - 1] == '\n') + message[strlen (message) - 1] = '\0'; + if (strlen (message) == 0) + goto next; + i = 0; + while ((NULL != commands[i].command) && + (0 != + strncasecmp (commands[i].command, message, + strlen (commands[i].command)))) + i++; + if (GNUNET_OK != + commands[i].Action (&message[strlen (commands[i].command)], NULL)) + goto out; + +next: + handle_cmd_task = + GNUNET_SCHEDULER_add_delayed_with_priority (GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_MILLISECONDS, 100), + GNUNET_SCHEDULER_PRIORITY_UI, + &handle_command, NULL); + return; + +out: + handle_cmd_task = GNUNET_SCHEDULER_NO_TASK; + GNUNET_SCHEDULER_shutdown (); +} + + +/** + * Main function that will be run by the scheduler. + * + * @param cls closure, NULL + * @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) +{ + GNUNET_HashCode me; + char *my_name; + + cfg = c; + /* check arguments */ + if (NULL == nickname) + { + FPRINTF (stderr, "%s", _("You must specify a nickname\n")); + ret = -1; + return; + } + if (NULL == room_name) + room_name = GNUNET_strdup ("gnunet"); + meta = GNUNET_CONTAINER_meta_data_create (); + GNUNET_CONTAINER_meta_data_insert (meta, "<gnunet>", EXTRACTOR_METATYPE_TITLE, + EXTRACTOR_METAFORMAT_UTF8, "text/plain", + nickname, strlen (nickname) + 1); + room = + GNUNET_CHAT_join_room (cfg, nickname, meta, room_name, -1, &join_cb, NULL, + &receive_cb, NULL, &member_list_cb, NULL, + &confirmation_cb, NULL, &me); + if (NULL == room) + { + FPRINTF (stderr, _("Failed to join room `%s'\n"), room_name); + GNUNET_free (room_name); + GNUNET_free (nickname); + GNUNET_CONTAINER_meta_data_destroy (meta); + ret = -1; + return; + } + my_name = GNUNET_PSEUDONYM_id_to_name (cfg, &me); + FPRINTF (stdout, _("Joining room `%s' as user `%s'...\n"), room_name, + my_name); + GNUNET_free (my_name); + handle_cmd_task = + GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_UI, + &handle_command, NULL); + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &do_stop_task, + NULL); +} + + +/** + * The main function to chat via GNUnet. + * + * @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 flags; + + static const struct GNUNET_GETOPT_CommandLineOption options[] = { + {'n', "nick", "NAME", + gettext_noop ("set the nickname to use (required)"), + 1, &GNUNET_GETOPT_set_string, &nickname}, + {'r', "room", "NAME", + gettext_noop ("set the chat room to join"), + 1, &GNUNET_GETOPT_set_string, &room_name}, + GNUNET_GETOPT_OPTION_END + }; + +#ifndef WINDOWS + flags = fcntl (0, F_GETFL, 0); + flags |= O_NONBLOCK; + fcntl (0, F_SETFL, flags); +#endif + return (GNUNET_OK == + GNUNET_PROGRAM_run (argc, argv, "gnunet-chat", + gettext_noop ("Join a chat on GNUnet."), options, + &run, NULL)) ? ret : 1; +} + +/* end of gnunet-chat.c */ diff --git a/src/chat/gnunet-service-chat.c b/src/chat/gnunet-service-chat.c new file mode 100644 index 0000000..fb127b2 --- /dev/null +++ b/src/chat/gnunet-service-chat.c @@ -0,0 +1,1737 @@ +/* + This file is part of GNUnet. + (C) 2009, 2011 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 3, 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file chat/gnunet-service-chat.c + * @brief service providing chat functionality + * @author Christian Grothoff + * @author Vitaly Minko + */ + +#include "platform.h" +#include "gnunet_core_service.h" +#include "gnunet_crypto_lib.h" +#include "gnunet_protocols.h" +#include "gnunet_service_lib.h" +#include "gnunet_signatures.h" +#include "chat.h" + +#define DEBUG_CHAT_SERVICE GNUNET_EXTRA_LOGGING +#define MAX_TRANSMIT_DELAY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60) +#define EXPECTED_NEIGHBOUR_COUNT 16 +#define QUEUE_SIZE 16 +#define MAX_ANONYMOUS_MSG_LIST_LENGTH 16 + + +/** + * Linked list of our current clients. + */ +struct ChatClient +{ + struct ChatClient *next; + + /** + * Handle for a chat client (NULL for external clients). + */ + struct GNUNET_SERVER_Client *client; + + /** + * Public key of the client. + */ + struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded public_key; + + /** + * Name of the room which the client is in. + */ + char *room; + + /** + * Serialized metadata of the client. + */ + char *member_info; + + /** + * Hash of the public key (for convenience). + */ + GNUNET_HashCode id; + + /** + * Options which the client is willing to receive. + */ + uint32_t msg_options; + + /** + * Length of serialized metadata in member_info. + */ + uint16_t meta_len; + + /** + * Sequence number of the last message sent by the client. + */ + uint32_t msg_sequence_number; + + /** + * Sequence number of the last receipt sent by the client. + * Used to discard already processed receipts. + */ + uint32_t rcpt_sequence_number; + +}; + +/** + * Information about a peer that we are connected to. + * We track data that is useful for determining which + * peers should receive our requests. + */ +struct ConnectedPeer +{ + /** + * The peer's identity. + */ + GNUNET_PEER_Id pid; +}; + +/** + * Linked list of recent anonymous messages. + */ +struct AnonymousMessage +{ + struct AnonymousMessage *next; + + /** + * Hash of the message. + */ + GNUNET_HashCode hash; + +}; + + +/** + * Handle to the core service (NULL until we've connected to it). + */ +static struct GNUNET_CORE_Handle *core; + +/** + * Our configuration. + */ +static const struct GNUNET_CONFIGURATION_Handle *cfg; + +/** + * The identity of this host. + */ +static const struct GNUNET_PeerIdentity *me; + +/** + * Head of the list of current clients. + */ +static struct ChatClient *client_list_head = NULL; + +/** + * Notification context containing all connected clients. + */ +struct GNUNET_SERVER_NotificationContext *nc = NULL; + +/** + * Head of the list of recent anonymous messages. + */ +static struct AnonymousMessage *anonymous_list_head = NULL; + +/** + * Map of peer identifiers to "struct ConnectedPeer" (for that peer). + */ +static struct GNUNET_CONTAINER_MultiHashMap *connected_peers; + + +static void +remember_anonymous_message (const struct P2PReceiveNotificationMessage + *p2p_rnmsg) +{ + static GNUNET_HashCode hash; + struct AnonymousMessage *anon_msg; + struct AnonymousMessage *prev; + int anon_list_len; + + GNUNET_CRYPTO_hash (p2p_rnmsg, ntohs (p2p_rnmsg->header.size), &hash); + anon_msg = GNUNET_malloc (sizeof (struct AnonymousMessage)); + anon_msg->hash = hash; + anon_msg->next = anonymous_list_head; + anonymous_list_head = anon_msg; + anon_list_len = 1; + prev = NULL; + while ((NULL != anon_msg->next)) + { + prev = anon_msg; + anon_msg = anon_msg->next; + anon_list_len++; + } + if (anon_list_len == MAX_ANONYMOUS_MSG_LIST_LENGTH) + { + GNUNET_free (anon_msg); + if (NULL != prev) + prev->next = NULL; + } +} + + +static int +lookup_anonymous_message (const struct P2PReceiveNotificationMessage *p2p_rnmsg) +{ + static GNUNET_HashCode hash; + struct AnonymousMessage *anon_msg; + + GNUNET_CRYPTO_hash (p2p_rnmsg, ntohs (p2p_rnmsg->header.size), &hash); + anon_msg = anonymous_list_head; + while ((NULL != anon_msg) && + (0 != memcmp (&anon_msg->hash, &hash, sizeof (GNUNET_HashCode)))) + anon_msg = anon_msg->next; + return (NULL != anon_msg); +} + + +/** + * Transmit a message notification to the peer. + * + * @param cls closure, pointer to the 'struct P2PReceiveNotificationMessage' + * @param size number of bytes available in buf + * @param buf where the callee should write the message + * @return number of bytes written to buf + */ +static size_t +transmit_message_notification_to_peer (void *cls, size_t size, void *buf) +{ + struct P2PReceiveNotificationMessage *my_msg = cls; + struct P2PReceiveNotificationMessage *m = buf; + size_t msg_size; + +#if DEBUG_CHAT_SERVICE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Transmitting P2P message notification\n"); +#endif + if (buf == NULL) + { + /* client disconnected */ +#if DEBUG_CHAT_SERVICE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Buffer is NULL, dropping the message\n"); +#endif + return 0; + } + msg_size = ntohs (my_msg->header.size); + GNUNET_assert (size >= msg_size); + memcpy (m, my_msg, msg_size); + GNUNET_free (my_msg); + return msg_size; +} + + +/** + * Ask to send a message notification to the peer. + */ +static int +send_message_noficiation (void *cls, const GNUNET_HashCode * key, void *value) +{ + struct P2PReceiveNotificationMessage *msg = cls; + struct ConnectedPeer *cp = value; + struct GNUNET_PeerIdentity pid; + struct P2PReceiveNotificationMessage *my_msg; + + GNUNET_PEER_resolve (cp->pid, &pid); +#if DEBUG_CHAT_SERVICE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending message notification to `%s'\n", + GNUNET_i2s (&pid)); +#endif + my_msg = GNUNET_memdup (msg, ntohs (msg->header.size)); + if (NULL == + GNUNET_CORE_notify_transmit_ready (core, GNUNET_NO, 1, MAX_TRANSMIT_DELAY, + &pid, ntohs (msg->header.size), + &transmit_message_notification_to_peer, + my_msg)) + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("Failed to queue a message notification\n")); + return GNUNET_YES; +} + + +/** + * A client sent a chat message. Encrypt the message text if the message is + * private. Send the message to local room members and to all connected peers. + * + * @param cls closure, NULL + * @param client identification of the client + * @param message the actual message + */ +static void +handle_transmit_request (void *cls, struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + static GNUNET_HashCode all_zeros; + const struct TransmitRequestMessage *trmsg; + struct ReceiveNotificationMessage *rnmsg; + struct P2PReceiveNotificationMessage *p2p_rnmsg; + struct ChatClient *pos; + struct ChatClient *target; + struct GNUNET_CRYPTO_AesSessionKey key; + char encrypted_msg[MAX_MESSAGE_LENGTH]; + const char *room; + size_t room_len; + int msg_len; + int is_priv; + int is_anon; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Client sent a chat message\n"); + if (ntohs (message->size) <= sizeof (struct TransmitRequestMessage)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Malformed message: wrong size\n"); + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + trmsg = (const struct TransmitRequestMessage *) message; + msg_len = ntohs (trmsg->header.size) - sizeof (struct TransmitRequestMessage); + is_priv = (0 != (ntohl (trmsg->msg_options) & GNUNET_CHAT_MSG_PRIVATE)); + if (is_priv) + { +#if DEBUG_CHAT_SERVICE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Encrypting the message text\n"); +#endif + GNUNET_CRYPTO_aes_create_session_key (&key); + msg_len = + GNUNET_CRYPTO_aes_encrypt (&trmsg[1], msg_len, &key, + (const struct + GNUNET_CRYPTO_AesInitializationVector *) + INITVALUE, encrypted_msg); + if (-1 == msg_len) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not encrypt the message text\n"); + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + } + rnmsg = GNUNET_malloc (sizeof (struct ReceiveNotificationMessage) + msg_len); + rnmsg->header.size = + htons (sizeof (struct ReceiveNotificationMessage) + msg_len); + rnmsg->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_MESSAGE_NOTIFICATION); + rnmsg->msg_options = trmsg->msg_options; + rnmsg->timestamp = trmsg->timestamp; + pos = client_list_head; + while ((NULL != pos) && (pos->client != client)) + pos = pos->next; + if (NULL == pos) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "The client is not a member of a chat room. Client has to " + "join a chat room first\n"); + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + GNUNET_free (rnmsg); + return; + } + room = pos->room; + pos->msg_sequence_number = ntohl (trmsg->sequence_number); + is_anon = (0 != (ntohl (trmsg->msg_options) & GNUNET_CHAT_MSG_ANONYMOUS)); + if (is_anon) + { + memset (&rnmsg->sender, 0, sizeof (GNUNET_HashCode)); + rnmsg->sequence_number = 0; + } + else + { + rnmsg->sender = pos->id; + rnmsg->sequence_number = trmsg->sequence_number; + } + if (is_priv) + { +#if DEBUG_CHAT_SERVICE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Encrypting the session key using the public key of '%s'\n", + GNUNET_h2s (&trmsg->target)); +#endif + if (0 == memcmp (&all_zeros, &trmsg->target, sizeof (GNUNET_HashCode))) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Malformed message: private, but no target\n"); + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + GNUNET_free (rnmsg); + return; + } + memcpy (&rnmsg[1], encrypted_msg, msg_len); + target = client_list_head; + while ((NULL != target) && + (0 != + memcmp (&target->id, &trmsg->target, sizeof (GNUNET_HashCode)))) + target = target->next; + if (NULL == target) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unknown target of the private message\n"); + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + GNUNET_free (rnmsg); + return; + } + if (GNUNET_SYSERR == + GNUNET_CRYPTO_rsa_encrypt (&key, + sizeof (struct GNUNET_CRYPTO_AesSessionKey), + &target->public_key, &rnmsg->encrypted_key)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Could not encrypt the session key\n"); + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + GNUNET_free (rnmsg); + return; + } + } + else + { + memcpy (&rnmsg[1], &trmsg[1], msg_len); + } + pos = client_list_head; +#if DEBUG_CHAT_SERVICE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending message to local room members\n"); +#endif + while (NULL != pos) + { + if ((0 == strcmp (room, pos->room)) && (NULL != pos->client) && + (pos->client != client)) + { + if (((!is_priv) || + (0 == memcmp (&trmsg->target, &pos->id, sizeof (GNUNET_HashCode)))) + && (0 == (ntohl (trmsg->msg_options) & (~pos->msg_options)))) + { + GNUNET_SERVER_notification_context_unicast (nc, pos->client, + &rnmsg->header, GNUNET_NO); + } + } + pos = pos->next; + } +#if DEBUG_CHAT_SERVICE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Broadcasting message to neighbour peers\n"); +#endif + if (is_anon) + { + room_len = strlen (room); + p2p_rnmsg = + GNUNET_malloc (sizeof (struct P2PReceiveNotificationMessage) + msg_len + + room_len); + p2p_rnmsg->header.size = + htons (sizeof (struct P2PReceiveNotificationMessage) + msg_len + + room_len); + p2p_rnmsg->room_name_len = htons (room_len); + memcpy ((char *) &p2p_rnmsg[1], room, room_len); + memcpy ((char *) &p2p_rnmsg[1] + room_len, &trmsg[1], msg_len); + } + else + { + p2p_rnmsg = + GNUNET_malloc (sizeof (struct P2PReceiveNotificationMessage) + msg_len); + p2p_rnmsg->header.size = + htons (sizeof (struct P2PReceiveNotificationMessage) + msg_len); + if (is_priv) + { + memcpy (&p2p_rnmsg[1], encrypted_msg, msg_len); + memcpy (&p2p_rnmsg->encrypted_key, &rnmsg->encrypted_key, + sizeof (struct GNUNET_CRYPTO_RsaEncryptedData)); + } + else + memcpy (&p2p_rnmsg[1], &trmsg[1], msg_len); + } + p2p_rnmsg->header.type = + htons (GNUNET_MESSAGE_TYPE_CHAT_P2P_MESSAGE_NOTIFICATION); + p2p_rnmsg->msg_options = trmsg->msg_options; + p2p_rnmsg->sequence_number = trmsg->sequence_number; + p2p_rnmsg->timestamp = trmsg->timestamp; + p2p_rnmsg->reserved = htons (0); + p2p_rnmsg->sender = rnmsg->sender; + p2p_rnmsg->target = trmsg->target; + if (is_anon) + remember_anonymous_message (p2p_rnmsg); + GNUNET_CONTAINER_multihashmap_iterate (connected_peers, + &send_message_noficiation, p2p_rnmsg); + GNUNET_free (p2p_rnmsg); + GNUNET_SERVER_receive_done (client, GNUNET_OK); + GNUNET_free (rnmsg); +} + + +/** + * Transmit a join notification to the peer. + * + * @param cls closure, pointer to the 'struct ChatClient' + * @param size number of bytes available in buf + * @param buf where the callee should write the message + * @return number of bytes written to buf + */ +static size_t +transmit_join_notification_to_peer (void *cls, size_t size, void *buf) +{ + struct ChatClient *entry = cls; + struct P2PJoinNotificationMessage *m = buf; + size_t room_len; + size_t meta_len; + size_t msg_size; + char *roomptr; + +#if DEBUG_CHAT_SERVICE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Transmitting P2P join notification\n"); +#endif + room_len = strlen (entry->room); + meta_len = entry->meta_len; + msg_size = sizeof (struct P2PJoinNotificationMessage) + meta_len + room_len; + GNUNET_assert (size >= msg_size); + GNUNET_assert (NULL != buf); + m = buf; + m->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_P2P_JOIN_NOTIFICATION); + m->header.size = htons (msg_size); + m->msg_options = htonl (entry->msg_options); + m->room_name_len = htons (room_len); + m->reserved = htons (0); + m->reserved2 = htonl (0); + m->public_key = entry->public_key; + roomptr = (char *) &m[1]; + memcpy (roomptr, entry->room, room_len); + if (meta_len > 0) + memcpy (&roomptr[room_len], entry->member_info, meta_len); + return msg_size; +} + + +/** + * Ask to send a join notification to the peer. + */ +static int +send_join_noficiation (void *cls, const GNUNET_HashCode * key, void *value) +{ + struct ChatClient *entry = cls; + struct ConnectedPeer *cp = value; + struct GNUNET_PeerIdentity pid; + size_t msg_size; + + GNUNET_PEER_resolve (cp->pid, &pid); +#if DEBUG_CHAT_SERVICE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending join notification to `%s'\n", + GNUNET_i2s (&pid)); +#endif + msg_size = + sizeof (struct P2PJoinNotificationMessage) + strlen (entry->room) + + entry->meta_len; + if (NULL == + GNUNET_CORE_notify_transmit_ready (core, GNUNET_NO, 1, MAX_TRANSMIT_DELAY, + &pid, msg_size, + &transmit_join_notification_to_peer, + entry)) + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("Failed to queue a join notification\n")); + return GNUNET_YES; +} + + +/** + * A client asked for entering a chat room. Add the new member to the list of + * clients and notify remaining room members. + * + * @param cls closure, NULL + * @param client identification of the client + * @param message the actual message + */ +static void +handle_join_request (void *cls, struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + const struct JoinRequestMessage *jrmsg; + char *room_name; + const char *roomptr; + uint16_t header_size; + uint16_t meta_len; + uint16_t room_name_len; + struct ChatClient *new_entry; + struct ChatClient *entry; + struct JoinNotificationMessage *jnmsg; + struct JoinNotificationMessage *entry_jnmsg; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Client sent a join request\n"); + if (ntohs (message->size) <= sizeof (struct JoinRequestMessage)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Malformed message: wrong size\n"); + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + jrmsg = (const struct JoinRequestMessage *) message; + header_size = ntohs (jrmsg->header.size); + room_name_len = ntohs (jrmsg->room_name_len); + if (header_size - sizeof (struct JoinRequestMessage) <= room_name_len) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Malformed message: wrong length of the room name\n"); + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + meta_len = header_size - sizeof (struct JoinRequestMessage) - room_name_len; + roomptr = (const char *) &jrmsg[1]; + room_name = GNUNET_malloc (room_name_len + 1); + memcpy (room_name, roomptr, room_name_len); + room_name[room_name_len] = '\0'; + new_entry = GNUNET_malloc (sizeof (struct ChatClient)); + memset (new_entry, 0, sizeof (struct ChatClient)); + new_entry->client = client; + new_entry->room = room_name; + new_entry->public_key = jrmsg->public_key; + new_entry->meta_len = meta_len; + if (meta_len > 0) + { + new_entry->member_info = GNUNET_malloc (meta_len); + memcpy (new_entry->member_info, &roomptr[room_name_len], meta_len); + } + else + new_entry->member_info = NULL; + GNUNET_CRYPTO_hash (&new_entry->public_key, + sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded), + &new_entry->id); + new_entry->msg_options = ntohl (jrmsg->msg_options); + new_entry->next = client_list_head; + client_list_head = new_entry; +#if DEBUG_CHAT_SERVICE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Synchronizing room members between local clients\n"); +#endif + jnmsg = GNUNET_malloc (sizeof (struct JoinNotificationMessage) + meta_len); + jnmsg->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_JOIN_NOTIFICATION); + jnmsg->header.size = + htons (sizeof (struct JoinNotificationMessage) + meta_len); + jnmsg->msg_options = jrmsg->msg_options; + jnmsg->public_key = new_entry->public_key; + memcpy (&jnmsg[1], &roomptr[room_name_len], meta_len); + GNUNET_SERVER_notification_context_add (nc, client); + entry = client_list_head; + while (NULL != entry) + { + if (0 == strcmp (room_name, entry->room)) + { + if (NULL != entry->client) + GNUNET_SERVER_notification_context_unicast (nc, entry->client, + &jnmsg->header, GNUNET_NO); + if (entry->client != client) + { + entry_jnmsg = + GNUNET_malloc (sizeof (struct JoinNotificationMessage) + + entry->meta_len); + entry_jnmsg->header.type = + htons (GNUNET_MESSAGE_TYPE_CHAT_JOIN_NOTIFICATION); + entry_jnmsg->header.size = + htons (sizeof (struct JoinNotificationMessage) + entry->meta_len); + entry_jnmsg->msg_options = entry->msg_options; + entry_jnmsg->public_key = entry->public_key; + memcpy (&entry_jnmsg[1], entry->member_info, entry->meta_len); + GNUNET_SERVER_notification_context_unicast (nc, client, + &entry_jnmsg->header, + GNUNET_NO); + GNUNET_free (entry_jnmsg); + } + } + entry = entry->next; + } +#if DEBUG_CHAT_SERVICE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Broadcasting join notification to neighbour peers\n"); +#endif + GNUNET_CONTAINER_multihashmap_iterate (connected_peers, + &send_join_noficiation, new_entry); + GNUNET_SERVER_receive_done (client, GNUNET_OK); + GNUNET_free (jnmsg); +} + +/** + * Transmit a confirmation receipt to the peer. + * + * @param cls closure, pointer to the 'struct P2PConfirmationReceiptMessage' + * @param size number of bytes available in buf + * @param buf where the callee should write the message + * @return number of bytes written to buf + */ +static size_t +transmit_confirmation_receipt_to_peer (void *cls, size_t size, void *buf) +{ + struct P2PConfirmationReceiptMessage *receipt = cls; + size_t msg_size; + +#if DEBUG_CHAT_SERVICE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Transmitting P2P confirmation receipt to '%s'\n", + GNUNET_h2s (&receipt->target)); +#endif + if (buf == NULL) + { + /* client disconnected */ +#if DEBUG_CHAT_SERVICE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Buffer is NULL, dropping the message\n"); +#endif + return 0; + } + msg_size = sizeof (struct P2PConfirmationReceiptMessage); + GNUNET_assert (size >= msg_size); + memcpy (buf, receipt, msg_size); + GNUNET_free (receipt); + return msg_size; +} + + +/** + * Ask to send a confirmation receipt to the peer. + */ +static int +send_confirmation_receipt (void *cls, const GNUNET_HashCode * key, void *value) +{ + struct P2PConfirmationReceiptMessage *receipt = cls; + struct ConnectedPeer *cp = value; + struct GNUNET_PeerIdentity pid; + struct P2PConfirmationReceiptMessage *my_receipt; + size_t msg_size; + + GNUNET_PEER_resolve (cp->pid, &pid); +#if DEBUG_CHAT_SERVICE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending confirmation receipt to `%s'\n", + GNUNET_i2s (&pid)); +#endif + msg_size = sizeof (struct P2PConfirmationReceiptMessage); + my_receipt = + GNUNET_memdup (receipt, sizeof (struct P2PConfirmationReceiptMessage)); + if (NULL == + GNUNET_CORE_notify_transmit_ready (core, GNUNET_YES, 1, + MAX_TRANSMIT_DELAY, &pid, msg_size, + &transmit_confirmation_receipt_to_peer, + my_receipt)) + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("Failed to queue a confirmation receipt\n")); + return GNUNET_YES; +} + + +/** + * A client sent a confirmation receipt. Broadcast the receipt to all connected + * peers if the author of the original message is a local client. Otherwise + * check the signature and notify the user if the signature is valid. + * + * @param cls closure, NULL + * @param client identification of the client + * @param message the actual message + */ +static void +handle_acknowledge_request (void *cls, struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + const struct ConfirmationReceiptMessage *receipt; + struct ConfirmationReceiptMessage *crmsg; + struct P2PConfirmationReceiptMessage *p2p_crmsg; + struct ChatClient *target; + struct ChatClient *author; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Client sent a confirmation receipt\n"); + receipt = (const struct ConfirmationReceiptMessage *) message; + author = client_list_head; + while ((NULL != author) && + (0 != + memcmp (&receipt->author, &author->id, sizeof (GNUNET_HashCode)))) + author = author->next; + if (NULL == author) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unknown author of the original message\n"); + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + target = client_list_head; + while ((NULL != target) && + (0 != + memcmp (&receipt->target, &target->id, sizeof (GNUNET_HashCode)))) + target = target->next; + if (NULL == target) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unknown target of the confirmation receipt\n"); + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + if (NULL == author->client) + { + target->rcpt_sequence_number++; +#if DEBUG_CHAT_SERVICE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Broadcasting %s's receipt #%u to neighbour peers\n", + GNUNET_h2s (&target->id), target->rcpt_sequence_number); +#endif + p2p_crmsg = GNUNET_malloc (sizeof (struct P2PConfirmationReceiptMessage)); + p2p_crmsg->header.size = + htons (sizeof (struct P2PConfirmationReceiptMessage)); + p2p_crmsg->header.type = + htons (GNUNET_MESSAGE_TYPE_CHAT_P2P_CONFIRMATION_RECEIPT); + p2p_crmsg->reserved = htonl (0); + p2p_crmsg->signature = receipt->signature; + p2p_crmsg->purpose = receipt->purpose; + p2p_crmsg->msg_sequence_number = receipt->sequence_number; + p2p_crmsg->timestamp = receipt->timestamp; + p2p_crmsg->target = receipt->target; + p2p_crmsg->author = receipt->author; + p2p_crmsg->content = receipt->content; + p2p_crmsg->sequence_number = htonl (target->rcpt_sequence_number); + GNUNET_CONTAINER_multihashmap_iterate (connected_peers, + &send_confirmation_receipt, + p2p_crmsg); + GNUNET_free (p2p_crmsg); + } + else + { +#if DEBUG_CHAT_SERVICE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Verifying signature of the receipt\n"); +#endif + if (GNUNET_OK != + GNUNET_CRYPTO_rsa_verify (GNUNET_SIGNATURE_PURPOSE_CHAT_RECEIPT, + &receipt->purpose, &receipt->signature, + &target->public_key)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Invalid signature of the receipt\n"); + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } +#if DEBUG_CHAT_SERVICE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending receipt to the client which sent the original message\n"); +#endif + crmsg = GNUNET_memdup (receipt, sizeof (struct ConfirmationReceiptMessage)); + crmsg->header.type = + htons (GNUNET_MESSAGE_TYPE_CHAT_CONFIRMATION_NOTIFICATION); + GNUNET_SERVER_notification_context_unicast (nc, author->client, + &crmsg->header, GNUNET_NO); + GNUNET_free (crmsg); + } + GNUNET_SERVER_receive_done (client, GNUNET_OK); +} + + +/** + * Transmit a leave notification to the peer. + * + * @param cls closure, pointer to the + * 'struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded' + * @param size number of bytes available in buf + * @param buf where the callee should write the message + * @return number of bytes written to buf + */ +static size_t +transmit_leave_notification_to_peer (void *cls, size_t size, void *buf) +{ + struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *public_key = cls; + struct P2PLeaveNotificationMessage *m = buf; + size_t msg_size; + +#if DEBUG_CHAT_SERVICE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Transmitting P2P leave notification\n"); +#endif + if (buf == NULL) + { + /* client disconnected */ +#if DEBUG_CHAT_SERVICE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Buffer is NULL, dropping the message\n"); +#endif + return 0; + } + msg_size = sizeof (struct P2PLeaveNotificationMessage); + GNUNET_assert (size >= msg_size); + m = buf; + m->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_P2P_LEAVE_NOTIFICATION); + m->header.size = htons (msg_size); + m->reserved = htonl (0); + m->user = *public_key; + GNUNET_free (public_key); + return msg_size; +} + + +/** + * Ask to send a leave notification to the peer. + */ +static int +send_leave_noficiation (void *cls, const GNUNET_HashCode * key, void *value) +{ + struct ChatClient *entry = cls; + struct ConnectedPeer *cp = value; + struct GNUNET_PeerIdentity pid; + struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *public_key; + size_t msg_size; + + GNUNET_PEER_resolve (cp->pid, &pid); +#if DEBUG_CHAT_SERVICE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending leave notification to `%s'\n", + GNUNET_i2s (&pid)); +#endif + msg_size = sizeof (struct P2PLeaveNotificationMessage); + public_key = + GNUNET_memdup (&entry->public_key, + sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded)); + if (NULL == + GNUNET_CORE_notify_transmit_ready (core, GNUNET_YES, 1, + MAX_TRANSMIT_DELAY, &pid, msg_size, + &transmit_leave_notification_to_peer, + public_key)) + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("Failed to queue a leave notification\n")); + return GNUNET_YES; +} + + +/** + * A client disconnected. Remove all of its data structure entries and notify + * remaining room members. + * + * @param cls closure, NULL + * @param client identification of the client + */ +static void +handle_client_disconnect (void *cls, struct GNUNET_SERVER_Client *client) +{ + struct ChatClient *entry; + struct ChatClient *pos; + struct ChatClient *prev; + struct LeaveNotificationMessage lnmsg; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Client disconnected\n"); + pos = client_list_head; + prev = NULL; + while ((NULL != pos) && (pos->client != client)) + { + prev = pos; + pos = pos->next; + } + if (NULL == pos) + { +#if DEBUG_CHAT_SERVICE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "No such client. There is nothing to do\n"); +#endif + return; + } + if (NULL == prev) + client_list_head = pos->next; + else + prev->next = pos->next; + entry = client_list_head; +#if DEBUG_CHAT_SERVICE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Notifying local room members that the client has disconnected\n"); +#endif + lnmsg.header.size = htons (sizeof (struct LeaveNotificationMessage)); + lnmsg.header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_LEAVE_NOTIFICATION); + lnmsg.reserved = htonl (0); + lnmsg.user = pos->public_key; + while (NULL != entry) + { + if ((0 == strcmp (pos->room, entry->room)) && (NULL != entry->client)) + { + GNUNET_SERVER_notification_context_unicast (nc, entry->client, + &lnmsg.header, GNUNET_NO); + } + entry = entry->next; + } +#if DEBUG_CHAT_SERVICE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Broadcasting leave notification to neighbour peers\n"); +#endif + GNUNET_CONTAINER_multihashmap_iterate (connected_peers, + &send_leave_noficiation, pos); + GNUNET_free (pos->room); + GNUNET_free_non_null (pos->member_info); + GNUNET_free (pos); +} + + +/** + * Handle P2P join notification. + * + * @param cls closure, always NULL + * @param other the other peer involved + * @param message the actual message + * @param atsi performance information + * @param atsi_count number of entries in atsi + * @return GNUNET_OK to keep the connection open, + * GNUNET_SYSERR to close it (signal serious error) + */ +static int +handle_p2p_join_notification (void *cls, + const struct GNUNET_PeerIdentity *other, + const struct GNUNET_MessageHeader *message, + const struct GNUNET_ATS_Information *atsi, + unsigned int atsi_count) +{ + const struct P2PJoinNotificationMessage *p2p_jnmsg; + char *room_name; + const char *roomptr; + uint16_t header_size; + uint16_t meta_len; + uint16_t room_name_len; + struct ChatClient *new_entry; + struct ChatClient *entry; + struct JoinNotificationMessage *jnmsg; + GNUNET_HashCode id; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Got P2P join notification\n"); + if (ntohs (message->size) <= sizeof (struct P2PJoinNotificationMessage)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Malformed message: wrong size\n"); + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + p2p_jnmsg = (const struct P2PJoinNotificationMessage *) message; + header_size = ntohs (p2p_jnmsg->header.size); + room_name_len = ntohs (p2p_jnmsg->room_name_len); + if (header_size - sizeof (struct P2PJoinNotificationMessage) <= room_name_len) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Malformed message: wrong length of the room name\n"); + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + GNUNET_CRYPTO_hash (&p2p_jnmsg->public_key, + sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded), + &id); + entry = client_list_head; + while (NULL != entry) + { + if (0 == memcmp (&entry->id, &id, sizeof (GNUNET_HashCode))) + { +#if DEBUG_CHAT_SERVICE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "The client has already joined. There is nothing to do\n"); +#endif + return GNUNET_OK; + } + entry = entry->next; + } + meta_len = + header_size - sizeof (struct P2PJoinNotificationMessage) - room_name_len; + roomptr = (const char *) &p2p_jnmsg[1]; + room_name = GNUNET_malloc (room_name_len + 1); + memcpy (room_name, roomptr, room_name_len); + room_name[room_name_len] = '\0'; + new_entry = GNUNET_malloc (sizeof (struct ChatClient)); + memset (new_entry, 0, sizeof (struct ChatClient)); + new_entry->id = id; + new_entry->client = NULL; + new_entry->room = room_name; + new_entry->public_key = p2p_jnmsg->public_key; + new_entry->meta_len = meta_len; + if (meta_len > 0) + { + new_entry->member_info = GNUNET_malloc (meta_len); + memcpy (new_entry->member_info, &roomptr[room_name_len], meta_len); + } + else + new_entry->member_info = NULL; + new_entry->msg_options = ntohl (p2p_jnmsg->msg_options); + new_entry->next = client_list_head; + client_list_head = new_entry; +#if DEBUG_CHAT_SERVICE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Notifying local room members that we have a new client\n"); +#endif + jnmsg = GNUNET_malloc (sizeof (struct JoinNotificationMessage) + meta_len); + jnmsg->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_JOIN_NOTIFICATION); + jnmsg->header.size = + htons (sizeof (struct JoinNotificationMessage) + meta_len); + jnmsg->msg_options = p2p_jnmsg->msg_options; + jnmsg->public_key = new_entry->public_key; + memcpy (&jnmsg[1], &roomptr[room_name_len], meta_len); + entry = client_list_head; + while (NULL != entry) + { + if ((0 == strcmp (room_name, entry->room)) && (NULL != entry->client)) + { + GNUNET_SERVER_notification_context_unicast (nc, entry->client, + &jnmsg->header, GNUNET_NO); + } + entry = entry->next; + } +#if DEBUG_CHAT_SERVICE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Broadcasting join notification to neighbour peers\n"); +#endif + GNUNET_CONTAINER_multihashmap_iterate (connected_peers, + &send_join_noficiation, new_entry); + GNUNET_free (jnmsg); + return GNUNET_OK; +} + + +/** + * Handle P2P leave notification. + * + * @param cls closure, always NULL + * @param other the other peer involved + * @param message the actual message + * @param atsi performance information + * @param atsi_count number of entries in atsi + * @return GNUNET_OK to keep the connection open, + * GNUNET_SYSERR to close it (signal serious error) + */ +static int +handle_p2p_leave_notification (void *cls, + const struct GNUNET_PeerIdentity *other, + const struct GNUNET_MessageHeader *message, + const struct GNUNET_ATS_Information *atsi, + unsigned int atsi_count) +{ + const struct P2PLeaveNotificationMessage *p2p_lnmsg; + GNUNET_HashCode id; + struct ChatClient *pos; + struct ChatClient *prev; + struct ChatClient *entry; + struct LeaveNotificationMessage lnmsg; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Got P2P leave notification\n"); + p2p_lnmsg = (const struct P2PLeaveNotificationMessage *) message; + GNUNET_CRYPTO_hash (&p2p_lnmsg->user, + sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded), + &id); + pos = client_list_head; + prev = NULL; + while (NULL != pos) + { + if (0 == memcmp (&pos->id, &id, sizeof (GNUNET_HashCode))) + break; + prev = pos; + pos = pos->next; + } + if (NULL == pos) + { +#if DEBUG_CHAT_SERVICE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "No such client. There is nothing to do\n"); +#endif + return GNUNET_OK; + } + if (NULL == prev) + client_list_head = pos->next; + else + prev->next = pos->next; +#if DEBUG_CHAT_SERVICE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Notifying local room members that the client has gone away\n"); +#endif + lnmsg.header.size = htons (sizeof (struct LeaveNotificationMessage)); + lnmsg.header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_LEAVE_NOTIFICATION); + lnmsg.reserved = htonl (0); + lnmsg.user = pos->public_key; + entry = client_list_head; + while (NULL != entry) + { + if (0 == strcmp (pos->room, entry->room) && (NULL != entry->client)) + { + GNUNET_SERVER_notification_context_unicast (nc, entry->client, + &lnmsg.header, GNUNET_NO); + } + entry = entry->next; + } +#if DEBUG_CHAT_SERVICE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Broadcasting leave notification to neighbour peers\n"); +#endif + GNUNET_CONTAINER_multihashmap_iterate (connected_peers, + &send_leave_noficiation, pos); + GNUNET_free (pos->room); + GNUNET_free_non_null (pos->member_info); + GNUNET_free (pos); + return GNUNET_OK; +} + + +/** + * Handle P2P message notification. + * + * @param cls closure, always NULL + * @param other the other peer involved + * @param message the actual message + * @param atsi performance information + * @param atsi_count number of entries in atsi + * @return GNUNET_OK to keep the connection open, + * GNUNET_SYSERR to close it (signal serious error) + */ +static int +handle_p2p_message_notification (void *cls, + const struct GNUNET_PeerIdentity *other, + const struct GNUNET_MessageHeader *message, + const struct GNUNET_ATS_Information *atsi, + unsigned int atsi_count) +{ + const struct P2PReceiveNotificationMessage *p2p_rnmsg; + struct P2PReceiveNotificationMessage *my_p2p_rnmsg; + struct ReceiveNotificationMessage *rnmsg; + struct ChatClient *sender; + struct ChatClient *pos; + static GNUNET_HashCode all_zeros; + int is_priv; + int is_anon; + uint16_t msg_len; + uint16_t room_name_len; + char *room_name = NULL; + char *text; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Got P2P message notification\n"); + if (ntohs (message->size) <= sizeof (struct P2PReceiveNotificationMessage)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Malformed message: wrong size\n"); + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + p2p_rnmsg = (const struct P2PReceiveNotificationMessage *) message; + msg_len = + ntohs (p2p_rnmsg->header.size) - + sizeof (struct P2PReceiveNotificationMessage); + + is_anon = (0 != (ntohl (p2p_rnmsg->msg_options) & GNUNET_CHAT_MSG_ANONYMOUS)); + if (is_anon) + { + room_name_len = ntohs (p2p_rnmsg->room_name_len); + if (msg_len <= room_name_len) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Malformed message: wrong length of the room name\n"); + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + msg_len -= room_name_len; + if (lookup_anonymous_message (p2p_rnmsg)) + { +#if DEBUG_CHAT_SERVICE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "This anonymous message has already been handled."); +#endif + return GNUNET_OK; + } + remember_anonymous_message (p2p_rnmsg); + room_name = GNUNET_malloc (room_name_len + 1); + memcpy (room_name, (char *) &p2p_rnmsg[1], room_name_len); + room_name[room_name_len] = '\0'; + text = (char *) &p2p_rnmsg[1] + room_name_len; + } + else + { + sender = client_list_head; + while ((NULL != sender) && + (0 != + memcmp (&sender->id, &p2p_rnmsg->sender, sizeof (GNUNET_HashCode)))) + sender = sender->next; + if (NULL == sender) + { + /* not an error since the sender may have left before we got the + * message */ +#if DEBUG_CHAT_SERVICE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Unknown source. Rejecting the message\n"); +#endif + return GNUNET_OK; + } + if (sender->msg_sequence_number >= ntohl (p2p_rnmsg->sequence_number)) + { +#if DEBUG_CHAT_SERVICE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "This message has already been handled." + " Sequence numbers (msg/sender): %u/%u\n", + ntohl (p2p_rnmsg->sequence_number), + sender->msg_sequence_number); +#endif + return GNUNET_OK; + } + sender->msg_sequence_number = ntohl (p2p_rnmsg->sequence_number); + room_name = sender->room; + text = (char *) &p2p_rnmsg[1]; + } + +#if DEBUG_CHAT_SERVICE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending message to local room members\n"); +#endif + rnmsg = GNUNET_malloc (sizeof (struct ReceiveNotificationMessage) + msg_len); + rnmsg->header.size = + htons (sizeof (struct ReceiveNotificationMessage) + msg_len); + rnmsg->header.type = htons (GNUNET_MESSAGE_TYPE_CHAT_MESSAGE_NOTIFICATION); + rnmsg->msg_options = p2p_rnmsg->msg_options; + rnmsg->sequence_number = p2p_rnmsg->sequence_number; + rnmsg->reserved = htonl (0); + rnmsg->timestamp = p2p_rnmsg->timestamp; + is_priv = + (0 != memcmp (&all_zeros, &p2p_rnmsg->target, sizeof (GNUNET_HashCode))); + if (is_priv) + memcpy (&rnmsg->encrypted_key, &p2p_rnmsg->encrypted_key, + sizeof (struct GNUNET_CRYPTO_RsaEncryptedData)); + rnmsg->sender = p2p_rnmsg->sender; + memcpy (&rnmsg[1], text, msg_len); + pos = client_list_head; + while (NULL != pos) + { + if ((0 == strcmp (room_name, pos->room)) && (NULL != pos->client)) + { + if (((!is_priv) || + (0 == + memcmp (&p2p_rnmsg->target, &pos->id, sizeof (GNUNET_HashCode)))) && + (0 == (ntohl (p2p_rnmsg->msg_options) & (~pos->msg_options)))) + { + GNUNET_SERVER_notification_context_unicast (nc, pos->client, + &rnmsg->header, GNUNET_NO); + } + } + pos = pos->next; + } + if (is_anon) + GNUNET_free (room_name); +#if DEBUG_CHAT_SERVICE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Broadcasting message notification to neighbour peers\n"); +#endif + my_p2p_rnmsg = GNUNET_memdup (p2p_rnmsg, ntohs (p2p_rnmsg->header.size)); + GNUNET_CONTAINER_multihashmap_iterate (connected_peers, + &send_message_noficiation, + my_p2p_rnmsg); + GNUNET_free (rnmsg); + return GNUNET_OK; +} + + +/** + * Handle P2P sync request. + * + * @param cls closure, always NULL + * @param other the other peer involved + * @param message the actual message + * @param atsi performance information + * @param atsi_count number of entries in atsi + * @return GNUNET_OK to keep the connection open, + * GNUNET_SYSERR to close it (signal serious error) + */ +static int +handle_p2p_sync_request (void *cls, const struct GNUNET_PeerIdentity *other, + const struct GNUNET_MessageHeader *message, + const struct GNUNET_ATS_Information *atsi, + unsigned int atsi_count) +{ + struct ChatClient *entry; + struct GNUNET_CORE_TransmitHandle *th; + size_t msg_size; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Got P2P sync request\n"); +#if DEBUG_CHAT_SERVICE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Notifying the requester of all known clients\n"); +#endif + entry = client_list_head; + while (NULL != entry) + { + msg_size = + sizeof (struct P2PJoinNotificationMessage) + strlen (entry->room) + + entry->meta_len; + th = GNUNET_CORE_notify_transmit_ready (core, GNUNET_NO, 1, + MAX_TRANSMIT_DELAY, other, msg_size, + &transmit_join_notification_to_peer, + entry); + GNUNET_assert (NULL != th); + entry = entry->next; + } + return GNUNET_OK; +} + + +/** + * Handle P2P confirmation receipt. + * + * @param cls closure, always NULL + * @param other the other peer involved + * @param message the actual message + * @param atsi performance information + * @param atsi_count number of entries in atsi + * @return GNUNET_OK to keep the connection open, + * GNUNET_SYSERR to close it (signal serious error) + */ +static int +handle_p2p_confirmation_receipt (void *cls, + const struct GNUNET_PeerIdentity *other, + const struct GNUNET_MessageHeader *message, + const struct GNUNET_ATS_Information *atsi, + unsigned int atsi_count) +{ + const struct P2PConfirmationReceiptMessage *p2p_crmsg; + struct P2PConfirmationReceiptMessage *my_p2p_crmsg; + struct ConfirmationReceiptMessage *crmsg; + struct ChatClient *target; + struct ChatClient *author; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Got P2P confirmation receipt\n"); + p2p_crmsg = (const struct P2PConfirmationReceiptMessage *) message; + target = client_list_head; + while ((NULL != target) && + (0 != + memcmp (&target->id, &p2p_crmsg->target, sizeof (GNUNET_HashCode)))) + target = target->next; + if (NULL == target) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unknown source of the receipt. Rejecting the message\n"); + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + if (target->rcpt_sequence_number >= ntohl (p2p_crmsg->sequence_number)) + { +#if DEBUG_CHAT_SERVICE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "This receipt has already been handled." + " Sequence numbers (msg/sender): %u/%u\n", + ntohl (p2p_crmsg->sequence_number), + target->rcpt_sequence_number); +#endif + return GNUNET_OK; + } + target->rcpt_sequence_number = ntohl (p2p_crmsg->sequence_number); + author = client_list_head; + while ((NULL != author) && + (0 != + memcmp (&author->id, &p2p_crmsg->author, sizeof (GNUNET_HashCode)))) + author = author->next; + if (NULL == author) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unknown addressee. Rejecting the receipt\n"); + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + + if (NULL == author->client) + { +#if DEBUG_CHAT_SERVICE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "The author of the original message is not a local client." + " Broadcasting receipt to neighbour peers\n"); +#endif + my_p2p_crmsg = + GNUNET_memdup (p2p_crmsg, + sizeof (struct P2PConfirmationReceiptMessage)); + GNUNET_CONTAINER_multihashmap_iterate (connected_peers, + &send_confirmation_receipt, + my_p2p_crmsg); + GNUNET_free (my_p2p_crmsg); + } + else + { +#if DEBUG_CHAT_SERVICE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "The author of the original message is a local client." + " Verifying signature of the receipt\n"); +#endif + crmsg = GNUNET_malloc (sizeof (struct ConfirmationReceiptMessage)); + crmsg->header.size = htons (sizeof (struct ConfirmationReceiptMessage)); + crmsg->header.type = + htons (GNUNET_MESSAGE_TYPE_CHAT_CONFIRMATION_NOTIFICATION); + crmsg->signature = p2p_crmsg->signature; + crmsg->purpose = p2p_crmsg->purpose; + crmsg->sequence_number = p2p_crmsg->msg_sequence_number; + crmsg->reserved2 = 0; + crmsg->timestamp = p2p_crmsg->timestamp; + crmsg->target = p2p_crmsg->target; + crmsg->author = p2p_crmsg->author; + crmsg->content = p2p_crmsg->content; + if (GNUNET_OK != + GNUNET_CRYPTO_rsa_verify (GNUNET_SIGNATURE_PURPOSE_CHAT_RECEIPT, + &crmsg->purpose, &crmsg->signature, + &target->public_key)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Invalid signature of the receipt\n"); + GNUNET_break_op (0); + return GNUNET_SYSERR; + } +#if DEBUG_CHAT_SERVICE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "The author of the original message is a local client." + " Sending receipt to the client\n"); +#endif + GNUNET_SERVER_notification_context_unicast (nc, author->client, + &crmsg->header, GNUNET_NO); + GNUNET_free (crmsg); + } + return GNUNET_OK; +} + + +/** + * Transmit a sync request to the peer. + * + * @param cls closure, NULL + * @param size number of bytes available in buf + * @param buf where the callee should write the message + * @return number of bytes written to buf + */ +static size_t +transmit_sync_request_to_peer (void *cls, size_t size, void *buf) +{ + struct GNUNET_MessageHeader *m = buf; + size_t msg_size; + +#if DEBUG_CHAT_SERVICE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Transmitting P2P sync request\n"); +#endif + msg_size = sizeof (struct GNUNET_MessageHeader); + GNUNET_assert (size >= msg_size); + GNUNET_assert (NULL != buf); + m = buf; + m->type = htons (GNUNET_MESSAGE_TYPE_CHAT_P2P_SYNC_REQUEST); + m->size = htons (msg_size); + return msg_size; +} + + +/** + * Method called whenever a peer connects. + * + * @param cls closure + * @param peer peer identity this notification is about + * @param atsi performance data + * @param atsi_count number of entries in atsi + */ +static void +peer_connect_handler (void *cls, const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_ATS_Information *atsi, + unsigned int atsi_count) +{ + struct ConnectedPeer *cp; + struct GNUNET_CORE_TransmitHandle *th; + + if (0 == memcmp (peer, me, sizeof (struct GNUNET_PeerIdentity))) + return; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Peer connected: %s\n", + GNUNET_i2s (peer)); + th = GNUNET_CORE_notify_transmit_ready (core, GNUNET_YES, 1, + MAX_TRANSMIT_DELAY, peer, + sizeof (struct GNUNET_MessageHeader), + &transmit_sync_request_to_peer, NULL); + GNUNET_assert (NULL != th); + cp = GNUNET_CONTAINER_multihashmap_get (connected_peers, &peer->hashPubKey); + if (NULL != cp) + { + GNUNET_break (0); + return; + } + cp = GNUNET_malloc (sizeof (struct ConnectedPeer)); + cp->pid = GNUNET_PEER_intern (peer); + GNUNET_break (GNUNET_OK == + GNUNET_CONTAINER_multihashmap_put (connected_peers, + &peer->hashPubKey, cp, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); +} + + +/** + * Iterator to free peer entries. + * + * @param cls closure, unused + * @param key current key code + * @param value value in the hash map (peer entry) + * @return GNUNET_YES (we should continue to iterate) + */ +static int +clean_peer (void *cls, const GNUNET_HashCode * key, void *value) +{ + struct ConnectedPeer *cp; + const struct GNUNET_PeerIdentity *peer = + (const struct GNUNET_PeerIdentity *) key; + + cp = GNUNET_CONTAINER_multihashmap_get (connected_peers, &peer->hashPubKey); + if (cp == NULL) + return GNUNET_YES; + GNUNET_break (GNUNET_YES == + GNUNET_CONTAINER_multihashmap_remove (connected_peers, + &peer->hashPubKey, cp)); + GNUNET_PEER_change_rc (cp->pid, -1); + GNUNET_free (cp); + return GNUNET_YES; +} + + +/** + * Method called whenever a peer disconnects. + * + * @param cls closure, not used + * @param peer peer identity this notification is about + */ +static void +peer_disconnect_handler (void *cls, const struct GNUNET_PeerIdentity *peer) +{ + + if (0 == memcmp (peer, me, sizeof (struct GNUNET_PeerIdentity))) + return; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Peer disconnected: %s\n", + GNUNET_i2s (peer)); + clean_peer (NULL, (const GNUNET_HashCode *) peer, NULL); +} + + +/** + * Task run during shutdown. + * + * @param cls unused + * @param tc unused + */ +static void +cleanup_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct AnonymousMessage *next_msg; + struct ChatClient *next_client; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Cleaning up\n"); + if (NULL != core) + { + GNUNET_CORE_disconnect (core); + core = NULL; + } + if (NULL != nc) + { + GNUNET_SERVER_notification_context_destroy (nc); + nc = NULL; + } + while (NULL != client_list_head) + { + next_client = client_list_head->next; + GNUNET_free (client_list_head->room); + GNUNET_free_non_null (client_list_head->member_info); + GNUNET_free (client_list_head); + client_list_head = next_client; + } + while (NULL != anonymous_list_head) + { + next_msg = anonymous_list_head->next; + GNUNET_free (anonymous_list_head); + anonymous_list_head = next_msg; + } + GNUNET_CONTAINER_multihashmap_iterate (connected_peers, &clean_peer, NULL); + GNUNET_CONTAINER_multihashmap_destroy (connected_peers); + connected_peers = NULL; +} + + +/** + * To be called on core init/fail. + * + * @param cls closure, NULL + * @param server handle to the server for this service + * @param my_identity the public identity of this peer + */ +static void +core_init (void *cls, struct GNUNET_CORE_Handle *server, + const struct GNUNET_PeerIdentity *my_identity) +{ + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Core initialized\n"); + me = my_identity; +} + + +/** + * Process chat requests. + * + * @param cls closure, NULL + * @param server the initialized server + * @param c configuration to use + */ +static void +run (void *cls, struct GNUNET_SERVER_Handle *server, + const struct GNUNET_CONFIGURATION_Handle *c) +{ + static const struct GNUNET_SERVER_MessageHandler handlers[] = { + {&handle_join_request, NULL, + GNUNET_MESSAGE_TYPE_CHAT_JOIN_REQUEST, 0}, + {&handle_transmit_request, NULL, + GNUNET_MESSAGE_TYPE_CHAT_TRANSMIT_REQUEST, 0}, + {&handle_acknowledge_request, NULL, + GNUNET_MESSAGE_TYPE_CHAT_CONFIRMATION_RECEIPT, + sizeof (struct ConfirmationReceiptMessage)}, + {NULL, NULL, 0, 0} + }; + static const struct GNUNET_CORE_MessageHandler p2p_handlers[] = { + {&handle_p2p_join_notification, + GNUNET_MESSAGE_TYPE_CHAT_P2P_JOIN_NOTIFICATION, 0}, + {&handle_p2p_leave_notification, + GNUNET_MESSAGE_TYPE_CHAT_P2P_LEAVE_NOTIFICATION, + sizeof (struct P2PLeaveNotificationMessage)}, + {&handle_p2p_message_notification, + GNUNET_MESSAGE_TYPE_CHAT_P2P_MESSAGE_NOTIFICATION, 0}, + {&handle_p2p_sync_request, + GNUNET_MESSAGE_TYPE_CHAT_P2P_SYNC_REQUEST, + sizeof (struct GNUNET_MessageHeader)}, + {&handle_p2p_confirmation_receipt, + GNUNET_MESSAGE_TYPE_CHAT_P2P_CONFIRMATION_RECEIPT, + sizeof (struct P2PConfirmationReceiptMessage)}, + {NULL, 0, 0} + }; + + GNUNET_log_setup ("gnunet-service-chat", +#if DEBUG_CHAT_SERVICE + "DEBUG", +#else + "WARNING", +#endif + NULL); + cfg = c; + nc = GNUNET_SERVER_notification_context_create (server, 16); + connected_peers = + GNUNET_CONTAINER_multihashmap_create (EXPECTED_NEIGHBOUR_COUNT); + GNUNET_SERVER_add_handlers (server, handlers); + core = + GNUNET_CORE_connect (cfg, QUEUE_SIZE, NULL, &core_init, + &peer_connect_handler, &peer_disconnect_handler, + NULL, GNUNET_NO, NULL, GNUNET_NO, p2p_handlers); + GNUNET_SERVER_disconnect_notify (server, &handle_client_disconnect, NULL); + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &cleanup_task, + NULL); +} + + +/** + * The main function for the chat service. + * + * @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) +{ + return (GNUNET_OK == + GNUNET_SERVICE_run (argc, argv, "chat", GNUNET_SERVICE_OPTION_NONE, + &run, NULL)) ? 0 : 1; +} + +/* end of gnunet-service-chat.c */ diff --git a/src/chat/test_chat.c b/src/chat/test_chat.c new file mode 100644 index 0000000..fec5db0 --- /dev/null +++ b/src/chat/test_chat.c @@ -0,0 +1,586 @@ +/* + This file is part of GNUnet. + (C) 2005, 2006, 2007, 2008, 2011 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 3, 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file chat/test_chat.c + * @brief base test case for the chat library + * @author Christian Grothoff + * @author Nathan Evans + * @author Vitaly Minko + * + * This test case serves as a base for simple chatting, anonymous chatting, + * authenticated chatting and acknowledgements test cases. Based on the + * executable being run the correct test case will be performed. Private + * chatting is covered by a separate test case since it requires 3 users. + */ + +#include "platform.h" +#include "gnunet_crypto_lib.h" +#include "gnunet_util_lib.h" +#include "gnunet_arm_service.h" +#include "gnunet_chat_service.h" + +#define VERBOSE GNUNET_NO + +#define START_ARM GNUNET_YES + +/** + * How long until we give up on passing the test? + */ +#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60) + +struct PeerContext +{ + struct GNUNET_CONFIGURATION_Handle *cfg; +#if START_ARM + struct GNUNET_OS_Process *arm_proc; +#endif +}; + +struct Wanted +{ + struct GNUNET_CONTAINER_MetaData *meta; + + GNUNET_HashCode *sender; + + char *msg; + + const char *me; + + enum GNUNET_CHAT_MsgOptions opt; + + uint32_t sequence_number; + + struct GNUNET_TIME_Absolute timestamp; + + GNUNET_SCHEDULER_Task next_task; + + void *next_task_cls; + +}; + +static struct PeerContext p1; + +static struct PeerContext p2; + +static GNUNET_HashCode alice; + +static GNUNET_HashCode bob; + +static struct GNUNET_CHAT_Room *alice_room; + +static struct GNUNET_CHAT_Room *bob_room; + +static struct GNUNET_CONTAINER_MetaData *alice_meta; + +static struct GNUNET_CONTAINER_MetaData *bob_meta; + +static struct Wanted alice_wanted; + +static struct Wanted bob_wanted; + +static GNUNET_SCHEDULER_TaskIdentifier kill_task; + +static GNUNET_SCHEDULER_TaskIdentifier wait_task; + +static int err; + +static int is_ready; + +static int is_p2p; + +static int is_ackn; + +static int is_anon; + +static int is_auth; + + +static void +setup_peer (struct PeerContext *p, const char *cfgname) +{ + p->cfg = GNUNET_CONFIGURATION_create (); +#if START_ARM + p->arm_proc = + GNUNET_OS_start_process (GNUNET_YES, NULL, NULL, "gnunet-service-arm", + "gnunet-service-arm", +#if VERBOSE + "-L", "DEBUG", +#endif + "-c", cfgname, NULL); +#endif + GNUNET_assert (GNUNET_OK == GNUNET_CONFIGURATION_load (p->cfg, cfgname)); +} + + +static void +stop_arm (struct PeerContext *p) +{ +#if START_ARM + if (0 != GNUNET_OS_process_kill (p->arm_proc, SIGTERM)) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill"); + if (GNUNET_OS_process_wait (p->arm_proc) != GNUNET_OK) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "waitpid"); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "ARM process %u stopped\n", + GNUNET_OS_process_get_pid (p->arm_proc)); + GNUNET_OS_process_close (p->arm_proc); + p->arm_proc = NULL; +#endif + GNUNET_CONFIGURATION_destroy (p->cfg); +} + + +static void +abort_test (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + if (alice_room != NULL) + { + GNUNET_CHAT_leave_room (alice_room); + alice_room = NULL; + } + if (bob_room != NULL) + { + GNUNET_CHAT_leave_room (bob_room); + bob_room = NULL; + } + err = 1; +} + + +static void +timeout_kill (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ +#if VERBOSE + printf ("Timed out, stopping the test.\n"); +#endif + kill_task = GNUNET_SCHEDULER_NO_TASK; + if (wait_task != GNUNET_SCHEDULER_NO_TASK) + { + GNUNET_SCHEDULER_cancel (wait_task); + wait_task = GNUNET_SCHEDULER_NO_TASK; + } + GNUNET_SCHEDULER_add_continuation (&abort_test, NULL, + GNUNET_SCHEDULER_REASON_PREREQ_DONE); +} + + +static int +join_cb (void *cls) +{ + struct Wanted *want = cls; + +#if VERBOSE + printf ("%s has joined\n", want->me); +#endif + if (NULL != want->next_task) + GNUNET_SCHEDULER_add_now (want->next_task, want->next_task_cls); + return GNUNET_OK; +} + + +static int +member_list_cb (void *cls, const struct GNUNET_CONTAINER_MetaData *member_info, + const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *member_id, + enum GNUNET_CHAT_MsgOptions options) +{ + struct Wanted *want = cls; + GNUNET_HashCode sender; + +#if VERBOSE + printf ("%s - told that %s has %s\n", want->me, + member_info == + NULL ? NULL : GNUNET_CONTAINER_meta_data_get_by_type (member_info, + EXTRACTOR_METATYPE_TITLE), + member_info == NULL ? "left" : "joined"); +#endif + GNUNET_CRYPTO_hash (member_id, + sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded), + &sender); + if ((0 == memcmp (&sender, want->sender, sizeof (GNUNET_HashCode))) && + (((member_info == NULL) && (want->meta == NULL)) || + ((member_info != NULL) && (want->meta != NULL) && + (GNUNET_CONTAINER_meta_data_test_equal (member_info, want->meta)))) && + (options == want->opt)) + { + if (NULL != want->next_task) + GNUNET_SCHEDULER_add_now (want->next_task, want->next_task_cls); + } + else + { + GNUNET_SCHEDULER_cancel (kill_task); + kill_task = GNUNET_SCHEDULER_NO_TASK; + GNUNET_SCHEDULER_add_now (&abort_test, NULL); + } + return GNUNET_OK; +} + + +static int +receive_cb (void *cls, struct GNUNET_CHAT_Room *room, + const GNUNET_HashCode * sender, + const struct GNUNET_CONTAINER_MetaData *meta, const char *message, + struct GNUNET_TIME_Absolute timestamp, + enum GNUNET_CHAT_MsgOptions options) +{ + struct Wanted *want = cls; + +#if VERBOSE + printf ("%s - told that %s said %s\n", want->me, + meta == NULL ? NULL : GNUNET_CONTAINER_meta_data_get_by_type (meta, + EXTRACTOR_METATYPE_TITLE), + message); +#endif + if ((0 == strcmp (message, want->msg)) && + (((sender == NULL) && (want->sender == NULL)) || + ((sender != NULL) && (want->sender != NULL) && + (0 == memcmp (sender, want->sender, sizeof (GNUNET_HashCode))))) && + (GNUNET_CONTAINER_meta_data_test_equal (meta, want->meta)) && + (options == want->opt) && + /* Not == since the library sets the actual timestamp, so it may be + * slightly greater + */ + (timestamp.abs_value >= want->timestamp.abs_value)) + { + if (NULL != want->next_task) + GNUNET_SCHEDULER_add_now (want->next_task, want->next_task_cls); + } + else + { + GNUNET_SCHEDULER_cancel (kill_task); + kill_task = GNUNET_SCHEDULER_NO_TASK; + GNUNET_SCHEDULER_add_now (&abort_test, NULL); + } + return GNUNET_OK; +} + + +static int +confirmation_cb (void *cls, struct GNUNET_CHAT_Room *room, + uint32_t orig_seq_number, + struct GNUNET_TIME_Absolute timestamp, + const GNUNET_HashCode * receiver) +{ + struct Wanted *want = cls; + +#if VERBOSE + printf ("%s - told that %s acknowledged message #%d\n", want->me, + GNUNET_CONTAINER_meta_data_get_by_type (want->meta, + EXTRACTOR_METATYPE_TITLE), + orig_seq_number); +#endif + if ((0 == memcmp (receiver, want->sender, sizeof (GNUNET_HashCode))) && + (orig_seq_number == want->sequence_number) && + (timestamp.abs_value >= want->timestamp.abs_value)) + { + if (NULL != want->next_task) + GNUNET_SCHEDULER_add_now (want->next_task, want->next_task_cls); + } + else + { + GNUNET_SCHEDULER_cancel (kill_task); + kill_task = GNUNET_SCHEDULER_NO_TASK; + GNUNET_SCHEDULER_add_now (&abort_test, NULL); + } + return GNUNET_OK; +} + + +static void +wait_until_ready (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + GNUNET_SCHEDULER_Task task = cls; + +#if VERBOSE + printf ("Waiting...\n"); +#endif + if (is_ready) + { + wait_task = GNUNET_SCHEDULER_NO_TASK; + GNUNET_SCHEDULER_add_now (task, NULL); + } + else + wait_task = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_MILLISECONDS, 50), + &wait_until_ready, task); +} + + +static void +disconnect_alice (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ +#if VERBOSE + printf ("Alice is leaving.\n"); +#endif + if (is_p2p) + stop_arm (&p2); + GNUNET_CHAT_leave_room (alice_room); + alice_room = NULL; + GNUNET_SCHEDULER_cancel (kill_task); + kill_task = GNUNET_SCHEDULER_NO_TASK; +} + + +static void +disconnect_bob (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ +#if VERBOSE + printf ("Bod is leaving.\n"); +#endif + alice_wanted.meta = NULL; + alice_wanted.sender = &bob; + alice_wanted.msg = NULL; + alice_wanted.opt = 0; + alice_wanted.next_task = &disconnect_alice; + alice_wanted.next_task_cls = NULL; + GNUNET_CHAT_leave_room (bob_room); + bob_room = NULL; +} + + +static void +set_ready (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + is_ready = GNUNET_YES; +} + + +static void +send_to_alice (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ +#if VERBOSE + printf ("Bob says 'Hi!'\n"); +#endif + + alice_wanted.meta = bob_meta; + alice_wanted.sender = &bob; + alice_wanted.msg = "Hi Alice!"; + alice_wanted.opt = GNUNET_CHAT_MSG_OPTION_NONE; + alice_wanted.timestamp = GNUNET_TIME_absolute_get (); + alice_wanted.next_task = &disconnect_bob; + alice_wanted.next_task_cls = NULL; + GNUNET_CHAT_send_message (bob_room, "Hi Alice!", GNUNET_CHAT_MSG_OPTION_NONE, + NULL, NULL); +} + + +static void +send_to_bob (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + enum GNUNET_CHAT_MsgOptions options; + uint32_t *seq = NULL; + +#if VERBOSE + printf ("Alice says 'Hi!'\n"); +#endif + if (is_ackn) + { + options = GNUNET_CHAT_MSG_ACKNOWLEDGED; + alice_wanted.meta = bob_meta; + alice_wanted.sender = &bob; + alice_wanted.timestamp = GNUNET_TIME_absolute_get (); + alice_wanted.next_task = &disconnect_bob; + alice_wanted.next_task_cls = NULL; + bob_wanted.meta = alice_meta; + bob_wanted.sender = &alice; + bob_wanted.next_task = NULL; + seq = &(alice_wanted.sequence_number); + } + else if (is_anon) + { + options = GNUNET_CHAT_MSG_ANONYMOUS; + bob_wanted.meta = NULL; + bob_wanted.sender = NULL; + bob_wanted.next_task = &disconnect_bob; + } + else if (is_auth) + { + options = GNUNET_CHAT_MSG_AUTHENTICATED; + bob_wanted.meta = alice_meta; + bob_wanted.sender = &alice; + bob_wanted.next_task = &disconnect_bob; + } + else + { + options = GNUNET_CHAT_MSG_OPTION_NONE; + bob_wanted.meta = alice_meta; + bob_wanted.sender = &alice; + bob_wanted.next_task = &send_to_alice; + } + bob_wanted.msg = "Hi Bob!"; + bob_wanted.opt = options; + bob_wanted.timestamp = GNUNET_TIME_absolute_get (); + bob_wanted.next_task_cls = NULL; + GNUNET_CHAT_send_message (alice_room, "Hi Bob!", options, NULL, seq); +} + + +static void +prepare_for_alice_task (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + bob_wanted.meta = alice_meta; + bob_wanted.sender = &alice; + bob_wanted.msg = NULL; + bob_wanted.opt = -1; + bob_wanted.next_task = &set_ready; + bob_wanted.next_task_cls = NULL; +} + + +static void +join_bob_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ +#if VERBOSE + printf ("Bob joining\n"); +#endif + alice_wanted.meta = bob_meta; + alice_wanted.sender = &bob; + alice_wanted.msg = NULL; + alice_wanted.opt = -1; + alice_wanted.next_task = &wait_until_ready; + alice_wanted.next_task_cls = &send_to_bob; + bob_wanted.next_task = &prepare_for_alice_task; + bob_wanted.next_task_cls = NULL; + is_ready = GNUNET_NO; + bob_room = + GNUNET_CHAT_join_room (is_p2p ? p2.cfg : p1.cfg, "bob", bob_meta, "test", + -1, &join_cb, &bob_wanted, &receive_cb, + &bob_wanted, &member_list_cb, &bob_wanted, + &confirmation_cb, &bob_wanted, &bob); + if (NULL == bob_room) + { + GNUNET_SCHEDULER_cancel (kill_task); + kill_task = GNUNET_SCHEDULER_NO_TASK; + GNUNET_CHAT_leave_room (alice_room); + alice_room = NULL; + err = 1; + } +} + + +static void +join_alice_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ +#if VERBOSE + printf ("Alice joining\n"); +#endif + alice_wanted.next_task = &join_bob_task; + alice_wanted.next_task_cls = NULL; + alice_room = + GNUNET_CHAT_join_room (p1.cfg, "alice", alice_meta, "test", -1, &join_cb, + &alice_wanted, &receive_cb, &alice_wanted, + &member_list_cb, &alice_wanted, &confirmation_cb, + &alice_wanted, &alice); + if (NULL == alice_room) + { + GNUNET_SCHEDULER_cancel (kill_task); + kill_task = GNUNET_SCHEDULER_NO_TASK; + err = 1; + } +} + + +static void +run (void *cls, char *const *args, const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + if (is_p2p) + { + setup_peer (&p1, "test_chat_peer1.conf"); + setup_peer (&p2, "test_chat_peer2.conf"); + } + else + setup_peer (&p1, "test_chat_data.conf"); + + memset (&alice_wanted, 0, sizeof (struct Wanted)); + memset (&bob_wanted, 0, sizeof (struct Wanted)); + alice_wanted.me = "Alice"; + bob_wanted.me = "Bob"; + alice_meta = GNUNET_CONTAINER_meta_data_create (); + GNUNET_CONTAINER_meta_data_insert (alice_meta, "<gnunet>", + EXTRACTOR_METATYPE_TITLE, + EXTRACTOR_METAFORMAT_UTF8, "text/plain", + "Alice", strlen ("Alice") + 1); + bob_meta = GNUNET_CONTAINER_meta_data_create (); + GNUNET_CONTAINER_meta_data_insert (bob_meta, "<gnunet>", + EXTRACTOR_METATYPE_TITLE, + EXTRACTOR_METAFORMAT_UTF8, "text/plain", + "Bob", strlen ("Bob") + 1); + kill_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, &timeout_kill, NULL); + GNUNET_SCHEDULER_add_now (&join_alice_task, NULL); +} + + +int +main (int argc, char *argv[]) +{ + char *const argvx[] = { + "test-chat", + "-c", + "test_chat_data.conf", +#if VERBOSE + "-L", "DEBUG", +#endif + NULL + }; + struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_OPTION_END + }; + + GNUNET_log_setup ("test_chat", +#if VERBOSE + "DEBUG", +#else + "WARNING", +#endif + NULL); + if (strstr (argv[0], "p2p") != NULL) + { + is_p2p = GNUNET_YES; + } + if (strstr (argv[0], "acknowledgment") != NULL) + { + is_ackn = GNUNET_YES; + } + else if (strstr (argv[0], "anonymous") != NULL) + { + is_anon = GNUNET_YES; + } + else if (strstr (argv[0], "authentication") != NULL) + { + is_auth = GNUNET_YES; + } + GNUNET_PROGRAM_run ((sizeof (argvx) / sizeof (char *)) - 1, argvx, + "test-chat", "nohelp", options, &run, NULL); + stop_arm (&p1); + GNUNET_CONTAINER_meta_data_destroy (alice_meta); + GNUNET_CONTAINER_meta_data_destroy (bob_meta); + if (is_p2p) + { + GNUNET_DISK_directory_remove ("/tmp/gnunet-test-chat-peer-1/"); + GNUNET_DISK_directory_remove ("/tmp/gnunet-test-chat-peer-2/"); + } + else + GNUNET_DISK_directory_remove ("/tmp/gnunet-test-chat/"); + return err; +} + +/* end of test_chat.c */ diff --git a/src/chat/test_chat_data.conf b/src/chat/test_chat_data.conf new file mode 100644 index 0000000..3052a4f --- /dev/null +++ b/src/chat/test_chat_data.conf @@ -0,0 +1,56 @@ +[PATHS] +SERVICEHOME = /tmp/gnunet-test-chat/ +DEFAULTCONFIG = test_chat_data.conf + +[gnunetd] +HOSTKEY = $SERVICEHOME/.hostkey + +[resolver] +PORT = 42464 +HOSTNAME = localhost + +[transport] +PORT = 42465 +PLUGINS = + +[arm] +PORT = 42466 +HOSTNAME = localhost +DEFAULTSERVICES = core chat + +[peerinfo] +PORT = 42469 +HOSTNAME = localhost + +[core] +PORT = 42470 +HOSTNAME = localhost + +[chat] +PORT = 42471 +HOSTNAME = localhost +HOME = $SERVICEHOME +CONFIG = $DEFAULTCONFIG +BINARY = gnunet-service-chat + +[testing] +WEAKRANDOM = YES + +[nat] +DISABLEV6 = YES +BINDTO = 127.0.0.1 +ENABLE_UPNP = NO +BEHIND_NAT = NO +ALLOW_NAT = NO +INTERNAL_ADDRESS = 127.0.0.1 +EXTERNAL_ADDRESS = 127.0.0.1 + +[dns] +AUTOSTART = NO + + + +[nse] +AUTOSTART = NO + + diff --git a/src/chat/test_chat_peer1.conf b/src/chat/test_chat_peer1.conf new file mode 100644 index 0000000..73280da --- /dev/null +++ b/src/chat/test_chat_peer1.conf @@ -0,0 +1,95 @@ +[PATHS] +SERVICEHOME = /tmp/gnunet-test-chat-peer-1/ +DEFAULTCONFIG = test_chat_peer1.conf + +[gnunetd] +HOSTKEY = $SERVICEHOME/.hostkey + +[hostlist] +HTTPPORT = 31000 +OPTIONS = -p + +[resolver] +PORT = 31001 +HOSTNAME = localhost +UNIXPATH = /tmp/gnunet-chat-p1-service-resolver.sock + +[transport] +PORT = 31002 +UNIXPATH = /tmp/gnunet-chat-p1-service-transport.sock +PLUGINS = tcp +#BINARY = /home/grothoff/bin/gnunet-service-transport +#PREFIX = valgrind + +[transport-tcp] +PORT = 31003 + +[arm] +PORT = 31004 +UNIXPATH = /tmp/gnunet-chat-p1-service-arm.sock +HOSTNAME = localhost +DEFAULTSERVICES = resolver transport core topology hostlist statistics chat + +[core] +PORT = 31005 +UNIXPATH = /tmp/gnunet-chat-p1-service-core.sock +HOSTNAME = localhost + +[topology] +MINIMUM-FRIENDS = 0 +FRIENDS-ONLY = NO +AUTOCONNECT = YES +TARGET-CONNECTION-COUNT = 16 +FRIENDS = $SERVICEHOME/friends +CONFIG = $DEFAULTCONFIG +BINARY = gnunet-daemon-topology + +[peerinfo] +PORT = 31006 +UNIXPATH = /tmp/gnunet-chat-p1-service-peerinfo.sock +HOSTNAME = localhost + +[statistics] +PORT = 31007 +HOSTNAME = localhost +UNIXPATH = /tmp/gnunet-chat-p1-service-statistics.sock + +[chat] +PORT = 31008 +HOSTNAME = localhost +HOME = $SERVICEHOME +CONFIG = $DEFAULTCONFIG +BINARY = gnunet-service-chat + +[testing] +WEAKRANDOM = YES + +[fs] +AUTOSTART = NO + +[datastore] +AUTOSTART = NO + +[dht] +AUTOSTART = NO + +[mesh] +AUTOSTART = NO +[nat] +DISABLEV6 = YES +BINDTO = 127.0.0.1 +ENABLE_UPNP = NO +BEHIND_NAT = NO +ALLOW_NAT = NO +INTERNAL_ADDRESS = 127.0.0.1 +EXTERNAL_ADDRESS = 127.0.0.1 + +[dns] +AUTOSTART = NO + +[nse] +AUTOSTART = NO + +[ats] +PORT = 31971 +UNIXPATH = /tmp/gnunet-chat-p1-service-ats.sock diff --git a/src/chat/test_chat_peer2.conf b/src/chat/test_chat_peer2.conf new file mode 100644 index 0000000..00808b5 --- /dev/null +++ b/src/chat/test_chat_peer2.conf @@ -0,0 +1,97 @@ +[PATHS] +SERVICEHOME = /tmp/gnunet-test-chat-peer-2/ +DEFAULTCONFIG = test_chat_peer2.conf + +[gnunetd] +HOSTKEY = $SERVICEHOME/.hostkey + +[hostlist] +SERVERS = http://localhost:31000/ +OPTIONS = -b + +[resolver] +PORT = 32001 +HOSTNAME = localhost +UNIXPATH = /tmp/gnunet-chat-p2-service-resolver.sock + +[transport] +PORT = 32002 +UNIXPATH = /tmp/gnunet-chat-p2-service-transport.sock +PLUGINS = tcp +#BINARY = /home/grothoff/bin/gnunet-service-transport +#PREFIX = valgrind + +[transport-tcp] +PORT = 32003 + +[arm] +PORT = 32004 +UNIXPATH = /tmp/gnunet-chat-p2-service-arm.sock +HOSTNAME = localhost +DEFAULTSERVICES = resolver transport core topology hostlist statistics chat + +[core] +PORT = 32005 +UNIXPATH = /tmp/gnunet-chat-p2-service-core.sock +HOSTNAME = localhost + +[topology] +MINIMUM-FRIENDS = 0 +FRIENDS-ONLY = NO +AUTOCONNECT = YES +TARGET-CONNECTION-COUNT = 16 +FRIENDS = $SERVICEHOME/friends +CONFIG = $DEFAULTCONFIG +BINARY = gnunet-daemon-topology + +[peerinfo] +PORT = 32006 +UNIXPATH = /tmp/gnunet-chat-p2-service-peerinfo.sock +HOSTNAME = localhost + +[statistics] +PORT = 32007 +HOSTNAME = localhost +UNIXPATH = /tmp/gnunet-chat-p2-service-statistics.sock + +[chat] +PORT = 32008 +HOSTNAME = localhost +HOME = $SERVICEHOME +CONFIG = $DEFAULTCONFIG +BINARY = gnunet-service-chat + +[testing] +WEAKRANDOM = YES + +[fs] +AUTOSTART = NO + +[datastore] +AUTOSTART = NO + +[dht] +AUTOSTART = NO + +[mesh] +AUTOSTART = NO + +[nat] +DISABLEV6 = YES +BINDTO = 127.0.0.1 +ENABLE_UPNP = NO +BEHIND_NAT = NO +ALLOW_NAT = NO +INTERNAL_ADDRESS = 127.0.0.1 +EXTERNAL_ADDRESS = 127.0.0.1 + +[dns] +AUTOSTART = NO + +[nse] +AUTOSTART = NO + +[ats] +PORT = 32971 +UNIXPATH = /tmp/gnunet-chat-p2-service-ats.sock + diff --git a/src/chat/test_chat_peer3.conf b/src/chat/test_chat_peer3.conf new file mode 100644 index 0000000..a394523 --- /dev/null +++ b/src/chat/test_chat_peer3.conf @@ -0,0 +1,96 @@ +[PATHS] +SERVICEHOME = /tmp/gnunet-test-chat-peer-3/ +DEFAULTCONFIG = test_chat_peer3.conf + +[gnunetd] +HOSTKEY = $SERVICEHOME/.hostkey + +[hostlist] +SERVERS = http://localhost:31000/ +OPTIONS = -b + +[resolver] +PORT = 33001 +HOSTNAME = localhost +UNIXPATH = /tmp/gnunet-chat-p3-service-resolver.sock + +[transport] +PORT = 33002 +UNIXPATH = /tmp/gnunet-chat-p3-service-transport.sock +PLUGINS = tcp +#BINARY = /home/grothoff/bin/gnunet-service-transport +#PREFIX = valgrind + +[transport-tcp] +PORT = 33003 + +[arm] +PORT = 33004 +UNIXPATH = /tmp/gnunet-chat-p3-service-arm.sock +HOSTNAME = localhost +DEFAULTSERVICES = resolver transport core topology hostlist statistics chat + +[core] +PORT = 33005 +UNIXPATH = /tmp/gnunet-chat-p3-service-core.sock +HOSTNAME = localhost + +[topology] +MINIMUM-FRIENDS = 0 +FRIENDS-ONLY = NO +AUTOCONNECT = YES +TARGET-CONNECTION-COUNT = 16 +FRIENDS = $SERVICEHOME/friends +CONFIG = $DEFAULTCONFIG +BINARY = gnunet-daemon-topology + +[peerinfo] +PORT = 33006 +UNIXPATH = /tmp/gnunet-chat-p3-service-peerinfo.sock +HOSTNAME = localhost + +[statistics] +PORT = 33007 +HOSTNAME = localhost +UNIXPATH = /tmp/gnunet-chat-p3-service-statistics.sock + +[chat] +PORT = 33008 +HOSTNAME = localhost +HOME = $SERVICEHOME +CONFIG = $DEFAULTCONFIG +BINARY = gnunet-service-chat + +[testing] +WEAKRANDOM = YES + +[fs] +AUTOSTART = NO + +[datastore] +AUTOSTART = NO + +[dht] +AUTOSTART = NO + +[mesh] +AUTOSTART = NO + +[nat] +DISABLEV6 = YES +BINDTO = 127.0.0.1 +ENABLE_UPNP = NO +BEHIND_NAT = NO +ALLOW_NAT = NO +INTERNAL_ADDRESS = 127.0.0.1 +EXTERNAL_ADDRESS = 127.0.0.1 + +[dns] +AUTOSTART = NO + + + +[nse] +AUTOSTART = NO + + diff --git a/src/chat/test_chat_private.c b/src/chat/test_chat_private.c new file mode 100644 index 0000000..cbc9065 --- /dev/null +++ b/src/chat/test_chat_private.c @@ -0,0 +1,654 @@ +/* + This file is part of GNUnet. + (C) 2011 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 3, 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file chat/test_chat_private.c + * @brief testcase for private chatting + * @author Vitaly Minko + */ + +#include "platform.h" +#include "gnunet_crypto_lib.h" +#include "gnunet_util_lib.h" +#include "gnunet_arm_service.h" +#include "gnunet_chat_service.h" + +#define VERBOSE GNUNET_NO + +#define START_ARM GNUNET_YES + +/** + * How long until we give up on passing the test? + */ +#define KILL_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60) + +/** + * How long until we give up on receiving somebody else's private message? + */ +#define PM_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5) + +struct PeerContext +{ + struct GNUNET_CONFIGURATION_Handle *cfg; +#if START_ARM + struct GNUNET_OS_Process *arm_proc; +#endif +}; + +struct Wanted +{ + struct GNUNET_CONTAINER_MetaData *meta; + + GNUNET_HashCode *sender; + + /** + * Alternative meta/sender is used when we expect join/leave notification + * from two peers and don't know which one will come first. + */ + struct GNUNET_CONTAINER_MetaData *meta2; + + GNUNET_HashCode *sender2; + + char *msg; + + const char *me; + + enum GNUNET_CHAT_MsgOptions opt; + + struct GNUNET_TIME_Absolute timestamp; + + GNUNET_SCHEDULER_Task next_task; + + void *next_task_cls; + +}; + +static struct PeerContext p1; + +static struct PeerContext p2; + +static struct PeerContext p3; + +static GNUNET_HashCode alice; + +static GNUNET_HashCode bob; + +static GNUNET_HashCode carol; + +static struct GNUNET_CHAT_Room *alice_room; + +static struct GNUNET_CHAT_Room *bob_room; + +static struct GNUNET_CHAT_Room *carol_room; + +static struct GNUNET_CONTAINER_MetaData *alice_meta; + +static struct GNUNET_CONTAINER_MetaData *bob_meta; + +static struct GNUNET_CONTAINER_MetaData *carol_meta; + +static struct Wanted alice_wanted; + +static struct Wanted bob_wanted; + +static struct Wanted carol_wanted; + +static GNUNET_SCHEDULER_TaskIdentifier kill_task; + +static GNUNET_SCHEDULER_TaskIdentifier finish_task; + +static GNUNET_SCHEDULER_TaskIdentifier wait_task; + +static int err; + +static int alice_ready; + +static int bob_ready; + +static int is_p2p; + +struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *bob_public_key = NULL; + + +static void +setup_peer (struct PeerContext *p, const char *cfgname) +{ + p->cfg = GNUNET_CONFIGURATION_create (); +#if START_ARM + p->arm_proc = + GNUNET_OS_start_process (GNUNET_YES, NULL, NULL, "gnunet-service-arm", + "gnunet-service-arm", +#if VERBOSE + "-L", "DEBUG", +#endif + "-c", cfgname, NULL); +#endif + GNUNET_assert (GNUNET_OK == GNUNET_CONFIGURATION_load (p->cfg, cfgname)); +} + + +static void +stop_arm (struct PeerContext *p) +{ +#if START_ARM + if (0 != GNUNET_OS_process_kill (p->arm_proc, SIGTERM)) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill"); + if (GNUNET_OS_process_wait (p->arm_proc) != GNUNET_OK) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "waitpid"); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "ARM process %u stopped\n", + GNUNET_OS_process_get_pid (p->arm_proc)); + GNUNET_OS_process_close (p->arm_proc); + p->arm_proc = NULL; +#endif + GNUNET_CONFIGURATION_destroy (p->cfg); +} + + +static void +abort_test (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + if (alice_room != NULL) + { + GNUNET_CHAT_leave_room (alice_room); + alice_room = NULL; + } + if (bob_room != NULL) + { + GNUNET_CHAT_leave_room (bob_room); + bob_room = NULL; + } + if (carol_room != NULL) + { + GNUNET_CHAT_leave_room (carol_room); + carol_room = NULL; + } + err = 1; +} + + +static void +timeout_kill (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ +#if VERBOSE + printf ("Timed out, stopping the test.\n"); +#endif + kill_task = GNUNET_SCHEDULER_NO_TASK; + if (wait_task != GNUNET_SCHEDULER_NO_TASK) + { + GNUNET_SCHEDULER_cancel (wait_task); + wait_task = GNUNET_SCHEDULER_NO_TASK; + } + GNUNET_SCHEDULER_add_continuation (&abort_test, NULL, + GNUNET_SCHEDULER_REASON_PREREQ_DONE); +} + + +static int +join_cb (void *cls) +{ + struct Wanted *want = cls; + +#if VERBOSE + printf ("%s has joined\n", want->me); +#endif + if (NULL != want->next_task) + GNUNET_SCHEDULER_add_now (want->next_task, want->next_task_cls); + return GNUNET_OK; +} + + +static int +member_list_cb (void *cls, const struct GNUNET_CONTAINER_MetaData *member_info, + const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *member_id, + enum GNUNET_CHAT_MsgOptions options) +{ + struct Wanted *want = cls; + GNUNET_HashCode sender; + +#if VERBOSE + printf ("%s - told that %s has %s\n", want->me, + member_info == + NULL ? NULL : GNUNET_CONTAINER_meta_data_get_by_type (member_info, + EXTRACTOR_METATYPE_TITLE), + member_info == NULL ? "left" : "joined"); +#endif + GNUNET_CRYPTO_hash (member_id, + sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded), + &sender); + /* entertain both primary and an alternative sender/meta */ + if (((0 == memcmp (&sender, want->sender, sizeof (GNUNET_HashCode))) || + ((want->sender2 != NULL) && + (0 == memcmp (&sender, want->sender2, sizeof (GNUNET_HashCode))))) && + (((member_info == NULL) && (want->meta == NULL)) || + ((member_info != NULL) && + (((want->meta != NULL) && + GNUNET_CONTAINER_meta_data_test_equal (member_info, want->meta)) || + ((want->meta2 != NULL) && + GNUNET_CONTAINER_meta_data_test_equal (member_info, want->meta2))))) + && (options == want->opt)) + { + /* remember Bob's public key, we need it to send private message */ + if (NULL == bob_public_key && + (0 == memcmp (&bob, want->sender, sizeof (GNUNET_HashCode)))) + bob_public_key = + GNUNET_memdup (member_id, + sizeof (struct + GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded)); + if (want->sender2 != NULL) + { + /* flush alternative sender */ + if (0 == memcmp (&sender, want->sender, sizeof (GNUNET_HashCode))) + { + want->sender = want->sender2; + want->meta = want->meta2; + } + want->sender2 = NULL; + want->meta2 = NULL; + } + else if (NULL != want->next_task) + GNUNET_SCHEDULER_add_now (want->next_task, want->next_task_cls); + } + else + { + GNUNET_SCHEDULER_cancel (kill_task); + kill_task = GNUNET_SCHEDULER_NO_TASK; + GNUNET_SCHEDULER_add_now (&abort_test, NULL); + } + return GNUNET_OK; +} + + +static int +receive_cb (void *cls, struct GNUNET_CHAT_Room *room, + const GNUNET_HashCode * sender, + const struct GNUNET_CONTAINER_MetaData *meta, const char *message, + struct GNUNET_TIME_Absolute timestamp, + enum GNUNET_CHAT_MsgOptions options) +{ + struct Wanted *want = cls; + +#if VERBOSE + printf ("%s - told that %s said '%s'\n", want->me, + meta == NULL ? NULL : GNUNET_CONTAINER_meta_data_get_by_type (meta, + EXTRACTOR_METATYPE_TITLE), + message); +#endif + + if ((want->msg != NULL) && (0 == strcmp (message, want->msg)) && + (((sender == NULL) && (want->sender == NULL)) || + ((sender != NULL) && (want->sender != NULL) && + (0 == memcmp (sender, want->sender, sizeof (GNUNET_HashCode))))) && + (GNUNET_CONTAINER_meta_data_test_equal (meta, want->meta)) && + (options == want->opt) && + /* Not == since the library sets the actual timestamp, so it may be + * slightly greater + */ + (timestamp.abs_value >= want->timestamp.abs_value)) + { + if (NULL != want->next_task) + GNUNET_SCHEDULER_add_now (want->next_task, want->next_task_cls); + } + else + { + GNUNET_SCHEDULER_cancel (kill_task); + kill_task = GNUNET_SCHEDULER_NO_TASK; + GNUNET_SCHEDULER_cancel (finish_task); + finish_task = GNUNET_SCHEDULER_NO_TASK; + GNUNET_SCHEDULER_add_now (&abort_test, NULL); + } + return GNUNET_OK; +} + + +static void +wait_until_all_ready (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + GNUNET_SCHEDULER_Task task = cls; + +#if VERBOSE + printf ("Waiting...\n"); +#endif + if (alice_ready && bob_ready) + { + wait_task = GNUNET_SCHEDULER_NO_TASK; + GNUNET_SCHEDULER_add_now (task, NULL); + } + else + wait_task = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_MILLISECONDS, 5000), + &wait_until_all_ready, task); +} + + +static void +set_alice_ready (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + alice_ready = GNUNET_YES; +} + + +static void +set_bob_ready (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + bob_ready = GNUNET_YES; +} + + +static void +disconnect_alice (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ +#if VERBOSE + printf ("Alice is leaving.\n"); +#endif + if (is_p2p) + stop_arm (&p2); + GNUNET_CHAT_leave_room (alice_room); + alice_room = NULL; + GNUNET_SCHEDULER_cancel (kill_task); + kill_task = GNUNET_SCHEDULER_NO_TASK; +} + + +static void +disconnect_bob (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ +#if VERBOSE + printf ("Bod is leaving.\n"); +#endif + if (is_p2p) + stop_arm (&p3); + alice_wanted.meta = NULL; + alice_wanted.sender = &bob; + alice_wanted.msg = NULL; + alice_wanted.opt = 0; + alice_wanted.next_task = &disconnect_alice; + alice_wanted.next_task_cls = NULL; + GNUNET_CHAT_leave_room (bob_room); + bob_room = NULL; +} + + +static void +disconnect_carol (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ +#if VERBOSE + printf ("Carol is leaving.\n"); +#endif + alice_wanted.meta = NULL; + alice_wanted.sender = &carol; + alice_wanted.msg = NULL; + alice_wanted.opt = 0; + alice_wanted.next_task = &set_alice_ready; + alice_wanted.next_task_cls = NULL; + alice_ready = GNUNET_NO; + bob_wanted.meta = NULL; + bob_wanted.sender = &carol; + bob_wanted.msg = NULL; + bob_wanted.opt = 0; + bob_wanted.next_task = &wait_until_all_ready; + bob_wanted.next_task_cls = &disconnect_bob; + bob_ready = GNUNET_YES; + GNUNET_CHAT_leave_room (carol_room); + carol_room = NULL; +} + + +static void +send_from_alice_to_bob (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + uint32_t seq; + +#if VERBOSE + printf ("Alice says 'Hi!' to Bob\n"); +#endif + alice_ready = GNUNET_YES; + bob_ready = GNUNET_NO; + bob_wanted.meta = alice_meta; + bob_wanted.sender = &alice; + bob_wanted.msg = "Hi Bob!"; + bob_wanted.opt = GNUNET_CHAT_MSG_PRIVATE; + bob_wanted.next_task = &set_bob_ready; + bob_wanted.next_task_cls = NULL; + /* Carol should not receive this message */ + carol_wanted.meta = NULL; + carol_wanted.sender = NULL; + carol_wanted.msg = NULL; + carol_wanted.opt = 0; + carol_wanted.next_task = NULL; + carol_wanted.next_task_cls = NULL; + GNUNET_CHAT_send_message (alice_room, "Hi Bob!", GNUNET_CHAT_MSG_PRIVATE, + bob_public_key, &seq); + finish_task = + GNUNET_SCHEDULER_add_delayed (PM_TIMEOUT, &wait_until_all_ready, + &disconnect_carol); +} + + +static void +prepare_bob_for_alice_task (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + bob_wanted.meta = alice_meta; + bob_wanted.sender = &alice; + bob_wanted.msg = NULL; + bob_wanted.opt = -1; + bob_wanted.next_task = &set_bob_ready; + bob_wanted.next_task_cls = NULL; +} + + +static void +prepare_carol_for_alice_and_bob_task (void *cls, + const struct GNUNET_SCHEDULER_TaskContext + *tc) +{ + carol_wanted.meta = alice_meta; + carol_wanted.sender = &alice; + /* set alternative meta/sender since we don't know from which peer + * notification will come first */ + carol_wanted.meta2 = bob_meta; + carol_wanted.sender2 = &bob; + carol_wanted.msg = NULL; + carol_wanted.opt = -1; + carol_wanted.next_task = &wait_until_all_ready; + carol_wanted.next_task_cls = &send_from_alice_to_bob; +} + + +static void +join_carol_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ +#if VERBOSE + printf ("Carol joining\n"); +#endif + alice_wanted.meta = carol_meta; + alice_wanted.sender = &carol; + alice_wanted.msg = NULL; + alice_wanted.opt = -1; + alice_wanted.next_task = &set_alice_ready; + alice_wanted.next_task_cls = NULL; + alice_ready = GNUNET_NO; + bob_wanted.meta = carol_meta; + bob_wanted.sender = &carol; + bob_wanted.msg = NULL; + bob_wanted.opt = -1; + bob_wanted.next_task = &set_bob_ready; + bob_wanted.next_task_cls = NULL; + bob_ready = GNUNET_NO; + carol_wanted.next_task = &prepare_carol_for_alice_and_bob_task; + carol_wanted.next_task_cls = NULL; + carol_room = + GNUNET_CHAT_join_room (is_p2p ? p3.cfg : p1.cfg, "carol", carol_meta, + "test", -1, &join_cb, &carol_wanted, &receive_cb, + &carol_wanted, &member_list_cb, &carol_wanted, + NULL, NULL, &carol); + if (NULL == carol_room) + { + GNUNET_SCHEDULER_cancel (kill_task); + kill_task = GNUNET_SCHEDULER_NO_TASK; + GNUNET_CHAT_leave_room (alice_room); + alice_room = NULL; + GNUNET_CHAT_leave_room (bob_room); + bob_room = NULL; + err = 1; + } +} + + +static void +join_bob_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ +#if VERBOSE + printf ("Bob joining\n"); +#endif + alice_wanted.meta = bob_meta; + alice_wanted.sender = &bob; + alice_wanted.msg = NULL; + alice_wanted.opt = -1; + alice_wanted.next_task = &wait_until_all_ready; + alice_wanted.next_task_cls = &join_carol_task; + alice_ready = GNUNET_YES; + bob_wanted.next_task = &prepare_bob_for_alice_task; + bob_wanted.next_task_cls = NULL; + bob_ready = GNUNET_NO; + bob_room = + GNUNET_CHAT_join_room (is_p2p ? p2.cfg : p1.cfg, "bob", bob_meta, "test", + -1, &join_cb, &bob_wanted, &receive_cb, + &bob_wanted, &member_list_cb, &bob_wanted, NULL, + NULL, &bob); + if (NULL == bob_room) + { + GNUNET_SCHEDULER_cancel (kill_task); + kill_task = GNUNET_SCHEDULER_NO_TASK; + GNUNET_CHAT_leave_room (alice_room); + alice_room = NULL; + err = 1; + } +} + + +static void +join_alice_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ +#if VERBOSE + printf ("Alice joining\n"); +#endif + alice_wanted.next_task = &join_bob_task; + alice_wanted.next_task_cls = NULL; + alice_room = + GNUNET_CHAT_join_room (p1.cfg, "alice", alice_meta, "test", -1, &join_cb, + &alice_wanted, &receive_cb, &alice_wanted, + &member_list_cb, &alice_wanted, NULL, NULL, + &alice); + if (NULL == alice_room) + { + GNUNET_SCHEDULER_cancel (kill_task); + kill_task = GNUNET_SCHEDULER_NO_TASK; + err = 1; + } +} + + +static void +run (void *cls, char *const *args, const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + if (is_p2p) + { + setup_peer (&p1, "test_chat_peer1.conf"); + setup_peer (&p2, "test_chat_peer2.conf"); + setup_peer (&p3, "test_chat_peer3.conf"); + } + else + setup_peer (&p1, "test_chat_data.conf"); + + memset (&alice_wanted, 0, sizeof (struct Wanted)); + memset (&bob_wanted, 0, sizeof (struct Wanted)); + memset (&carol_wanted, 0, sizeof (struct Wanted)); + alice_wanted.me = "Alice"; + bob_wanted.me = "Bob"; + carol_wanted.me = "Carol"; + alice_meta = GNUNET_CONTAINER_meta_data_create (); + GNUNET_CONTAINER_meta_data_insert (alice_meta, "<gnunet>", + EXTRACTOR_METATYPE_TITLE, + EXTRACTOR_METAFORMAT_UTF8, "text/plain", + "Alice", strlen ("Alice") + 1); + bob_meta = GNUNET_CONTAINER_meta_data_create (); + GNUNET_CONTAINER_meta_data_insert (bob_meta, "<gnunet>", + EXTRACTOR_METATYPE_TITLE, + EXTRACTOR_METAFORMAT_UTF8, "text/plain", + "Bob", strlen ("Bob") + 1); + carol_meta = GNUNET_CONTAINER_meta_data_create (); + GNUNET_CONTAINER_meta_data_insert (carol_meta, "<gnunet>", + EXTRACTOR_METATYPE_TITLE, + EXTRACTOR_METAFORMAT_UTF8, "text/plain", + "Carol", strlen ("Carol") + 1); + kill_task = GNUNET_SCHEDULER_add_delayed (KILL_TIMEOUT, &timeout_kill, NULL); + GNUNET_SCHEDULER_add_now (&join_alice_task, NULL); +} + + +int +main (int argc, char *argv[]) +{ + char *const argvx[] = { + "test-chat", + "-c", + "test_chat_data.conf", +#if VERBOSE + "-L", "DEBUG", +#endif + NULL + }; + struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_OPTION_END + }; + + GNUNET_log_setup ("test_chat", +#if VERBOSE + "DEBUG", +#else + "WARNING", +#endif + NULL); + if (strstr (argv[0], "p2p") != NULL) + { + is_p2p = GNUNET_YES; + } + GNUNET_PROGRAM_run ((sizeof (argvx) / sizeof (char *)) - 1, argvx, + "test-chat", "nohelp", options, &run, NULL); + stop_arm (&p1); + GNUNET_CONTAINER_meta_data_destroy (alice_meta); + GNUNET_CONTAINER_meta_data_destroy (bob_meta); + GNUNET_CONTAINER_meta_data_destroy (carol_meta); + if (is_p2p) + { + GNUNET_DISK_directory_remove ("/tmp/gnunet-test-chat-peer-1/"); + GNUNET_DISK_directory_remove ("/tmp/gnunet-test-chat-peer-2/"); + GNUNET_DISK_directory_remove ("/tmp/gnunet-test-chat-peer-3/"); + } + else + GNUNET_DISK_directory_remove ("/tmp/gnunet-test-chat/"); + return err; +} + +/* end of test_chat_private.c */ |