diff options
author | Simon McVittie <simon.mcvittie@collabora.co.uk> | 2007-04-19 17:17:02 +0000 |
---|---|---|
committer | Simon McVittie <simon.mcvittie@collabora.co.uk> | 2007-04-19 17:17:02 +0000 |
commit | 90720ee40eb30e2b1c9aa90501391d3a0a411250 (patch) | |
tree | e52f63ce4a4144d227f385123fd1ba84e121bdfc | |
parent | 3a7f26b9857e9077635aba41e20a590e448bfe11 (diff) |
Remove src, data, tests and Gabble-specific parts of configure.ac
86 files changed, 24 insertions, 32019 deletions
diff --git a/Makefile.am b/Makefile.am index 6fb4646a7..2dc007505 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,6 +1,6 @@ ACLOCAL_AMFLAGS = -I m4 -SUBDIRS = lib src data m4 tests +SUBDIRS = lib m4 EXTRA_DIST = \ check-coding-style.mk \ @@ -13,6 +13,3 @@ DISTCHECK_CONFIGURE_FLAGS = --enable-gtk-doc --disable-debug dist-hook: chmod u+w ${distdir}/ChangeLog if test -d _darcs; then darcs changes >${distdir}/ChangeLog; fi - -check-extra: check - $(MAKE) -C tests check-extra diff --git a/configure.ac b/configure.ac index 9724da161..c1578ebf9 100644 --- a/configure.ac +++ b/configure.ac @@ -5,12 +5,14 @@ AC_PREREQ([2.59]) # odd minor -> development series # even minor -> stable series # increment micro for each release within a series -# set gabble_release to 1. +# set nano_version to 0 +# make the release, tag it +# set nano_version to 1 -m4_define([gabble_major_version], [0]) -m4_define([gabble_minor_version], [5]) -m4_define([gabble_micro_version], [9]) -m4_define([gabble_nano_version], [1]) +m4_define([tp_glib_major_version], [0]) +m4_define([tp_glib_minor_version], [5]) +m4_define([tp_glib_micro_version], [9]) +m4_define([tp_glib_nano_version], [1]) # If library source has changed since last release, increment revision # If interfaces have been added, removed or changed since last release, @@ -23,13 +25,13 @@ m4_define([tp_glib_lt_revision], [0]) m4_define([tp_glib_lt_age], [0]) # Some magic -m4_define([gabble_base_version], - [gabble_major_version.gabble_minor_version.gabble_micro_version]) -m4_define([gabble_version], - [m4_if(gabble_nano_version, 0, [gabble_base_version], - [gabble_base_version].[gabble_nano_version])]) +m4_define([tp_glib_base_version], + [tp_glib_major_version.tp_glib_minor_version.tp_glib_micro_version]) +m4_define([tp_glib_version], + [m4_if(tp_glib_nano_version, 0, [tp_glib_base_version], + [tp_glib_base_version].[tp_glib_nano_version])]) -AC_INIT([Telepathy Gabble], [gabble_version], +AC_INIT([Telepathy-GLib], [tp_glib_version], [https://bugs.freedesktop.org/enter_bug.cgi?product=Telepathy]) AC_CONFIG_MACRO_DIR([m4]) @@ -64,7 +66,7 @@ AS_COMPILER_FLAG(-Wno-unused-parameter, wno_unused_parameter=yes, wno_unused_parameter=no) -ifelse(gabble_nano_version, 0, [], +ifelse(tp_glib_nano_version, 0, [], [ if test x$werror = xyes; then ERROR_CFLAGS="$ERROR_CFLAGS -Werror" @@ -79,29 +81,29 @@ ifelse(gabble_nano_version, 0, [], AC_SUBST(ERROR_CFLAGS) AC_ARG_ENABLE(debug, - AC_HELP_STRING([--disable-debug],[compile without debug code]), + AC_HELP_STRING([--disable-debug],[compile without debug code (note that this will disable much of the debug code in all GLib connection managers)]), enable_debug=$enableval, enable_debug=yes ) AC_ARG_ENABLE(handle-leak-debug, - AC_HELP_STRING([--enable-handle-leak-debug],[compile with handle leak debugging code]), + AC_HELP_STRING([--enable-handle-leak-debug],[compile with handle leak debugging code (the CM should be built with -rdynamic)]), enable_handle_leak_debug=$enableval, enable_handle_leak_debug=no ) AC_ARG_ENABLE(backtrace, - AC_HELP_STRING([--enable-backtrace],[enable printing out the backtrace in case of crash]), + AC_HELP_STRING([--enable-backtrace],[enable printing out the backtrace in case of crash (in most GLib connection managers)]), enable_backtrace=$enableval, enable_backtrace=no ) AC_ARG_ENABLE(coverage, AC_HELP_STRING([--enable-coverage],[compile with coverage info]), enable_coverage=$enableval, enable_coverage=no ) -ifelse(gabble_nano_version, 0, - [ # Gabble is version x.y.z - disable coding style checks by default +ifelse(tp_glib_nano_version, 0, + [ # tp-glib is version x.y.z - disable coding style checks by default AC_ARG_ENABLE(coding-style-checks, AC_HELP_STRING([--enable-coding-style-checks], [check coding style using grep]), [ENABLE_CODING_STYLE_CHECKS=$enableval], [ENABLE_CODING_STYLE_CHECKS=no] ) ], - [ # Gabble is version x.y.z.1 - enable coding style checks by default + [ # tp-glib is version x.y.z.1 - enable coding style checks by default AC_ARG_ENABLE(coding-style-checks, AC_HELP_STRING([--disable-coding-style-checks], [don't check coding style using grep]), @@ -130,14 +132,11 @@ AC_SUBST(COVERAGE_CFLAGS) AC_SUBST([ENABLE_CODING_STYLE_CHECKS]) -dnl lib/docs/Makefile.am needs to know whether it's an out of tree build +dnl docs/Makefile.am needs to know whether it's an out of tree build dnl (srcdir != builddir) AM_CONDITIONAL([OUT_OF_TREE_BUILD], [test "z$ac_srcdir" != z.]) -dnl Endianness (for the sha1 implementation) -AC_C_BIGENDIAN - -dnl Check for Glib +dnl Check for Glib PKG_CHECK_MODULES(GLIB, [glib-2.0 >= 2.4, gobject-2.0 >= 2.4]) AC_SUBST(GLIB_CFLAGS) @@ -152,11 +151,6 @@ PKG_CHECK_MODULES(DBUS, [dbus-1 >= 0.61, dbus-glib-1 >= 0.72]) AC_SUBST(DBUS_CFLAGS) AC_SUBST(DBUS_LIBS) -AS_AC_EXPAND(DATADIR, $datadir) -DBUS_SERVICES_DIR="$DATADIR/dbus-1/services" -AC_SUBST(DBUS_SERVICES_DIR) -AC_DEFINE_UNQUOTED(DBUS_SERVICES_DIR, "$DBUS_SERVICES_DIR", [DBus services directory]) - dnl Check for code generation tools XSLTPROC= AC_CHECK_PROGS([XSLTPROC], [xsltproc]) @@ -174,24 +168,6 @@ if test -z "$PYTHON"; then AC_MSG_ERROR([Python is required to compile this package]) fi -dnl Check for Loudmouth -AC_ARG_ENABLE(loudmouth-versioning, - AC_HELP_STRING([--disable-loudmouth-versioning], - [Stops configure checking the version of loudmouth to use.]), - enable_loudmouth_versioning=$enableval, - enable_loudmouth_versioning=yes) - -if test x$enable_loudmouth_versioning = xyes; then - LOUDMOUTH_VERSION_STR=" >= 1.1.1" -else - LOUDMOUTH_VERSION_STR="" -fi - -PKG_CHECK_MODULES(LOUDMOUTH, loudmouth-1.0$LOUDMOUTH_VERSION_STR) - -AC_SUBST(LOUDMOUTH_CFLAGS) -AC_SUBST(LOUDMOUTH_LIBS) - dnl glibc backtrace functions AC_CHECK_FUNCS(backtrace backtrace_symbols_fd) AC_CHECK_HEADERS(execinfo.h) @@ -211,8 +187,5 @@ AC_OUTPUT( Makefile \ lib/tests/Makefile \ lib/tools/Makefile \ lib/tools/update-spec-gen-am.sh \ - src/Makefile \ - m4/Makefile \ - data/Makefile \ - tests/Makefile + m4/Makefile \ ) diff --git a/data/.git-darcs-dir b/data/.git-darcs-dir deleted file mode 100644 index e69de29bb..000000000 --- a/data/.git-darcs-dir +++ /dev/null diff --git a/data/Makefile.am b/data/Makefile.am deleted file mode 100644 index e9ecced9c..000000000 --- a/data/Makefile.am +++ /dev/null @@ -1,22 +0,0 @@ -# Telepathy manager file -managerdir = $(datadir)/telepathy/managers -manager_DATA = gabble.manager - -# Dbus service file -servicedir = $(DBUS_SERVICES_DIR) -service_in_files = org.freedesktop.Telepathy.ConnectionManager.gabble.service.in -service_DATA = $(service_in_files:.service.in=.service) - -BUILT_FILES = $(service_DATA) $(manager_DATA) - -CLEANFILES = $(BUILT_FILES) - -EXTRA_DIST = $(service_in_files) - -# Rule to make the service file with bindir expanded -$(service_DATA): $(service_in_files) Makefile - @sed -e "s|\@bindir\@|$(bindir)|" $< > $@ - -$(manager_DATA): ../src/gabble-connection-manager.c ../src/write-mgr-file.c - $(MAKE) -C ../src write-mgr-file - ../src/write-mgr-file > $@ diff --git a/data/org.freedesktop.Telepathy.ConnectionManager.gabble.service.in b/data/org.freedesktop.Telepathy.ConnectionManager.gabble.service.in deleted file mode 100644 index 1988259cc..000000000 --- a/data/org.freedesktop.Telepathy.ConnectionManager.gabble.service.in +++ /dev/null @@ -1,3 +0,0 @@ -[D-BUS Service] -Name=org.freedesktop.Telepathy.ConnectionManager.gabble -Exec=@bindir@/telepathy-gabble diff --git a/src/.git-darcs-dir b/src/.git-darcs-dir deleted file mode 100644 index e69de29bb..000000000 --- a/src/.git-darcs-dir +++ /dev/null diff --git a/src/Makefile.am b/src/Makefile.am deleted file mode 100644 index 8cbc7db10..000000000 --- a/src/Makefile.am +++ /dev/null @@ -1,140 +0,0 @@ -BUILT_SOURCES = \ - gabble-media-session-enumtypes.h \ - gabble-media-session-enumtypes.c \ - gabble-signals-marshal.h \ - gabble-signals-marshal.c - -CLEANFILES = $(BUILT_SOURCES) - -bin_PROGRAMS=telepathy-gabble -noinst_PROGRAMS = write-mgr-file - -# These are the bits we wrote, so we can be really strict about coding style -libgabble_convenience_la_our_sources = \ - base64.h \ - base64.c \ - capabilities.h \ - capabilities.c \ - conn-aliasing.h \ - conn-aliasing.c \ - conn-avatars.h \ - conn-avatars.c \ - conn-presence.h \ - conn-presence.c \ - debug.h \ - debug.c \ - disco.h \ - disco.c \ - gabble-error.c \ - gabble-error.h \ - gabble-connection-manager.h \ - gabble-connection-manager.c \ - gabble-connection.h \ - gabble-connection.c \ - gabble-im-channel.h \ - gabble-im-channel.c \ - gabble-muc-channel.h \ - gabble-muc-channel.c \ - gabble-media-channel.h \ - gabble-media-channel.c \ - gabble-media-session.h \ - gabble-media-session.c \ - gabble-media-stream.h \ - gabble-media-stream.c \ - gabble-register.c \ - gabble-register.h \ - gabble-roster-channel.h \ - gabble-roster-channel.c \ - gabble-roomlist-channel.h \ - gabble-roomlist-channel.c \ - gabble-types.h \ - im-factory.h \ - im-factory.c \ - media-factory.h \ - media-factory.c \ - muc-factory.h \ - muc-factory.c \ - namespaces.h \ - presence.h \ - presence.c \ - presence-cache.h \ - presence-cache.c \ - roster.h \ - roster.c \ - text-mixin.h \ - text-mixin.c \ - util.h \ - util.c \ - vcard-manager.h \ - vcard-manager.c \ - gabble-signals-marshal.list - -# we don't want to subject third-party source to whitespace removal - -# it's more useful to keep it a verbatim copy -libgabble_convenience_la_SOURCES = \ - libmd5-rfc/md5.c \ - libmd5-rfc/md5.h \ - sha1/sha1.h \ - sha1/sha1.c \ - $(libgabble_convenience_la_our_sources) - -nodist_libgabble_convenience_la_SOURCES = \ - $(BUILT_SOURCES) - -write_mgr_file_SOURCES = write-mgr-file.c - -write_mgr_file_LDADD = libgabble-convenience.la \ - $(top_builddir)/lib/telepathy-glib/libtelepathy-glib.la - -telepathy_gabble_SOURCES = \ - gabble.h \ - gabble.c - -check_c_sources = \ - $(telepathy_gabble_SOURCES) \ - $(libgabble_convenience_la_our_sources) \ - $(telepathy_gabble_SOURCES) \ - $(write_mgr_file_SOURCES) -include $(top_srcdir)/check-coding-style.mk - -telepathy_gabble_LDADD = libgabble-convenience.la \ - $(top_builddir)/lib/telepathy-glib/libtelepathy-glib.la - -noinst_LTLIBRARIES = libgabble-convenience.la - -AM_CFLAGS = $(ERROR_CFLAGS) -I$(top_srcdir)/lib -I$(top_builddir)/lib \ - @DBUS_CFLAGS@ @GLIB_CFLAGS@ @LOUDMOUTH_CFLAGS@ \ - @HANDLE_LEAK_DEBUG_CFLAGS@ @COVERAGE_CFLAGS@ -AM_LDFLAGS = @DBUS_LIBS@ @GLIB_LIBS@ @LOUDMOUTH_LIBS@ - - -%-signals-marshal.h: %-signals-marshal.list - glib-genmarshal --header --prefix=$(subst -,_,$*)_marshal $< > $*-signals-marshal.h - -%-signals-marshal.c: %-signals-marshal.list - glib-genmarshal --body --prefix=$(subst -,_,$*)_marshal $< > $*-signals-marshal.c - -%-marshal.h: %-marshal.list - glib-genmarshal --header --prefix=$(subst -,_,$*)_marshal $< > $*-marshal.h - -%-marshal.c: %-marshal.list - glib-genmarshal --body --prefix=$(subst -,_,$*)_marshal $< > $*-marshal.c - - -# rules for making the glib enum objects -%-enumtypes.h: %.h Makefile.in - glib-mkenums \ - --fhead "#ifndef __$(shell echo $* | tr [:lower:]- [:upper:]_)_ENUM_TYPES_H__\n#define __$(shell echo $* | tr [:lower:]- [:upper:]_)_ENUM_TYPES_H__\n\n#include <glib-object.h>\n\nG_BEGIN_DECLS\n" \ - --fprod "/* enumerations from \"@filename@\" */\n" \ - --vhead "GType @enum_name@_get_type (void);\n#define $(shell echo $* | tr [:lower:]- [:upper:]_ | sed 's/_.*//')_TYPE_@ENUMSHORT@ (@enum_name@_get_type())\n" \ - --ftail "G_END_DECLS\n\n#endif /* __$(shell echo $* | tr [:lower:]- [:upper:]_)_ENUM_TYPES_H__ */" \ - $< > $@ - -%-enumtypes.c: %.h Makefile.in - glib-mkenums \ - --fhead "#include <$*.h>\n#include <$*-enumtypes.h>" \ - --fprod "\n/* enumerations from \"@filename@\" */" \ - --vhead "GType\n@enum_name@_get_type (void)\n{\n static GType etype = 0;\n if (etype == 0) {\n static const G@Type@Value values[] = {" \ - --vprod " { @VALUENAME@, \"@VALUENAME@\", \"@VALUENAME@\" }," \ - --vtail " { 0, NULL, NULL }\n };\n etype = g_@type@_register_static (\"@EnumName@\", values);\n }\n return etype;\n}\n" \ - $< > $@ diff --git a/src/base64.c b/src/base64.c deleted file mode 100644 index 2010da1f6..000000000 --- a/src/base64.c +++ /dev/null @@ -1,196 +0,0 @@ -/* - * base64.c - Base 64 encoding/decoding implementation - * Copyright (C) 2006 Collabora Ltd. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "base64.h" - -#define DEBUG_FLAG GABBLE_DEBUG_VCARD -#include "debug.h" - -#include <ctype.h> -#include <string.h> - -#include <glib.h> - -/* -|AAAA AABB|BBBB CCCC|CCDD DDDD| - -0xFC = 1111 1100 -0x03 = 0000 0011 -0xF0 = 1111 0000 -0x0F = 0000 1111 -0xC0 = 1100 0000 -0x3F = 0011 1111 - -3 input bytes = 4 output bytes; -2 input bytes = 2 output bytes; -1 input byte = 1 output byte. -*/ - -static const gchar *encoding = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - -static const guint decoding[256] = -{ - /* ... */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, - /* + */ - 62, - /* ... */ - 0, 0, 0, - /* / , 0-9 */ - 63, - 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, - /* ... */ - 0, 0, 0, 0, 0, 0, 0, - /* A */ - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, - 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, - /* ... */ - 0, 0, 0, 0, 0, 0, - /* a */ - 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, - 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 -}; - -#define GET_6_BITS_0(s) (((s)[0] & 0xFC) >> 2) -#define GET_6_BITS_1(s) (((s)[0] & 0x03) << 4) | \ - (((s)[1] & 0xF0) >> 4) -#define GET_6_BITS_2(s) (((s)[1] & 0x0F) << 2) | \ - (((s)[2] & 0xC0) >> 6) -#define GET_6_BITS_3(s) (((s)[2] & 0x3F) << 0) - -#define GET_BYTE_0(s) (((decoding[(guchar)(s)[0]] & 0x3F) << 2) | \ - ((decoding[(guchar)(s)[1]] & 0x30) >> 4)) -#define GET_BYTE_1(s) (((decoding[(guchar)(s)[1]] & 0x0F) << 4) | \ - ((decoding[(guchar)(s)[2]] & 0x3C) >> 2)) -#define GET_BYTE_2(s) (((decoding[(guchar)(s)[2]] & 0x03) << 6) | \ - ((decoding[(guchar)(s)[3]] & 0xFF) << 0)) - -gchar *base64_encode (const GString *str) -{ - guint i; - guint len; - GString *tmp; - - len = str->len; - /* TODO: calculate requisite output string length and allocate that big a - * GString */ - tmp = g_string_new (""); - - for (i = 0; i < len; i += 3) - { - guint c1, c2, c3, c4; - - if (i > 0 && (i * 4) % 76 == 0) - g_string_append_c (tmp, '\n'); - - switch (i + 3 - len) - { - case 1: - c1 = encoding[GET_6_BITS_0 (str->str + i)]; - c2 = encoding[GET_6_BITS_1 (str->str + i)]; - c3 = encoding[GET_6_BITS_2 (str->str + i)]; - c4 = '='; - break; - case 2: - c1 = encoding[GET_6_BITS_0 (str->str + i)]; - c2 = encoding[GET_6_BITS_1 (str->str + i)]; - c3 = '='; - c4 = '='; - break; - default: - c1 = encoding[GET_6_BITS_0 (str->str + i)]; - c2 = encoding[GET_6_BITS_1 (str->str + i)]; - c3 = encoding[GET_6_BITS_2 (str->str + i)]; - c4 = encoding[GET_6_BITS_3 (str->str + i)]; - } - - g_string_append_printf (tmp, "%c%c%c%c", c1, c2, c3, c4); - } - - return g_string_free (tmp, FALSE); -} - -GString *base64_decode (const gchar *str) -{ - guint i; - GString *tmp; - char group[4]; - guint filled = 0; - - for (i = 0; str[i]; i++) - { - if (str[i] != 'A' && - str[i] != '=' && - !isspace (str[i]) && - decoding[(guchar) str[i]] == 0) - { - DEBUG ("bad character %x at byte %u", (guchar)str[i], i); - return NULL; - } - } - - tmp = g_string_new (""); - - for (i = 0; str[i]; i++) - { - if (isspace (str[i])) - continue; - - group[filled++] = str[i]; - - if (filled == 4) - { - if (group[3] == '=') - { - if (group[2] == '=') - { - g_string_append_c (tmp, GET_BYTE_0(group)); - } - else - { - g_string_append_c (tmp, GET_BYTE_0(group)); - g_string_append_c (tmp, GET_BYTE_1(group)); - } - } - else - { - g_string_append_c (tmp, GET_BYTE_0(group)); - g_string_append_c (tmp, GET_BYTE_1(group)); - g_string_append_c (tmp, GET_BYTE_2(group)); - } - filled = 0; - } - } - - if (filled) - { - DEBUG ("insufficient padding at end of base64 string:\n%s", str); - g_string_free (tmp, TRUE); - return NULL; - } - - return tmp; -} - - diff --git a/src/base64.h b/src/base64.h deleted file mode 100644 index d73b50ca6..000000000 --- a/src/base64.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * base64.h - Base 64 encoding/decoding implementation - * Copyright (C) 2006 Collabora Ltd. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef __BASE64_H__ -#define __BASE64_H__ - -#include <glib.h> - -gchar *base64_encode (const GString *str); -GString *base64_decode (const gchar *str); - -#endif /* __BASE64_H__ */ diff --git a/src/capabilities.c b/src/capabilities.c deleted file mode 100644 index 926b1c3ea..000000000 --- a/src/capabilities.c +++ /dev/null @@ -1,100 +0,0 @@ -/* - * capabilities.c - Connection.Interface.Capabilities constants and utilities - * Copyright (C) 2005 Collabora Ltd. - * Copyright (C) 2005 Nokia Corporation - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "capabilities.h" - -#include "namespaces.h" -#include "config.h" -#include "presence-cache.h" -#include <telepathy-glib/interfaces.h> -#include "gabble-media-channel.h" - -static const Feature self_advertised_features[] = -{ - { VERSION, NS_GOOGLE_FEAT_SESSION, 0}, - { VERSION, NS_GOOGLE_TRANSPORT_P2P, PRESENCE_CAP_GOOGLE_TRANSPORT_P2P}, - { VERSION, NS_JINGLE, PRESENCE_CAP_JINGLE}, - { VERSION, NS_CHAT_STATES, PRESENCE_CAP_CHAT_STATES}, - - { BUNDLE_VOICE_V1, NS_GOOGLE_FEAT_VOICE, PRESENCE_CAP_GOOGLE_VOICE}, - { BUNDLE_JINGLE_AUDIO, NS_JINGLE_DESCRIPTION_AUDIO, - PRESENCE_CAP_JINGLE_DESCRIPTION_AUDIO}, - { BUNDLE_JINGLE_VIDEO, NS_JINGLE_DESCRIPTION_VIDEO, - PRESENCE_CAP_JINGLE_DESCRIPTION_VIDEO}, - { NULL, NULL, 0} -}; - -GSList * -capabilities_get_features (GabblePresenceCapabilities caps) -{ - GSList *features = NULL; - const Feature *i; - - for (i = self_advertised_features; NULL != i->ns; i++) - if ((i->caps & caps) == i->caps) - features = g_slist_append (features, (gpointer) i); - - return features; -} - -void -capabilities_fill_cache (GabblePresenceCache *cache) -{ - const Feature *feat; - for (feat = self_advertised_features; NULL != feat->ns; feat++) - { - gchar *node = g_strconcat (NS_GABBLE_CAPS "#", feat->bundle, NULL); - gabble_presence_cache_add_bundle_caps (cache, - node, feat->caps); - g_free (node); - } - - gabble_presence_cache_add_bundle_caps (cache, - "http://www.google.com/xmpp/client/caps#voice-v1", - PRESENCE_CAP_GOOGLE_VOICE); -} - -GabblePresenceCapabilities -capabilities_get_initial_caps () -{ - GabblePresenceCapabilities ret = 0; - const Feature *feat; - - for (feat = self_advertised_features; NULL != feat->ns; feat++) - { - if (g_str_equal (feat->bundle, VERSION)) - { - /* VERSION == bundle means a fixed feature, which we always - * advertise */ - ret |= feat->caps; - } - } - - return ret; -} - -const CapabilityConversionData capabilities_conversions[] = -{ - { TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA, - _gabble_media_channel_typeflags_to_caps, - _gabble_media_channel_caps_to_typeflags }, - { NULL, NULL, NULL} -}; - diff --git a/src/capabilities.h b/src/capabilities.h deleted file mode 100644 index 5c7bfd00b..000000000 --- a/src/capabilities.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * capabilities.h - Connection.Interface.Capabilities constants and utilities - * Copyright (C) 2005 Collabora Ltd. - * Copyright (C) 2005 Nokia Corporation - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef __GABBLE_CAPABILITIES__H__ -#define __GABBLE_CAPABILITIES__H__ - -#include <glib-object.h> - -#include "presence.h" - -#define BUNDLE_VOICE_V1 "voice-v1" -#define BUNDLE_JINGLE_AUDIO "jingle-audio" -#define BUNDLE_JINGLE_VIDEO "jingle-video" - -typedef struct _Feature Feature; - -struct _Feature -{ - const gchar *bundle; - const gchar *ns; - GabblePresenceCapabilities caps; -}; - -/* - * capabilities_get_features - * - * Return a linked list of const Feature structs corresponding to the given - * GabblePresenceCapabilities. - */ -GSList * -capabilities_get_features (GabblePresenceCapabilities caps); - -/* - * capabilities_fill_cache - * - * Fill up the given GabblePresenceCache with known feature nodes - */ -void -capabilities_fill_cache (GabblePresenceCache *cache); - -/* - * capabilities_get_initial_caps - * - * Return the GabblePresenceCapabilities we always have - */ -GabblePresenceCapabilities -capabilities_get_initial_caps (); - -typedef GabblePresenceCapabilities (*TypeFlagsToCapsFunc) (guint typeflags); -typedef guint (*CapsToTypeFlagsFunc) (GabblePresenceCapabilities caps); - -typedef struct _CapabilityConversionData CapabilityConversionData; - -struct _CapabilityConversionData -{ - const gchar *iface; - TypeFlagsToCapsFunc tf2c_fn; - CapsToTypeFlagsFunc c2tf_fn; -}; - -extern const CapabilityConversionData capabilities_conversions[]; - -#endif /* __GABBLE_CAPABILITIES__H__ */ - diff --git a/src/conn-aliasing.c b/src/conn-aliasing.c deleted file mode 100644 index 49138cc42..000000000 --- a/src/conn-aliasing.c +++ /dev/null @@ -1,378 +0,0 @@ -/* - * conn-aliasing.c - Gabble connection aliasing interface - * Copyright (C) 2005-2007 Collabora Ltd. - * Copyright (C) 2005-2007 Nokia Corporation - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "conn-aliasing.h" - -#include <telepathy-glib/svc-connection.h> - -#include "gabble-connection.h" -#include "roster.h" -#include "vcard-manager.h" - -#define DEBUG_FLAG GABBLE_DEBUG_CONNECTION - -#include "debug.h" - -/** - * gabble_connection_get_alias_flags - * - * Implements D-Bus method GetAliasFlags - * on interface org.freedesktop.Telepathy.Connection.Interface.Aliasing - * - * @error: Used to return a pointer to a GError detailing any error - * that occurred, D-Bus will throw the error only if this - * function returns FALSE. - * - * Returns: TRUE if successful, FALSE if an error was thrown. - */ -static void -gabble_connection_get_alias_flags (TpSvcConnectionInterfaceAliasing *iface, - DBusGMethodInvocation *context) -{ - TpBaseConnection *base = TP_BASE_CONNECTION (iface); - - g_assert (GABBLE_IS_CONNECTION (base)); - - TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); - - tp_svc_connection_interface_aliasing_return_from_get_alias_flags ( - context, TP_CONNECTION_ALIAS_FLAG_USER_SET); -} - - -typedef struct _AliasesRequest AliasesRequest; - -struct _AliasesRequest -{ - GabbleConnection *conn; - DBusGMethodInvocation *request_call; - guint pending_vcard_requests; - GArray *contacts; - GabbleVCardManagerRequest **vcard_requests; - gchar **aliases; -}; - - -static AliasesRequest * -aliases_request_new (GabbleConnection *conn, - DBusGMethodInvocation *request_call, - const GArray *contacts) -{ - AliasesRequest *request; - - request = g_slice_new0 (AliasesRequest); - request->conn = conn; - request->request_call = request_call; - request->contacts = g_array_new (FALSE, FALSE, sizeof (TpHandle)); - g_array_insert_vals (request->contacts, 0, contacts->data, contacts->len); - request->vcard_requests = - g_new0 (GabbleVCardManagerRequest *, contacts->len); - request->aliases = g_new0 (gchar *, contacts->len + 1); - return request; -} - - -static void -aliases_request_free (AliasesRequest *request) -{ - guint i; - - for (i = 0; i < request->contacts->len; i++) - { - if (request->vcard_requests[i] != NULL) - gabble_vcard_manager_cancel_request (request->conn->vcard_manager, - request->vcard_requests[i]); - } - - g_array_free (request->contacts, TRUE); - g_free (request->vcard_requests); - g_strfreev (request->aliases); - g_slice_free (AliasesRequest, request); -} - - -static gboolean -aliases_request_try_return (AliasesRequest *request) -{ - if (request->pending_vcard_requests == 0) - { - /* FIXME: I'm not entirely sure why gcc warns without this cast from - * (gchar **) to (const gchar **) */ - tp_svc_connection_interface_aliasing_return_from_request_aliases ( - request->request_call, (const gchar **)request->aliases); - return TRUE; - } - - return FALSE; -} - - -static void -aliases_request_vcard_cb (GabbleVCardManager *manager, - GabbleVCardManagerRequest *request, - TpHandle handle, - LmMessageNode *vcard, - GError *error, - gpointer user_data) -{ - AliasesRequest *aliases_request = (AliasesRequest *) user_data; - GabbleConnectionAliasSource source; - guint i; - gboolean found = FALSE; - gchar *alias = NULL; - - g_assert (aliases_request->pending_vcard_requests > 0); - - /* The index of the vCard request in the vCard request array is the - * index of the contact/alias in their respective arrays. */ - - for (i = 0; i < aliases_request->contacts->len; i++) - if (aliases_request->vcard_requests[i] == request) - { - found = TRUE; - break; - } - - g_assert (found); - source = _gabble_connection_get_cached_alias (aliases_request->conn, - g_array_index (aliases_request->contacts, TpHandle, i), &alias); - g_assert (source != GABBLE_CONNECTION_ALIAS_NONE); - g_assert (NULL != alias); - - aliases_request->pending_vcard_requests--; - aliases_request->vcard_requests[i] = NULL; - aliases_request->aliases[i] = alias; - - if (aliases_request_try_return (aliases_request)) - aliases_request_free (aliases_request); -} - - -/** - * gabble_connection_request_aliases - * - * Implements D-Bus method RequestAliases - * on interface org.freedesktop.Telepathy.Connection.Interface.Aliasing - * - * @context: The D-Bus invocation context to use to return values - * or throw an error. - */ -static void -gabble_connection_request_aliases (TpSvcConnectionInterfaceAliasing *iface, - const GArray *contacts, - DBusGMethodInvocation *context) -{ - GabbleConnection *self = GABBLE_CONNECTION (iface); - TpBaseConnection *base = (TpBaseConnection *)self; - TpHandleRepoIface *contact_handles = tp_base_connection_get_handles (base, - TP_HANDLE_TYPE_CONTACT); - guint i; - AliasesRequest *request; - GError *error = NULL; - - g_assert (GABBLE_IS_CONNECTION (self)); - - TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); - - if (!tp_handles_are_valid (contact_handles, contacts, FALSE, &error)) - { - dbus_g_method_return_error (context, error); - g_error_free (error); - return; - } - - request = aliases_request_new (self, context, contacts); - - for (i = 0; i < contacts->len; i++) - { - TpHandle handle = g_array_index (contacts, TpHandle, i); - GabbleConnectionAliasSource source; - GabbleVCardManagerRequest *vcard_request; - gchar *alias; - - source = _gabble_connection_get_cached_alias (self, handle, &alias); - g_assert (source != GABBLE_CONNECTION_ALIAS_NONE); - g_assert (NULL != alias); - - if (source >= GABBLE_CONNECTION_ALIAS_FROM_VCARD || - gabble_vcard_manager_has_cached_alias (self->vcard_manager, handle)) - { - /* Either the alias we got was from a vCard or better, or we already - * tried getting an alias from a vcard and failed, so there's no - * point trying again. */ - request->aliases[i] = alias; - } - else - { - DEBUG ("requesting vCard for alias of contact %s", - tp_handle_inspect (contact_handles, handle)); - - g_free (alias); - - if (gabble_vcard_manager_get_cached (self->vcard_manager, handle, - NULL)) - { - GabbleConnectionAliasSource source; - source = _gabble_connection_get_cached_alias (self, handle, - &alias); - request->vcard_requests[i] = NULL; - request->aliases[i] = alias; - request->pending_vcard_requests++; - continue; - } - - vcard_request = gabble_vcard_manager_request (self->vcard_manager, - handle, 0, aliases_request_vcard_cb, request, G_OBJECT (self), - &error); - - if (NULL != error) - { - dbus_g_method_return_error (context, error); - g_error_free (error); - aliases_request_free (request); - return; - } - - request->vcard_requests[i] = vcard_request; - request->pending_vcard_requests++; - } - } - - if (aliases_request_try_return (request)) - aliases_request_free (request); -} - - -struct _i_hate_g_hash_table_foreach -{ - GabbleConnection *conn; - GError **error; - gboolean retval; -}; - -static void -setaliases_foreach (gpointer key, gpointer value, gpointer user_data) -{ - struct _i_hate_g_hash_table_foreach *data = - (struct _i_hate_g_hash_table_foreach *) user_data; - TpHandle handle = GPOINTER_TO_INT (key); - gchar *alias = (gchar *) value; - GError *error = NULL; - TpBaseConnection *base = (TpBaseConnection *)data->conn; - TpHandleRepoIface *contact_handles = tp_base_connection_get_handles (base, - TP_HANDLE_TYPE_CONTACT); - - if (!tp_handle_is_valid (contact_handles, handle, &error)) - { - data->retval = FALSE; - } - else if (base->self_handle == handle) - { - /* only alter the roster if we're already there, e.g. because someone - * added us with another client - */ - if (gabble_roster_handle_has_entry (data->conn->roster, handle) - && !gabble_roster_handle_set_name (data->conn->roster, handle, - alias, data->error)) - { - data->retval = FALSE; - } - } - else if (!gabble_roster_handle_set_name (data->conn->roster, handle, alias, - data->error)) - { - data->retval = FALSE; - } - - if (base->self_handle == handle) - { - /* User has done SetAliases on themselves - patch their vCard. - * FIXME: because SetAliases is currently synchronous, we ignore errors - * here, and just let the request happen in the background - */ - gabble_vcard_manager_edit (data->conn->vcard_manager, 0, NULL, NULL, - G_OBJECT(data->conn), NULL, "NICKNAME", alias, NULL); - } - - if (NULL != error) - { - if (NULL == *(data->error)) - { - *(data->error) = error; - } - else - { - g_error_free (error); - } - } -} - -/** - * gabble_connection_set_aliases - * - * Implements D-Bus method SetAliases - * on interface org.freedesktop.Telepathy.Connection.Interface.Aliasing - */ -static void -gabble_connection_set_aliases (TpSvcConnectionInterfaceAliasing *iface, - GHashTable *aliases, - DBusGMethodInvocation *context) -{ - GabbleConnection *self = GABBLE_CONNECTION (iface); - TpBaseConnection *base = (TpBaseConnection *)self; - GError *error = NULL; - struct _i_hate_g_hash_table_foreach data = { NULL, NULL, TRUE }; - - g_assert (GABBLE_IS_CONNECTION (self)); - - TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); - - data.conn = self; - data.error = &error; - - g_hash_table_foreach (aliases, setaliases_foreach, &data); - - if (data.retval) - { - tp_svc_connection_interface_aliasing_return_from_set_aliases ( - context); - } - else - { - dbus_g_method_return_error (context, error); - g_error_free (error); - } -} - - -void -conn_aliasing_iface_init (gpointer g_iface, gpointer iface_data) -{ - TpSvcConnectionInterfaceAliasingClass *klass = - (TpSvcConnectionInterfaceAliasingClass *) g_iface; - -#define IMPLEMENT(x) tp_svc_connection_interface_aliasing_implement_##x (\ - klass, gabble_connection_##x) - IMPLEMENT(get_alias_flags); - IMPLEMENT(request_aliases); - IMPLEMENT(set_aliases); -#undef IMPLEMENT -} - diff --git a/src/conn-aliasing.h b/src/conn-aliasing.h deleted file mode 100644 index 51b1a5591..000000000 --- a/src/conn-aliasing.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * conn-aliasing.h - Header for Gabble connection aliasing interface - * Copyright (C) 2005-2007 Collabora Ltd. - * Copyright (C) 2005-2007 Nokia Corporation - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef __CONN_ALIASING_H__ -#define __CONN_ALIASING_H__ - -#include <glib.h> - -G_BEGIN_DECLS - -void conn_aliasing_iface_init (gpointer g_iface, gpointer iface_data); - -G_END_DECLS - -#endif /* __CONN_ALIASING_H__ */ - diff --git a/src/conn-avatars.c b/src/conn-avatars.c deleted file mode 100644 index 885f661a0..000000000 --- a/src/conn-avatars.c +++ /dev/null @@ -1,669 +0,0 @@ -/* - * conn-avatars.c - Gabble connection avatar interface - * Copyright (C) 2005-2007 Collabora Ltd. - * Copyright (C) 2005-2007 Nokia Corporation - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "config.h" - -#include "conn-avatars.h" - -#include <string.h> - -#include <loudmouth/loudmouth.h> - -#include <telepathy-glib/svc-connection.h> - -#include "base64.h" -#include "presence.h" -#include "presence-cache.h" -#include "namespaces.h" -#include "sha1/sha1.h" -#include "vcard-manager.h" -#include "util.h" - -#define DEBUG_FLAG GABBLE_DEBUG_CONNECTION - -#include "debug.h" - -/* If the SHA1 has changed, this function will copy it to self_presence, - * emit a signal and push it to the server. */ -static gboolean -update_own_avatar_sha1 (GabbleConnection *conn, - const gchar *sha1, - GError **out_error) -{ - TpBaseConnection *base = (TpBaseConnection *)conn; - GError *error = NULL; - - if (!tp_strdiff (sha1, conn->self_presence->avatar_sha1)) - return TRUE; - - tp_svc_connection_interface_avatars_emit_avatar_updated (conn, - base->self_handle, sha1); - - g_free (conn->self_presence->avatar_sha1); - conn->self_presence->avatar_sha1 = g_strdup (sha1); - - if (!_gabble_connection_signal_own_presence (conn, &error)) - { - if (out_error == NULL) - { - DEBUG ("failed to signal changed avatar sha1 to the server: %s", - error->message); - g_error_free (error); - } - - g_propagate_error (out_error, error); - - return FALSE; - } - - return TRUE; -} - - -static void -connection_avatar_update_cb (GabblePresenceCache *cache, - TpHandle handle, - gpointer user_data) -{ - GabbleConnection *conn = GABBLE_CONNECTION (user_data); - TpBaseConnection *base = (TpBaseConnection *)conn; - GabblePresence *presence; - - presence = gabble_presence_cache_get (conn->presence_cache, handle); - - g_assert (presence != NULL); - - gabble_vcard_manager_invalidate_cache (conn->vcard_manager, handle); - - if (handle == base->self_handle) - update_own_avatar_sha1 (conn, presence->avatar_sha1, NULL); - else - tp_svc_connection_interface_avatars_emit_avatar_updated (conn, - handle, presence->avatar_sha1); -} - -/* Called when our vCard is first fetched, so we can start putting the - * SHA-1 of an existing avatar in our presence. */ -static void -connection_got_self_initial_avatar_cb (GObject *obj, - gchar *sha1, - gpointer user_data) -{ - GabbleConnection *conn = GABBLE_CONNECTION (user_data); - - update_own_avatar_sha1 (conn, sha1, NULL); -} - - -/** - * gabble_connection_get_avatar_requirements - * - * Implements D-Bus method GetAvatarRequirements - * on interface org.freedesktop.Telepathy.Connection.Interface.Avatars - * - * @error: Used to return a pointer to a GError detailing any error - * that occurred, D-Bus will throw the error only if this - * function returns FALSE. - * - * Returns: TRUE if successful, FALSE if an error was thrown. - */ -static void -gabble_connection_get_avatar_requirements (TpSvcConnectionInterfaceAvatars *iface, - DBusGMethodInvocation *context) -{ - /* Jabber prescribes no MIME type for avatars, but XEP-0153 says support - * for image/png is REQUIRED, with image/jpeg and image/gif RECOMMENDED */ - static const char *mimetypes[] = { - "image/png", "image/jpeg", "image/gif", NULL }; - - TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (TP_BASE_CONNECTION (iface), - context); - - /* Jabber has no min/max width/height or max size, but XEP-0153 says - * you SHOULD use 32-96px either way, and no more than 8K of data */ - - tp_svc_connection_interface_avatars_return_from_get_avatar_requirements ( - context, mimetypes, 32, 32, 96, 96, 8192); -} - - -typedef struct { - DBusGMethodInvocation *invocation; - gchar **ret; - guint my_index; - gulong signal_conn; -} GetAvatarTokensContext; - - -static void -_got_self_avatar_for_get_avatar_tokens (GObject *obj, - gchar *sha1, - gpointer user_data) -{ - GetAvatarTokensContext *context = (GetAvatarTokensContext *) user_data; - - g_signal_handler_disconnect (obj, context->signal_conn); - g_free (context->ret[context->my_index]); - context->ret[context->my_index] = g_strdup (sha1); - - /* FIXME: I'm not entirely sure why gcc warns without this cast from - * (gchar **) to (const gchar **) */ - tp_svc_connection_interface_avatars_return_from_get_avatar_tokens ( - context->invocation, (const gchar **)context->ret); - g_strfreev (context->ret); - - g_slice_free (GetAvatarTokensContext, context); -} - - -/** - * gabble_connection_get_avatar_tokens - * - * Implements D-Bus method GetAvatarTokens - * on interface org.freedesktop.Telepathy.Connection.Interface.Avatars - * - * @context: The D-Bus invocation context to use to return values - * or throw an error. - */ -static void -gabble_connection_get_avatar_tokens (TpSvcConnectionInterfaceAvatars *iface, - const GArray *contacts, - DBusGMethodInvocation *invocation) -{ - GabbleConnection *self = GABBLE_CONNECTION (iface); - TpBaseConnection *base = (TpBaseConnection *)self; - gboolean wait_for_self_avatar = FALSE; - gboolean have_self_avatar; - guint i, my_index = 0; - gchar **ret; - GError *err = NULL; - TpHandleRepoIface *contact_handles = tp_base_connection_get_handles (base, - TP_HANDLE_TYPE_CONTACT); - - TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, invocation); - - if (!tp_handles_are_valid (contact_handles, contacts, FALSE, &err)) - { - dbus_g_method_return_error (invocation, err); - g_error_free (err); - return; - } - - g_object_get (self->vcard_manager, - "have-self-avatar", &have_self_avatar, - NULL); - - ret = g_new0 (gchar *, contacts->len + 1); - - /* TODO: always call the callback so we can defer presence lookups until - * we return the method, then we don't need to strdup the strings we're - * returning. */ - - for (i = 0; i < contacts->len; i++) - { - TpHandle handle; - GabblePresence *presence = NULL; - - handle = g_array_index (contacts, TpHandle, i); - - if (base->self_handle == handle) - { - if (have_self_avatar) - { - presence = self->self_presence; - } - else - { - wait_for_self_avatar = TRUE; - my_index = i; - } - } - else - { - presence = gabble_presence_cache_get (self->presence_cache, handle); - } - - if (NULL != presence && NULL != presence->avatar_sha1) - ret[i] = g_strdup (presence->avatar_sha1); - else - ret[i] = g_strdup (""); - } - - if (wait_for_self_avatar) - { - GetAvatarTokensContext *context = g_slice_new (GetAvatarTokensContext); - - context->invocation = invocation; - context->my_index = my_index; - context->ret = ret; - context->signal_conn = g_signal_connect (self->vcard_manager, - "got-self-initial-avatar", - G_CALLBACK (_got_self_avatar_for_get_avatar_tokens), - context); - - return; - } - - /* FIXME: I'm not entirely sure why gcc warns without this cast from - * (gchar **) to (const gchar **) */ - tp_svc_connection_interface_avatars_return_from_get_avatar_tokens ( - invocation, (const gchar **)ret); - g_strfreev (ret); -} - - -static void -_request_avatar_cb (GabbleVCardManager *self, - GabbleVCardManagerRequest *request, - TpHandle handle, - LmMessageNode *vcard, - GError *vcard_error, - gpointer user_data) -{ - DBusGMethodInvocation *context = (DBusGMethodInvocation *) user_data; - GabbleConnection *conn; - TpBaseConnection *base; - LmMessageNode *photo_node, *type_node, *binval_node; - const gchar *mime_type, *binval_value; - GArray *arr; - GError *error = NULL; - GString *avatar = NULL; - GabblePresence *presence; - - g_object_get (self, "connection", &conn, NULL); - base = TP_BASE_CONNECTION (conn); - - if (NULL == vcard) - { - dbus_g_method_return_error (context, vcard_error); - goto out; - } - - photo_node = lm_message_node_get_child (vcard, "PHOTO"); - - if (NULL == photo_node) - { - g_set_error (&error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE, - "contact vCard has no photo"); - dbus_g_method_return_error (context, error); - g_error_free (error); - goto out; - } - - type_node = lm_message_node_get_child (photo_node, "TYPE"); - - if (NULL == type_node) - { - g_set_error (&error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE, - "contact avatar is missing type node"); - dbus_g_method_return_error (context, error); - g_error_free (error); - goto out; - } - - binval_node = lm_message_node_get_child (photo_node, "BINVAL"); - - if (NULL == binval_node) - { - g_set_error (&error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE, - "contact avatar is missing binval node"); - dbus_g_method_return_error (context, error); - g_error_free (error); - goto out; - } - - binval_value = lm_message_node_get_value (binval_node); - - if (NULL == binval_value) - { - g_set_error (&error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE, - "contact avatar is missing binval content"); - dbus_g_method_return_error (context, error); - g_error_free (error); - goto out; - } - - avatar = base64_decode (binval_value); - - if (NULL == avatar) - { - g_set_error (&error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE, - "failed to decode avatar from base64"); - dbus_g_method_return_error (context, error); - g_error_free (error); - goto out; - } - - if (handle == base->self_handle) - presence = conn->self_presence; - else - presence = gabble_presence_cache_get (conn->presence_cache, handle); - - if (presence != NULL) - { - gchar *sha1; - - sha1 = sha1_hex (avatar->str, avatar->len); - - if (tp_strdiff (presence->avatar_sha1, sha1)) - { - /* the thinking here is that we have to return an error, because we - * can't give the user the vcard they're expecting, which has the - * hash from the time that they requested it. */ - DEBUG ("treason uncloaked! avatar hash in presence does not match " - "avatar in vCard for handle %u", handle); - - g_set_error (&error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE, - "avatar hash in presence does not match avatar in vCard"); - dbus_g_method_return_error (context, error); - g_error_free (error); - error = NULL; - - if (handle == base->self_handle) - { - update_own_avatar_sha1 (conn, sha1, NULL); - g_free (sha1); - } - else - { - g_free (presence->avatar_sha1); - presence->avatar_sha1 = sha1; /* take ownership */ - - tp_svc_connection_interface_avatars_emit_avatar_updated ( - conn, handle, sha1); - } - - goto out; - } - - g_free (sha1); - } - - mime_type = lm_message_node_get_value (type_node); - arr = g_array_new (FALSE, FALSE, sizeof (gchar)); - g_array_append_vals (arr, avatar->str, avatar->len); - tp_svc_connection_interface_avatars_return_from_request_avatar ( - context, arr, mime_type); - g_array_free (arr, TRUE); - -out: - if (avatar != NULL) - g_string_free (avatar, TRUE); - - g_object_unref (conn); -} - - -/** - * gabble_connection_request_avatar - * - * Implements D-Bus method RequestAvatar - * on interface org.freedesktop.Telepathy.Connection.Interface.Avatars - * - * @context: The D-Bus invocation context to use to return values - * or throw an error. - */ -static void -gabble_connection_request_avatar (TpSvcConnectionInterfaceAvatars *iface, - guint contact, - DBusGMethodInvocation *context) -{ - GabbleConnection *self = GABBLE_CONNECTION (iface); - TpBaseConnection *base = (TpBaseConnection *)self; - TpHandleRepoIface *contact_handles = tp_base_connection_get_handles (base, - TP_HANDLE_TYPE_CONTACT); - GError *err = NULL; - LmMessageNode *vcard_node; - - TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); - - if (!tp_handle_is_valid (contact_handles, contact, &err)) - { - dbus_g_method_return_error (context, err); - g_error_free (err); - return; - } - - if (gabble_vcard_manager_get_cached (self->vcard_manager, - contact, &vcard_node)) - { - _request_avatar_cb (self->vcard_manager, NULL, contact, vcard_node, NULL, - context); - } - else - { - gabble_vcard_manager_request (self->vcard_manager, contact, 0, - _request_avatar_cb, context, NULL, NULL); - } -} - - -struct _set_avatar_ctx { - GabbleConnection *conn; - DBusGMethodInvocation *invocation; - LmMessage *new_vcard_msg; - GString *avatar; - gchar *mime_type; -}; - - -static void -_set_avatar_ctx_free (struct _set_avatar_ctx *ctx) -{ - lm_message_unref (ctx->new_vcard_msg); - if (ctx->avatar) - g_string_free (ctx->avatar, TRUE); - g_free (ctx->mime_type); - g_free (ctx); -} - - -static void -_set_avatar_cb2 (GabbleVCardManager *manager, - GabbleVCardManagerRequest *request, - TpHandle handle, - LmMessageNode *vcard, - GError *vcard_error, - gpointer user_data) -{ - struct _set_avatar_ctx *ctx = (struct _set_avatar_ctx *) user_data; - TpBaseConnection *base = (TpBaseConnection *)ctx->conn; - - if (NULL == vcard) - { - dbus_g_method_return_error (ctx->invocation, vcard_error); - } - else - { - GabblePresence *presence = ctx->conn->self_presence; - GError *error = NULL; - - g_free (presence->avatar_sha1); - if (ctx->avatar) - { - presence->avatar_sha1 = sha1_hex (ctx->avatar->str, - ctx->avatar->len); - } - else - { - presence->avatar_sha1 = NULL; - } - - if (_gabble_connection_signal_own_presence (ctx->conn, &error)) - { - tp_svc_connection_interface_avatars_return_from_set_avatar ( - ctx->invocation, presence->avatar_sha1); - tp_svc_connection_interface_avatars_emit_avatar_updated ( - ctx->conn, base->self_handle, presence->avatar_sha1); - } - else - { - dbus_g_method_return_error (ctx->invocation, error); - g_error_free (error); - } - } - - _set_avatar_ctx_free (ctx); -} - - -static void -_set_avatar_cb1 (GabbleVCardManager *manager, - GabbleVCardManagerRequest *request, - TpHandle handle, - LmMessageNode *vcard, - GError *vcard_error, - gpointer user_data) -{ - struct _set_avatar_ctx *ctx = (struct _set_avatar_ctx *) user_data; - LmMessageNode *new_vcard, *photo_node, *type_node, *binval_node, *i, *next; - gchar *encoded; - - if (NULL == vcard) - { - dbus_g_method_return_error (ctx->invocation, vcard_error); - _set_avatar_ctx_free (ctx); - return; - } - - ctx->new_vcard_msg = lm_message_new (NULL, LM_MESSAGE_TYPE_UNKNOWN); - new_vcard = lm_message_node_add_child (ctx->new_vcard_msg->node, - "vCard", ""); - lm_message_node_set_attribute (new_vcard, "xmlns", NS_VCARD_TEMP); - lm_message_node_steal_children (new_vcard, vcard); - - for (i = new_vcard->children; i; i = next) - { - next = i->next; - if (0 == strcmp (i->name, "PHOTO")) - { - lm_message_node_unlink (i); - lm_message_node_unref (i); - } - } - - photo_node = lm_message_node_add_child (new_vcard, "PHOTO", ""); - - if (ctx->avatar) - { - - type_node = lm_message_node_get_child (photo_node, "TYPE"); - - if (NULL == type_node) - type_node = lm_message_node_add_child (photo_node, "TYPE", ""); - - lm_message_node_set_value (type_node, ctx->mime_type); - - binval_node = lm_message_node_get_child (photo_node, "BINVAL"); - - if (NULL == binval_node) - binval_node = lm_message_node_add_child (photo_node, "BINVAL", ""); - - encoded = base64_encode (ctx->avatar); - lm_message_node_set_value (binval_node, encoded); - g_free (encoded); - } - - gabble_vcard_manager_replace ( - manager, new_vcard, 0, _set_avatar_cb2, ctx, NULL, NULL); -} - - -/** - * gabble_connection_set_avatar - * - * Implements D-Bus method SetAvatar - * on interface org.freedesktop.Telepathy.Connection.Interface.Avatars - * - * @context: The D-Bus invocation context to use to return values - * or throw an error. - */ -static void -gabble_connection_set_avatar (TpSvcConnectionInterfaceAvatars *iface, - const GArray *avatar, - const gchar *mime_type, - DBusGMethodInvocation *context) -{ - GabbleConnection *self = GABBLE_CONNECTION (iface); - TpBaseConnection *base = (TpBaseConnection *)self; - struct _set_avatar_ctx *ctx; - - TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); - - ctx = g_new0 (struct _set_avatar_ctx, 1); - ctx->conn = self; - ctx->invocation = context; - if (avatar) - { - ctx->avatar = g_string_new_len (avatar->data, avatar->len); - ctx->mime_type = g_strdup (mime_type); - } - - DEBUG ("called"); - gabble_vcard_manager_invalidate_cache (self->vcard_manager, - base->self_handle); - gabble_vcard_manager_request (self->vcard_manager, - base->self_handle, 0, _set_avatar_cb1, ctx, NULL, NULL); -} - - -/** - * gabble_connection_clear_avatar - * - * Implements D-Bus method ClearAvatar - * on interface org.freedesktop.Telepathy.Connection.Interface.Avatars - * - * @context: The D-Bus invocation context to use to return values - * or throw an error. - */ -void -gabble_connection_clear_avatar (TpSvcConnectionInterfaceAvatars *iface, - DBusGMethodInvocation *context) -{ - gabble_connection_set_avatar (iface, NULL, NULL, context); -} - - -void -conn_avatars_init (GabbleConnection *conn) -{ - g_signal_connect (conn->vcard_manager, "got-self-initial-avatar", G_CALLBACK - (connection_got_self_initial_avatar_cb), conn); - g_signal_connect (conn->presence_cache, "avatar-update", G_CALLBACK - (connection_avatar_update_cb), conn); -} - - -void -conn_avatars_iface_init (gpointer g_iface, gpointer iface_data) -{ - TpSvcConnectionInterfaceAvatarsClass *klass = - (TpSvcConnectionInterfaceAvatarsClass *) g_iface; - -#define IMPLEMENT(x) tp_svc_connection_interface_avatars_implement_##x (\ - klass, gabble_connection_##x) - IMPLEMENT(get_avatar_requirements); - IMPLEMENT(get_avatar_tokens); - IMPLEMENT(request_avatar); - IMPLEMENT(set_avatar); - IMPLEMENT(clear_avatar); -#undef IMPLEMENT -} - diff --git a/src/conn-avatars.h b/src/conn-avatars.h deleted file mode 100644 index b40e160e2..000000000 --- a/src/conn-avatars.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * conn-avatars.h - Header for Gabble connection avatar interface - * Copyright (C) 2005-2007 Collabora Ltd. - * Copyright (C) 2005-2007 Nokia Corporation - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef __CONN_AVATARS_H__ -#define __CONN_AVATARS_H__ - -#include "gabble-connection.h" - -G_BEGIN_DECLS - -void conn_avatars_init (GabbleConnection *conn); -void conn_avatars_iface_init (gpointer g_iface, gpointer iface_data); - -G_END_DECLS - -#endif /* __CONN_AVATARS_H__ */ - diff --git a/src/conn-presence.c b/src/conn-presence.c deleted file mode 100644 index cb3e9490f..000000000 --- a/src/conn-presence.c +++ /dev/null @@ -1,710 +0,0 @@ -/* - * conn-presence - Gabble connection presence interface - * Copyright (C) 2005-2007 Collabora Ltd. - * Copyright (C) 2005-2007 Nokia Corporation - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "conn-presence.h" - -#include <string.h> - -#include <telepathy-glib/svc-connection.h> -#include <telepathy-glib/util.h> - -#include "gabble-connection.h" -#include "presence.h" -#include "presence-cache.h" - -#define DEBUG_FLAG GABBLE_DEBUG_CONNECTION - -#include "debug.h" - - -typedef struct _StatusInfo StatusInfo; - -struct _StatusInfo -{ - const gchar *name; - TpConnectionPresenceType presence_type; - const gboolean self; - const gboolean exclusive; -}; - -/* order must match PresenceId enum in gabble-connection.h */ -/* in increasing order of presence */ -static const StatusInfo gabble_statuses[NUM_GABBLE_PRESENCES] = { - { "offline", TP_CONNECTION_PRESENCE_TYPE_OFFLINE, TRUE, TRUE }, - { "hidden", TP_CONNECTION_PRESENCE_TYPE_HIDDEN, TRUE, TRUE }, - { "xa", TP_CONNECTION_PRESENCE_TYPE_EXTENDED_AWAY, TRUE, TRUE }, - { "away", TP_CONNECTION_PRESENCE_TYPE_AWAY, TRUE, TRUE }, - { "dnd", TP_CONNECTION_PRESENCE_TYPE_AWAY, TRUE, TRUE }, - { "available", TP_CONNECTION_PRESENCE_TYPE_AVAILABLE, TRUE, TRUE }, - { "chat", TP_CONNECTION_PRESENCE_TYPE_AVAILABLE, TRUE, TRUE } -}; - - -static GHashTable * -construct_presence_hash (GabbleConnection *self, - const GArray *contact_handles) -{ - TpBaseConnection *base = (TpBaseConnection *)self; - guint i; - TpHandle handle; - GHashTable *presence_hash, *contact_status, *parameters; - GValueArray *vals; - GValue *message; - GabblePresence *presence; - GabblePresenceId status; - const gchar *status_message; - /* this is never set at the moment*/ - guint timestamp = 0; - TpHandleRepoIface *handle_repo = tp_base_connection_get_handles (base, - TP_HANDLE_TYPE_CONTACT); - - g_assert (tp_handles_are_valid (handle_repo, contact_handles, FALSE, NULL)); - - presence_hash = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, - (GDestroyNotify) g_value_array_free); - - for (i = 0; i < contact_handles->len; i++) - { - handle = g_array_index (contact_handles, TpHandle, i); - - if (handle == base->self_handle) - presence = self->self_presence; - else - presence = gabble_presence_cache_get (self->presence_cache, handle); - - if (presence) - { - status = presence->status; - status_message = presence->status_message; - } - else - { - status = GABBLE_PRESENCE_OFFLINE; - status_message = NULL; - } - - parameters = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, - (GDestroyNotify) tp_g_value_slice_free); - if (status_message != NULL) { - message = g_slice_new0 (GValue); - g_value_init (message, G_TYPE_STRING); - g_value_set_static_string (message, status_message); - - - g_hash_table_insert (parameters, "message", message); - } - - contact_status = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, - (GDestroyNotify) g_hash_table_destroy); - g_hash_table_insert (contact_status, - (gchar *) gabble_statuses[status].name, parameters); - - vals = g_value_array_new (2); - - g_value_array_append (vals, NULL); - g_value_init (g_value_array_get_nth (vals, 0), G_TYPE_UINT); - g_value_set_uint (g_value_array_get_nth (vals, 0), timestamp); - - g_value_array_append (vals, NULL); - g_value_init (g_value_array_get_nth (vals, 1), - dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, - dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_VALUE))); - g_value_take_boxed (g_value_array_get_nth (vals, 1), contact_status); - - g_hash_table_insert (presence_hash, GINT_TO_POINTER (handle), vals); - } - - return presence_hash; -} - - -/** - * emit_presence_update: - * @self: A #GabbleConnection - * @contact_handles: A zero-terminated array of #TpHandle for - * the contacts to emit presence for - * - * Emits the Telepathy PresenceUpdate signal with the current - * stored presence information for the given contact. - */ -static void -emit_presence_update (GabbleConnection *self, - const GArray *contact_handles) -{ - GHashTable *presence_hash; - - presence_hash = construct_presence_hash (self, contact_handles); - tp_svc_connection_interface_presence_emit_presence_update (self, - presence_hash); - g_hash_table_destroy (presence_hash); -} - - -/** - * emit_one_presence_update: - * Convenience function for calling emit_presence_update with one handle. - */ - -static void -emit_one_presence_update (GabbleConnection *self, - TpHandle handle) -{ - GArray *handles = g_array_sized_new (FALSE, FALSE, sizeof (TpHandle), 1); - - g_array_insert_val (handles, 0, handle); - emit_presence_update (self, handles); - g_array_free (handles, TRUE); -} - - - - -/** - * gabble_connection_add_status - * - * Implements D-Bus method AddStatus - * on interface org.freedesktop.Telepathy.Connection.Interface.Presence - * - * @error: Used to return a pointer to a GError detailing any error - * that occurred, D-Bus will throw the error only if this - * function returns FALSE. - * - * Returns: TRUE if successful, FALSE if an error was thrown. - */ -static void -gabble_connection_add_status (TpSvcConnectionInterfacePresence *iface, - const gchar *status, - GHashTable *parms, - DBusGMethodInvocation *context) -{ - TpBaseConnection *self = TP_BASE_CONNECTION (iface); - GError unimpl = { TP_ERRORS, TP_ERROR_NOT_IMPLEMENTED, - "Only one status is possible at a time with this protocol" }; - - TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (self, context); - - dbus_g_method_return_error (context, &unimpl); -} - - -/** - * gabble_connection_clear_status - * - * Implements D-Bus method ClearStatus - * on interface org.freedesktop.Telepathy.Connection.Interface.Presence - * - * @error: Used to return a pointer to a GError detailing any error - * that occurred, D-Bus will throw the error only if this - * function returns FALSE. - * - * Returns: TRUE if successful, FALSE if an error was thrown. - */ -static void -gabble_connection_clear_status (TpSvcConnectionInterfacePresence *iface, - DBusGMethodInvocation *context) -{ - GabbleConnection *self = GABBLE_CONNECTION (iface); - TpBaseConnection *base = (TpBaseConnection *)self; - GError *error = NULL; - gboolean ok; - gchar *resource; - gint8 priority; - - g_assert (GABBLE_IS_CONNECTION (self)); - - TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); - - g_object_get (self, - "resource", &resource, - "priority", &priority, - NULL); - gabble_presence_update (self->self_presence, resource, - GABBLE_PRESENCE_AVAILABLE, NULL, priority); - emit_one_presence_update (self, base->self_handle); - ok = _gabble_connection_signal_own_presence (self, &error); - - if (ok) - { - tp_svc_connection_interface_presence_return_from_clear_status (context); - } - else - { - dbus_g_method_return_error (context, error); - g_error_free (error); - } - - g_free (resource); -} - - -/** - * status_is_available - * - * Returns a boolean to indicate whether the given gabble status is - * available on this connection. - */ -static gboolean -status_is_available (GabbleConnection *conn, int status) -{ - g_assert (GABBLE_IS_CONNECTION (conn)); - g_assert (status < NUM_GABBLE_PRESENCES); - - if (gabble_statuses[status].presence_type == TP_CONNECTION_PRESENCE_TYPE_HIDDEN && - (conn->features & GABBLE_CONNECTION_FEATURES_PRESENCE_INVISIBLE) == 0) - return FALSE; - else - return TRUE; -} - - -/** - * gabble_connection_get_presence - * - * Implements D-Bus method GetPresence - * on interface org.freedesktop.Telepathy.Connection.Interface.Presence - * - * @context: The D-Bus invocation context to use to return values - * or throw an error. - */ -static void -gabble_connection_get_presence (TpSvcConnectionInterfacePresence *iface, - const GArray *contacts, - DBusGMethodInvocation *context) -{ - GabbleConnection *self = GABBLE_CONNECTION (iface); - TpBaseConnection *base = (TpBaseConnection *)self; - GHashTable *presence_hash; - GError *error = NULL; - TpHandleRepoIface *contact_handles = tp_base_connection_get_handles (base, - TP_HANDLE_TYPE_CONTACT); - - if (!tp_handles_are_valid (contact_handles, contacts, FALSE, &error)) - { - dbus_g_method_return_error (context, error); - g_error_free (error); - } - - presence_hash = construct_presence_hash (self, contacts); - tp_svc_connection_interface_presence_return_from_get_presence ( - context, presence_hash); - g_hash_table_destroy (presence_hash); -} - - -static GHashTable * -get_statuses_arguments () -{ - static GHashTable *arguments = NULL; - - if (arguments == NULL) - { - arguments = g_hash_table_new (g_str_hash, g_str_equal); - - g_hash_table_insert (arguments, "message", "s"); - g_hash_table_insert (arguments, "priority", "n"); - } - - return arguments; -} - - -/** - * gabble_connection_get_statuses - * - * Implements D-Bus method GetStatuses - * on interface org.freedesktop.Telepathy.Connection.Interface.Presence - * - * @error: Used to return a pointer to a GError detailing any error - * that occurred, D-Bus will throw the error only if this - * function returns FALSE. - * - * Returns: TRUE if successful, FALSE if an error was thrown. - */ -static void -gabble_connection_get_statuses (TpSvcConnectionInterfacePresence *iface, - DBusGMethodInvocation *context) -{ - GabbleConnection *self = GABBLE_CONNECTION (iface); - TpBaseConnection *base = (TpBaseConnection *)self; - GHashTable *ret; - GValueArray *status; - int i; - - g_assert (GABBLE_IS_CONNECTION (self)); - - TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); - - DEBUG ("called."); - - ret = g_hash_table_new_full (g_str_hash, g_str_equal, - NULL, (GDestroyNotify) g_value_array_free); - - for (i=0; i < NUM_GABBLE_PRESENCES; i++) - { - /* don't report the invisible presence if the server - * doesn't have the presence-invisible feature */ - if (!status_is_available (self, i)) - continue; - - status = g_value_array_new (5); - - g_value_array_append (status, NULL); - g_value_init (g_value_array_get_nth (status, 0), G_TYPE_UINT); - g_value_set_uint (g_value_array_get_nth (status, 0), - gabble_statuses[i].presence_type); - - g_value_array_append (status, NULL); - g_value_init (g_value_array_get_nth (status, 1), G_TYPE_BOOLEAN); - g_value_set_boolean (g_value_array_get_nth (status, 1), - gabble_statuses[i].self); - - g_value_array_append (status, NULL); - g_value_init (g_value_array_get_nth (status, 2), G_TYPE_BOOLEAN); - g_value_set_boolean (g_value_array_get_nth (status, 2), - gabble_statuses[i].exclusive); - - g_value_array_append (status, NULL); - g_value_init (g_value_array_get_nth (status, 3), - DBUS_TYPE_G_STRING_STRING_HASHTABLE); - g_value_set_static_boxed (g_value_array_get_nth (status, 3), - get_statuses_arguments ()); - - g_hash_table_insert (ret, (gchar*) gabble_statuses[i].name, status); - } - - tp_svc_connection_interface_presence_return_from_get_statuses ( - context, ret); - g_hash_table_destroy (ret); -} - - -/** - * gabble_connection_remove_status - * - * Implements D-Bus method RemoveStatus - * on interface org.freedesktop.Telepathy.Connection.Interface.Presence - * - * @error: Used to return a pointer to a GError detailing any error - * that occurred, D-Bus will throw the error only if this - * function returns FALSE. - * - * Returns: TRUE if successful, FALSE if an error was thrown. - */ -static void -gabble_connection_remove_status (TpSvcConnectionInterfacePresence *iface, - const gchar *status, - DBusGMethodInvocation *context) -{ - GabbleConnection *self = GABBLE_CONNECTION (iface); - TpBaseConnection *base = (TpBaseConnection *)self; - GabblePresence *presence = self->self_presence; - GError *error = NULL; - gchar *resource; - gint8 priority; - - g_assert (GABBLE_IS_CONNECTION (self)); - - TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); - - g_object_get (self, - "resource", &resource, - "priority", &priority, - NULL); - - if (strcmp (status, gabble_statuses[presence->status].name) == 0) - { - gabble_presence_update (presence, resource, - GABBLE_PRESENCE_AVAILABLE, NULL, priority); - emit_one_presence_update (self, base->self_handle); - if (_gabble_connection_signal_own_presence (self, &error)) - { - tp_svc_connection_interface_presence_return_from_remove_status ( - context); - } - else - { - dbus_g_method_return_error (context, error); - g_error_free (error); - } - } - else - { - GError nonexistent = { TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, - "Attempting to remove non-existent presence." }; - dbus_g_method_return_error (context, &nonexistent); - } - - g_free (resource); -} - - -/** - * gabble_connection_request_presence - * - * Implements D-Bus method RequestPresence - * on interface org.freedesktop.Telepathy.Connection.Interface.Presence - */ -static void -gabble_connection_request_presence (TpSvcConnectionInterfacePresence *iface, - const GArray *contacts, - DBusGMethodInvocation *context) -{ - GabbleConnection *self = GABBLE_CONNECTION (iface); - TpBaseConnection *base = (TpBaseConnection *)self; - GError *error = NULL; - TpHandleRepoIface *contact_handles = tp_base_connection_get_handles (base, - TP_HANDLE_TYPE_CONTACT); - - g_assert (GABBLE_IS_CONNECTION (self)); - - TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); - - if (!tp_handles_are_valid (contact_handles, contacts, FALSE, &error)) - { - dbus_g_method_return_error (context, error); - g_error_free (error); - } - - if (contacts->len) - emit_presence_update (self, contacts); - - tp_svc_connection_interface_presence_return_from_request_presence ( - context); -} - - -/** - * gabble_connection_set_last_activity_time - * - * Implements D-Bus method SetLastActivityTime - * on interface org.freedesktop.Telepathy.Connection.Interface.Presence - */ -static void -gabble_connection_set_last_activity_time (TpSvcConnectionInterfacePresence *iface, - guint time, - DBusGMethodInvocation *context) -{ - GabbleConnection *self = GABBLE_CONNECTION (iface); - TpBaseConnection *base = (TpBaseConnection *)self; - - g_assert (GABBLE_IS_CONNECTION (self)); - - TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); - - tp_svc_connection_interface_presence_return_from_set_last_activity_time ( - context); -} - - -struct _i_hate_g_hash_table_foreach -{ - GabbleConnection *conn; - GError **error; - gboolean retval; -}; - - -static void -setstatuses_foreach (gpointer key, gpointer value, gpointer user_data) -{ - struct _i_hate_g_hash_table_foreach *data = - (struct _i_hate_g_hash_table_foreach*) user_data; - TpBaseConnection *base = (TpBaseConnection *)data->conn; - gchar *resource; - int i; - - g_object_get (data->conn, "resource", &resource, NULL); - - for (i = 0; i < NUM_GABBLE_PRESENCES; i++) - { - if (0 == strcmp (gabble_statuses[i].name, (const gchar*) key)) - break; - } - - if (i < NUM_GABBLE_PRESENCES) - { - GHashTable *args = (GHashTable *)value; - GValue *message = g_hash_table_lookup (args, "message"); - GValue *priority = g_hash_table_lookup (args, "priority"); - const gchar *status = NULL; - gint8 prio; - - g_object_get (data->conn, "priority", &prio, NULL); - - if (!status_is_available (data->conn, i)) - { - DEBUG ("requested status %s is not available", (const gchar *) key); - g_set_error (data->error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE, - "requested status '%s' is not available on this connection", - (const gchar *) key); - data->retval = FALSE; - goto OUT; - } - - if (message) - { - if (!G_VALUE_HOLDS_STRING (message)) - { - DEBUG ("got a status message which was not a string"); - g_set_error (data->error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, - "Status argument 'message' requires a string"); - data->retval = FALSE; - goto OUT; - } - status = g_value_get_string (message); - } - - if (priority) - { - if (!G_VALUE_HOLDS_INT (priority)) - { - DEBUG ("got a priority value which was not a signed integer"); - g_set_error (data->error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, - "Status argument 'priority' requires a signed integer"); - data->retval = FALSE; - goto OUT; - } - prio = CLAMP (g_value_get_int (priority), G_MININT8, G_MAXINT8); - } - - gabble_presence_update (data->conn->self_presence, resource, i, status, - prio); - emit_one_presence_update (data->conn, base->self_handle); - data->retval = _gabble_connection_signal_own_presence (data->conn, - data->error); - } - else - { - DEBUG ("got unknown status identifier %s", (const gchar *) key); - g_set_error (data->error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, - "unknown status identifier: %s", (const gchar *) key); - data->retval = FALSE; - } - -OUT: - g_free (resource); -} - -/** - * gabble_connection_set_status - * - * Implements D-Bus method SetStatus - * on interface org.freedesktop.Telepathy.Connection.Interface.Presence - * - * @error: Used to return a pointer to a GError detailing any error - * that occurred, D-Bus will throw the error only if this - * function returns FALSE. - * - * Returns: TRUE if successful, FALSE if an error was thrown. - */ -static void -gabble_connection_set_status (TpSvcConnectionInterfacePresence *iface, - GHashTable *statuses, - DBusGMethodInvocation *context) -{ - GabbleConnection *self = GABBLE_CONNECTION (iface); - TpBaseConnection *base = (TpBaseConnection *)self; - struct _i_hate_g_hash_table_foreach data = { NULL, NULL, TRUE }; - GError *error = NULL; - - g_assert (GABBLE_IS_CONNECTION (self)); - - TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); - - if (g_hash_table_size (statuses) != 1) - { - GError invalid = { TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, - "Only one status may be set at a time in this protocol" }; - DEBUG ("got more than one status"); - dbus_g_method_return_error (context, &invalid); - return; - } - - data.conn = self; - data.error = &error; - g_hash_table_foreach (statuses, setstatuses_foreach, &data); - - if (data.retval) - { - tp_svc_connection_interface_presence_return_from_set_status ( - context); - } - else - { - dbus_g_method_return_error (context, error); - g_error_free (error); - } -} - - -static void -connection_presence_update_cb ( - GabblePresenceCache *cache, - TpHandle handle, - gpointer user_data) -{ - GabbleConnection *conn = GABBLE_CONNECTION (user_data); - - emit_one_presence_update (conn, handle); -} - - -static void -connection_status_changed_cb ( - GabbleConnection *conn, - TpConnectionStatus status, - TpConnectionStatusReason reason, - gpointer user_data) -{ - TpBaseConnection *base = (TpBaseConnection *) conn; - - if (status == TP_CONNECTION_STATUS_CONNECTED) - emit_one_presence_update (conn, base->self_handle); -} - - -void -conn_presence_init (GabbleConnection *conn) -{ - g_signal_connect (conn->presence_cache, "presence-update", - G_CALLBACK (connection_presence_update_cb), conn); - g_signal_connect (conn, "status-changed", - G_CALLBACK (connection_status_changed_cb), conn); -} - - -void -conn_presence_iface_init (gpointer g_iface, gpointer iface_data) -{ - TpSvcConnectionInterfacePresenceClass *klass = - (TpSvcConnectionInterfacePresenceClass *) g_iface; - -#define IMPLEMENT(x) tp_svc_connection_interface_presence_implement_##x (\ - klass, gabble_connection_##x) - IMPLEMENT(add_status); - IMPLEMENT(clear_status); - IMPLEMENT(get_presence); - IMPLEMENT(get_statuses); - IMPLEMENT(remove_status); - IMPLEMENT(request_presence); - IMPLEMENT(set_last_activity_time); - IMPLEMENT(set_status); -#undef IMPLEMENT -} diff --git a/src/conn-presence.h b/src/conn-presence.h deleted file mode 100644 index 50f656159..000000000 --- a/src/conn-presence.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * conn-presence.h - Header for Gabble connection presence interface - * Copyright (C) 2005-2007 Collabora Ltd. - * Copyright (C) 2005-2007 Nokia Corporation - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef __CONN_PRESENCE_H__ -#define __CONN_PRESENCE_H__ - -#include <glib.h> - -#include "gabble-connection.h" - -G_BEGIN_DECLS - -void conn_presence_init (GabbleConnection *conn); -void conn_presence_iface_init (gpointer g_iface, gpointer iface_data); - -G_END_DECLS - -#endif /* __CONN_PRESENCE_H__ */ - diff --git a/src/debug.c b/src/debug.c deleted file mode 100644 index 0c06a77e4..000000000 --- a/src/debug.c +++ /dev/null @@ -1,73 +0,0 @@ -#include "config.h" - -#ifdef ENABLE_DEBUG - -#include <stdarg.h> - -#include <glib.h> - -#include <telepathy-glib/debug.h> - -#include "debug.h" - -static GabbleDebugFlags flags = 0; - -static GDebugKey keys[] = { - { "presence", GABBLE_DEBUG_PRESENCE }, - { "groups", GABBLE_DEBUG_GROUPS }, - { "roster", GABBLE_DEBUG_ROSTER }, - { "disco", GABBLE_DEBUG_DISCO }, - { "properties", GABBLE_DEBUG_PROPERTIES }, - { "roomlist", GABBLE_DEBUG_ROOMLIST }, - { "media-channel", GABBLE_DEBUG_MEDIA }, - { "im", GABBLE_DEBUG_IM }, - { "muc", GABBLE_DEBUG_MUC }, - { "connection", GABBLE_DEBUG_CONNECTION }, - { "persist", GABBLE_DEBUG_PERSIST }, - { "vcard", GABBLE_DEBUG_VCARD }, - { "jid", GABBLE_DEBUG_JID }, - { 0, }, -}; - -void gabble_debug_set_flags_from_env () -{ - guint nkeys; - const gchar *flags_string; - - for (nkeys = 0; keys[nkeys].value; nkeys++); - - flags_string = g_getenv ("GABBLE_DEBUG"); - - if (flags_string) - { - tp_debug_set_flags_from_env ("GABBLE_DEBUG"); - gabble_debug_set_flags (g_parse_debug_string (flags_string, keys, - nkeys)); - } -} - -void gabble_debug_set_flags (GabbleDebugFlags new_flags) -{ - flags |= new_flags; -} - -gboolean gabble_debug_flag_is_set (GabbleDebugFlags flag) -{ - return flag & flags; -} - -void gabble_debug (GabbleDebugFlags flag, - const gchar *format, - ...) -{ - if (flag & flags) - { - va_list args; - va_start (args, format); - g_logv (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, format, args); - va_end (args); - } -} - -#endif /* ENABLE_DEBUG */ - diff --git a/src/debug.h b/src/debug.h deleted file mode 100644 index 522d1d715..000000000 --- a/src/debug.h +++ /dev/null @@ -1,70 +0,0 @@ - -#ifndef __DEBUG_H__ -#define __DEBUG_H_ - -#include "config.h" - -#ifdef ENABLE_DEBUG - -#include <glib.h> - -G_BEGIN_DECLS - -typedef enum -{ - GABBLE_DEBUG_PRESENCE = 1 << 0, - GABBLE_DEBUG_GROUPS = 1 << 1, - GABBLE_DEBUG_ROSTER = 1 << 2, - GABBLE_DEBUG_DISCO = 1 << 3, - GABBLE_DEBUG_PROPERTIES = 1 << 4, - GABBLE_DEBUG_ROOMLIST = 1 << 5, - GABBLE_DEBUG_MEDIA = 1 << 6, - GABBLE_DEBUG_MUC = 1 << 7, - GABBLE_DEBUG_CONNECTION = 1 << 8, - GABBLE_DEBUG_IM = 1 << 9, - GABBLE_DEBUG_PERSIST = 1 << 10, - GABBLE_DEBUG_VCARD = 1 << 11, - GABBLE_DEBUG_PIPELINE = 1 << 12, - GABBLE_DEBUG_JID = 1 << 13, -} GabbleDebugFlags; - -void gabble_debug_set_flags_from_env (); -void gabble_debug_set_flags (GabbleDebugFlags flags); -gboolean gabble_debug_flag_is_set (GabbleDebugFlags flag); -void gabble_debug (GabbleDebugFlags flag, const gchar *format, ...) - G_GNUC_PRINTF (2, 3); - -G_END_DECLS - -#ifdef DEBUG_FLAG - -#define DEBUG(format, ...) \ - gabble_debug (DEBUG_FLAG, "%s: " format, G_STRFUNC, ##__VA_ARGS__) - -#define DEBUGGING gabble_debug_flag_is_set (DEBUG_FLAG) - -#define NODE_DEBUG(n, s) \ -G_STMT_START { \ - gchar *debug_tmp = lm_message_node_to_string (n); \ - gabble_debug (DEBUG_FLAG, "%s: " s ":\n%s", G_STRFUNC, debug_tmp); \ - g_free (debug_tmp); \ -} G_STMT_END - -#endif /* DEBUG_FLAG */ - -#else /* ENABLE_DEBUG */ - -#ifdef DEBUG_FLAG - -#define DEBUG(format, ...) - -#define DEBUGGING 0 - -#define NODE_DEBUG(n, s) - -#endif /* DEBUG_FLAG */ - -#endif /* ENABLE_DEBUG */ - -#endif /* __DEBUG_H__ */ - diff --git a/src/disco.c b/src/disco.c deleted file mode 100644 index 6ba6f80bb..000000000 --- a/src/disco.c +++ /dev/null @@ -1,919 +0,0 @@ -/* - * disco.c - Source for Gabble service discovery - * - * Copyright (C) 2006 Collabora Ltd. - * Copyright (C) 2006 Nokia Corporation - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * -- LET'S DISCO!!! \o/ \o_ _o/ /\o/\ _/o/- -\o\_ -- - */ - -#include "disco.h" - -#define DBUS_API_SUBJECT_TO_CHANGE - -#include <dbus/dbus-glib.h> -#include <dbus/dbus-glib-lowlevel.h> -#include <stdlib.h> -#include <string.h> - -#define DEBUG_FLAG GABBLE_DEBUG_DISCO - -#include "debug.h" -#include "gabble-connection.h" -#include "gabble-error.h" -#include "namespaces.h" -#include <telepathy-glib/dbus.h> -#include "util.h" - -#define DEFAULT_REQUEST_TIMEOUT 20000 -#define DISCO_PIPELINE_SIZE 10 - -/* Properties */ -enum -{ - PROP_CONNECTION = 1, - LAST_PROPERTY -}; - -G_DEFINE_TYPE(GabbleDisco, gabble_disco, G_TYPE_OBJECT); - -typedef struct _GabbleDiscoPrivate GabbleDiscoPrivate; -struct _GabbleDiscoPrivate -{ - GabbleConnection *connection; - GSList *service_cache; - GList *requests; - gboolean dispose_has_run; -}; - -struct _GabbleDiscoRequest -{ - GabbleDisco *disco; - guint timer_id; - - GabbleDiscoType type; - gchar *jid; - gchar *node; - GabbleDiscoCb callback; - gpointer user_data; - GObject *bound_object; -}; - -GQuark -gabble_disco_error_quark (void) -{ - static GQuark quark = 0; - if (!quark) - quark = g_quark_from_static_string ("gabble-disco-error"); - return quark; -} - -#define GABBLE_DISCO_GET_PRIVATE(o) ((GabbleDiscoPrivate*)((o)->priv)); - -static void -gabble_disco_init (GabbleDisco *obj) -{ - GabbleDiscoPrivate *priv = - G_TYPE_INSTANCE_GET_PRIVATE (obj, GABBLE_TYPE_DISCO, GabbleDiscoPrivate); - obj->priv = priv; -} - -static GObject *gabble_disco_constructor (GType type, guint n_props, - GObjectConstructParam *props); -static void gabble_disco_set_property (GObject *object, guint property_id, - const GValue *value, GParamSpec *pspec); -static void gabble_disco_get_property (GObject *object, guint property_id, - GValue *value, GParamSpec *pspec); -static void gabble_disco_dispose (GObject *object); -static void gabble_disco_finalize (GObject *object); - -static void -gabble_disco_class_init (GabbleDiscoClass *gabble_disco_class) -{ - GObjectClass *object_class = G_OBJECT_CLASS (gabble_disco_class); - GParamSpec *param_spec; - - g_type_class_add_private (gabble_disco_class, sizeof (GabbleDiscoPrivate)); - - object_class->constructor = gabble_disco_constructor; - - object_class->get_property = gabble_disco_get_property; - object_class->set_property = gabble_disco_set_property; - - object_class->dispose = gabble_disco_dispose; - object_class->finalize = gabble_disco_finalize; - - param_spec = g_param_spec_object ("connection", "GabbleConnection object", - "Gabble connection object that owns this " - "XMPP Discovery object.", - GABBLE_TYPE_CONNECTION, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | - G_PARAM_STATIC_NICK | - G_PARAM_STATIC_BLURB); - g_object_class_install_property (object_class, PROP_CONNECTION, param_spec); -} - -static void -gabble_disco_get_property (GObject *object, - guint property_id, - GValue *value, - GParamSpec *pspec) -{ - GabbleDisco *chan = GABBLE_DISCO (object); - GabbleDiscoPrivate *priv = GABBLE_DISCO_GET_PRIVATE (chan); - - switch (property_id) { - case PROP_CONNECTION: - g_value_set_object (value, priv->connection); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } -} - -static void -gabble_disco_set_property (GObject *object, - guint property_id, - const GValue *value, - GParamSpec *pspec) -{ - GabbleDisco *chan = GABBLE_DISCO (object); - GabbleDiscoPrivate *priv = GABBLE_DISCO_GET_PRIVATE (chan); - - switch (property_id) { - case PROP_CONNECTION: - priv->connection = g_value_get_object (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } -} - -static void gabble_disco_conn_status_changed_cb (GabbleConnection *conn, - TpConnectionStatus status, TpConnectionStatusReason reason, gpointer data); - -static GObject * -gabble_disco_constructor (GType type, guint n_props, - GObjectConstructParam *props) -{ - GObject *obj; - GabbleDisco *disco; - GabbleDiscoPrivate *priv; - - obj = G_OBJECT_CLASS (gabble_disco_parent_class)-> constructor (type, - n_props, props); - disco = GABBLE_DISCO (obj); - priv = GABBLE_DISCO_GET_PRIVATE (disco); - - g_signal_connect (priv->connection, "status-changed", - G_CALLBACK (gabble_disco_conn_status_changed_cb), disco); - - return obj; -} - -static void cancel_request (GabbleDiscoRequest *request); - -void -gabble_disco_dispose (GObject *object) -{ - GabbleDisco *self = GABBLE_DISCO (object); - GabbleDiscoPrivate *priv = GABBLE_DISCO_GET_PRIVATE (self); - GSList *l; - DBusGProxy *bus_proxy; - bus_proxy = tp_get_bus_proxy (); - - if (priv->dispose_has_run) - return; - - priv->dispose_has_run = TRUE; - - DEBUG ("dispose called"); - - /* cancel request removes the element from the list after cancelling */ - while (priv->requests) - cancel_request (priv->requests->data); - - for (l = priv->service_cache; l; l = g_slist_next (l)) - { - GabbleDiscoItem *item = (GabbleDiscoItem *) l->data; - g_free ((char *) item->jid); - g_free ((char *) item->name); - g_free ((char *) item->type); - g_free ((char *) item->category); - g_hash_table_destroy (item->features); - g_free (item); - } - - g_slist_free (priv->service_cache); - priv->service_cache = NULL; - - if (G_OBJECT_CLASS (gabble_disco_parent_class)->dispose) - G_OBJECT_CLASS (gabble_disco_parent_class)->dispose (object); -} - -void -gabble_disco_finalize (GObject *object) -{ - DEBUG ("called with %p", object); - - G_OBJECT_CLASS (gabble_disco_parent_class)->finalize (object); -} - -/** - * gabble_disco_new: - * @conn: The #GabbleConnection to use for service discovery - * - * Creates an object to use for Jabber service discovery (DISCO) - * There should be one of these per connection - */ -GabbleDisco * -gabble_disco_new (GabbleConnection *conn) -{ - GabbleDisco *disco; - - g_return_val_if_fail (GABBLE_IS_CONNECTION (conn), NULL); - - disco = GABBLE_DISCO (g_object_new (GABBLE_TYPE_DISCO, - "connection", conn, - NULL)); - - return disco; -} - - -static void notify_delete_request (gpointer data, GObject *obj); - -static void -delete_request (GabbleDiscoRequest *request) -{ - GabbleDisco *disco = request->disco; - GabbleDiscoPrivate *priv; - - g_assert (NULL != request); - g_assert (GABBLE_IS_DISCO (disco)); - - priv = GABBLE_DISCO_GET_PRIVATE (disco); - - g_assert (NULL != g_list_find (priv->requests, request)); - - priv->requests = g_list_remove (priv->requests, request); - - if (NULL != request->bound_object) - { - g_object_weak_unref (request->bound_object, notify_delete_request, - request); - } - - if (0 != request->timer_id) - { - g_source_remove (request->timer_id); - } - - g_free (request->jid); - g_free (request->node); - g_slice_free (GabbleDiscoRequest, request); -} - -static gboolean -timeout_request (gpointer data) -{ - GabbleDiscoRequest *request = (GabbleDiscoRequest*) data; - GError *err /* doesn't need initializing */; - g_return_val_if_fail (data != NULL, FALSE); - - err = g_error_new (GABBLE_DISCO_ERROR, GABBLE_DISCO_ERROR_TIMEOUT, - "Request for %s on %s timed out", - (request->type == GABBLE_DISCO_TYPE_INFO)?"info":"items", - request->jid); - (request->callback)(request->disco, request, request->jid, request->node, - NULL, err, request->user_data); - g_error_free (err); - - request->timer_id = 0; - delete_request (request); - return FALSE; -} - -static void -cancel_request (GabbleDiscoRequest *request) -{ - GError *err /* doesn't need initializing */; - - g_assert (request != NULL); - - err = g_error_new (GABBLE_DISCO_ERROR, GABBLE_DISCO_ERROR_CANCELLED, - "Request for %s on %s cancelled", - (request->type == GABBLE_DISCO_TYPE_INFO)?"info":"items", - request->jid); - (request->callback)(request->disco, request, request->jid, request->node, - NULL, err, request->user_data); - g_error_free (err); - - delete_request (request); -} - -static LmHandlerResult -request_reply_cb (GabbleConnection *conn, LmMessage *sent_msg, - LmMessage *reply_msg, GObject *object, gpointer user_data) -{ - GabbleDiscoRequest *request = (GabbleDiscoRequest*) user_data; - GabbleDisco *disco = GABBLE_DISCO (object); - GabbleDiscoPrivate *priv = GABBLE_DISCO_GET_PRIVATE (disco); - LmMessageNode *query_node; - GError *err = NULL; - - g_assert (request); - - if (!g_list_find (priv->requests, request)) - return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; - - query_node = lm_message_node_get_child (reply_msg->node, "query"); - - if (lm_message_get_sub_type (reply_msg) == LM_MESSAGE_SUB_TYPE_ERROR) - { - LmMessageNode *error_node; - - error_node = lm_message_node_get_child (reply_msg->node, "error"); - if (error_node) - { - err = gabble_xmpp_error_to_g_error ( - gabble_xmpp_error_from_node (error_node)); - } - - if (err == NULL) - { - err = g_error_new (GABBLE_DISCO_ERROR, - GABBLE_DISCO_ERROR_UNKNOWN, - "an unknown error occurred"); - } - } - else if (NULL == query_node) - { - err = g_error_new (GABBLE_DISCO_ERROR, GABBLE_DISCO_ERROR_UNKNOWN, - "disco response contained no <query> node"); - } - - request->callback (request->disco, request, request->jid, request->node, - query_node, err, request->user_data); - delete_request (request); - - if (err) - g_error_free (err); - - return LM_HANDLER_RESULT_REMOVE_MESSAGE; -} - -static void -notify_delete_request (gpointer data, GObject *obj) -{ - GabbleDiscoRequest *request = (GabbleDiscoRequest *) data; - request->bound_object = NULL; - delete_request (request); -} - -/** - * gabble_disco_request: - * @self: #GabbleDisco object to use for request - * @type: type of request - * @jid: Jabber ID to request on - * @node: node to request on @jid, or NULL - * @callback: #GabbleDiscoCb to call on request fullfilment - * @object: GObject to bind request to. the callback will not be - * called if this object has been unrefed. NULL if not needed - * @error: #GError to return a telepathy error in if unable to make - * request, NULL if unneeded. - * - * Make a disco request on the given jid with the default timeout. - */ -GabbleDiscoRequest * -gabble_disco_request (GabbleDisco *self, GabbleDiscoType type, - const gchar *jid, const char *node, - GabbleDiscoCb callback, gpointer user_data, - GObject *object, GError **error) -{ - return gabble_disco_request_with_timeout (self, type, jid, node, - DEFAULT_REQUEST_TIMEOUT, - callback, user_data, - object, error); -} - -/** - * gabble_disco_request_with_timeout: - * @self: #GabbleDisco object to use for request - * @type: type of request - * @jid: Jabber ID to request on - * @node: node to request on @jid, or NULL - * @timeout: the time until the request fails, in milliseconds (1/1000ths of - * a second) - * @callback: #GabbleDiscoCb to call on request fullfilment - * @object: GObject to bind request to. the callback will not be - * called if this object has been unrefed. NULL if not needed - * @error: #GError to return a telepathy error in if unable to make - * request, NULL if unneeded. - * - * Make a disco request on the given jid, which will fail unless a reply - * is received within the given timeout interval. - */ -GabbleDiscoRequest * -gabble_disco_request_with_timeout (GabbleDisco *self, GabbleDiscoType type, - const gchar *jid, const char *node, - guint timeout, GabbleDiscoCb callback, - gpointer user_data, GObject *object, - GError **error) -{ - GabbleDiscoPrivate *priv = GABBLE_DISCO_GET_PRIVATE (self); - GabbleDiscoRequest *request; - LmMessage *msg; - LmMessageNode *lm_node; - const gchar *xmlns; - - request = g_slice_new0 (GabbleDiscoRequest); - request->disco = self; - request->type = type; - request->jid = g_strdup (jid); - if (node) - request->node = g_strdup (node); - request->callback = callback; - request->user_data = user_data; - request->bound_object = object; - - if (NULL != object) - g_object_weak_ref (object, notify_delete_request, request); - - DEBUG ("Creating disco request %p for %s", - request, request->jid); - - priv->requests = g_list_prepend (priv->requests, request); - msg = lm_message_new_with_sub_type (jid, LM_MESSAGE_TYPE_IQ, - LM_MESSAGE_SUB_TYPE_GET); - lm_node = lm_message_node_add_child (msg->node, "query", NULL); - - switch (type) { - case GABBLE_DISCO_TYPE_INFO: - xmlns = NS_DISCO_INFO; - break; - case GABBLE_DISCO_TYPE_ITEMS: - xmlns = NS_DISCO_ITEMS; - break; - default: - g_assert_not_reached (); - return NULL; - } - - lm_message_node_set_attribute (lm_node, "xmlns", xmlns); - - if (node) - { - lm_message_node_set_attribute (lm_node, "node", node); - } - - if (! _gabble_connection_send_with_reply (priv->connection, msg, - request_reply_cb, G_OBJECT(self), request, error)) - { - delete_request (request); - lm_message_unref (msg); - return NULL; - } - else - { - request->timer_id = - g_timeout_add (timeout, timeout_request, request); - lm_message_unref (msg); - return request; - } -} - -void -gabble_disco_cancel_request (GabbleDisco *disco, GabbleDiscoRequest *request) -{ - GabbleDiscoPrivate *priv; - - g_return_if_fail (GABBLE_IS_DISCO (disco)); - g_return_if_fail (NULL != request); - - priv = GABBLE_DISCO_GET_PRIVATE (disco); - - g_return_if_fail (NULL != g_list_find (priv->requests, request)); - - cancel_request (request); -} - -/* Disco pipeline */ - - -typedef struct _GabbleDiscoPipeline GabbleDiscoPipeline; -struct _GabbleDiscoPipeline { - GabbleDisco *disco; - gpointer user_data; - GabbleDiscoPipelineCb callback; - GabbleDiscoEndCb end_callback; - GPtrArray *disco_pipeline; - GHashTable *remaining_items; - GabbleDiscoRequest *list_request; - gboolean running; -}; - -static void -gabble_disco_fill_pipeline (GabbleDisco *disco, GabbleDiscoPipeline *pipeline); - -static void -item_info_cb (GabbleDisco *disco, - GabbleDiscoRequest *request, - const gchar *jid, - const gchar *node, - LmMessageNode *result, - GError *error, - gpointer user_data) -{ - LmMessageNode *identity, *feature, *field, *value_node; - const char *category, *type, *var, *name, *value; - GHashTable *keys; - GabbleDiscoItem item; - - GabbleDiscoPipeline *pipeline = (GabbleDiscoPipeline *) user_data; - - g_ptr_array_remove_fast (pipeline->disco_pipeline, request); - - if (error) - { - DEBUG ("got error %s", error->message); - goto done; - } - - identity = lm_message_node_get_child (result, "identity"); - if (NULL == identity) - goto done; - - name = lm_message_node_get_attribute (identity, "name"); - if (NULL == name) - goto done; - - category = lm_message_node_get_attribute (identity, "category"); - if (NULL == category) - goto done; - - type = lm_message_node_get_attribute (identity, "type"); - if (NULL == type) - goto done; - - DEBUG ("got item identity, jid=%s, name=%s, category=%s, type=%s", - jid, name, category, type); - - keys = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); - - for (feature = result->children; feature; feature = feature->next) - { - if (0 == strcmp (feature->name, "feature")) - { - var = lm_message_node_get_attribute (feature, "var"); - if (var) - g_hash_table_insert (keys, g_strdup (var), NULL); - } - else if (0 == strcmp (feature->name, "x")) - { - if (lm_message_node_has_namespace (feature, NS_X_DATA, NULL)) - { - for (field = feature->children; - field; field = field->next) - { - if (0 != strcmp (field->name, "field")) - continue; - - var = lm_message_node_get_attribute (field, "var"); - if (NULL == var) - continue; - - value_node = lm_message_node_get_child (field, "value"); - if (NULL == value_node) - continue; - - value = lm_message_node_get_value (value_node); - if (NULL == value) - continue; - - g_hash_table_insert (keys, g_strdup (var), g_strdup (value)); - } - } - } - } - - item.jid = jid; - item.name = name; - item.category = category; - item.type = type; - item.features = keys; - - pipeline->callback (pipeline, &item, pipeline->user_data); - g_hash_table_destroy (keys); - -done: - gabble_disco_fill_pipeline (disco, pipeline); - - return; -} - - -static gboolean -return_true (gpointer key, gpointer value, gpointer data) -{ - return TRUE; -} - -static void -gabble_disco_fill_pipeline (GabbleDisco *disco, GabbleDiscoPipeline *pipeline) -{ - if (!pipeline->running) - { - DEBUG ("pipeline not running, not refilling"); - } - else - { - /* send disco requests for the JIDs in the remaining_items hash table - * until there are DISCO_PIPELINE_SIZE requests in progress */ - while (pipeline->disco_pipeline->len < DISCO_PIPELINE_SIZE) - { - gchar *jid; - GabbleDiscoRequest *request; - - jid = (gchar *) g_hash_table_find (pipeline->remaining_items, - return_true, NULL); - if (NULL == jid) - break; - - request = gabble_disco_request (disco, - GABBLE_DISCO_TYPE_INFO, jid, NULL, item_info_cb, pipeline, - G_OBJECT(disco), NULL); - - g_ptr_array_add (pipeline->disco_pipeline, request); - - /* frees jid */ - g_hash_table_remove (pipeline->remaining_items, jid); - } - - if (0 == pipeline->disco_pipeline->len) - { - /* signal that the pipeline has finished */ - pipeline->running = FALSE; - pipeline->end_callback (pipeline, pipeline->user_data); - } - } -} - - -static void -disco_items_cb (GabbleDisco *disco, - GabbleDiscoRequest *request, - const gchar *jid, - const gchar *node, - LmMessageNode *result, - GError *error, - gpointer user_data) -{ - LmMessageNode *iter; - const char *item_jid; - gpointer key, value; - GabbleDiscoPipeline *pipeline = (GabbleDiscoPipeline *) user_data; - - pipeline->list_request = NULL; - - if (error) - { - DEBUG ("Got error on items request: %s", error->message); - goto out; - } - - iter = result->children; - - for (; iter; iter = iter->next) - { - if (0 != strcmp (iter->name, "item")) - continue; - - item_jid = lm_message_node_get_attribute (iter, "jid"); - - if (NULL != item_jid && - !g_hash_table_lookup_extended (pipeline->remaining_items, item_jid, - &key, &value)) - { - gchar *tmp = g_strdup (item_jid); - DEBUG ("discovered service item: %s", tmp); - g_hash_table_insert (pipeline->remaining_items, tmp, tmp); - } - } - -out: - gabble_disco_fill_pipeline (disco, pipeline); -} - -/** - * gabble_disco_pipeline_init: - * @disco: disco object to use in the pipeline - * @callback: GFunc to call on request fullfilment - * @user_data: the usual - * - * Prepares the pipeline for making the ITEM request on the server and - * subsequent INFO elements on returned items. - * - * GabbleDiscoPipeline is opaque structure for the user. - */ -gpointer gabble_disco_pipeline_init (GabbleDisco *disco, - GabbleDiscoPipelineCb callback, - GabbleDiscoEndCb end_callback, - gpointer user_data) -{ - GabbleDiscoPipeline *pipeline = g_new (GabbleDiscoPipeline, 1); - pipeline->user_data = user_data; - pipeline->callback = callback; - pipeline->end_callback = end_callback; - pipeline->disco_pipeline = g_ptr_array_sized_new (DISCO_PIPELINE_SIZE); - pipeline->remaining_items = g_hash_table_new_full (g_str_hash, g_str_equal, - g_free, NULL); - pipeline->running = TRUE; - pipeline->disco = disco; - - return pipeline; -} - -/** - * gabble_disco_pipeline_run: - * @self: reference to the pipeline structure - * @server: server to query - * - * Makes ITEMS request on the server, and afterwards queries for INFO - * on each item. INFO queries are pipelined. The item properties are stored - * in hash table parameter to the callback function. The user is responsible - * for destroying the hash table after it's done with. - * - * Upon returning all the results, the end_callback is called with - * reference to the pipeline. - */ -void -gabble_disco_pipeline_run (gpointer self, const char *server) -{ - GabbleDiscoPipeline *pipeline = (GabbleDiscoPipeline *) self; - - pipeline->running = TRUE; - - pipeline->list_request = gabble_disco_request (pipeline->disco, - GABBLE_DISCO_TYPE_ITEMS, server, NULL, disco_items_cb, pipeline, - G_OBJECT (pipeline->disco), NULL); -} - - -/** - * gabble_disco_pipeline_cancel: - * @pipeline: pipeline to cancel - * - * Flushes the pipeline (cancels all pending disco requests) and - * destroys it. - */ -void -gabble_disco_pipeline_destroy (gpointer self) -{ - GabbleDiscoPipeline *pipeline = (GabbleDiscoPipeline *) self; - - pipeline->running = FALSE; - - if (pipeline->list_request != NULL) - { - gabble_disco_cancel_request (pipeline->disco, pipeline->list_request); - pipeline->list_request = NULL; - } - - /* iterate using a while loop otherwise we're modifying - * the array as we iterate it, and miss things! */ - while (pipeline->disco_pipeline->len > 0) - { - GabbleDiscoRequest *request = - g_ptr_array_index (pipeline->disco_pipeline, 0); - gabble_disco_cancel_request (pipeline->disco, request); - } - - g_hash_table_destroy (pipeline->remaining_items); - g_ptr_array_free (pipeline->disco_pipeline, TRUE); - g_free (pipeline); -} - - -static void -service_feature_copy_one (gpointer k, gpointer v, gpointer user_data) -{ - char *key = (char *) k; - char *value = (char *) v; - - GHashTable *target = (GHashTable *) user_data; - g_hash_table_insert (target, g_strdup (key), g_strdup (value)); -} - -/* Service discovery */ -static void -services_cb (gpointer pipeline, GabbleDiscoItem *item, gpointer user_data) -{ - GabbleDisco *disco = GABBLE_DISCO (user_data); - GabbleDiscoPrivate *priv = GABBLE_DISCO_GET_PRIVATE (disco); - GabbleDiscoItem *my_item = g_new0 (GabbleDiscoItem, 1); - - my_item->jid = g_strdup (item->jid); - my_item->name = g_strdup (item->name); - my_item->type = g_strdup (item->type); - my_item->category = g_strdup (item->category); - - my_item->features = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, - NULL); - g_hash_table_foreach (item->features, service_feature_copy_one, - my_item->features); - - priv->service_cache = g_slist_prepend (priv->service_cache, my_item); -} - -static void -end_cb (gpointer pipeline, gpointer user_data) -{ - GabbleDisco *disco = GABBLE_DISCO (user_data); - GabbleDiscoPrivate *priv = GABBLE_DISCO_GET_PRIVATE (disco); - - gabble_disco_pipeline_destroy (pipeline); - priv->service_cache = g_slist_reverse (priv->service_cache); - - /* FIXME - service discovery done - signal that somehow */ -} - -static void -gabble_disco_conn_status_changed_cb (GabbleConnection *conn, - TpConnectionStatus status, - TpConnectionStatusReason reason, - gpointer data) -{ - GabbleDisco *disco = GABBLE_DISCO (data); - GabbleDiscoPrivate *priv = GABBLE_DISCO_GET_PRIVATE (disco); - - if (status == TP_CONNECTION_STATUS_CONNECTED) - { - char *server; - - g_object_get (priv->connection, "stream-server", &server, NULL); - - g_assert (server != NULL); - - DEBUG ("connected, initiating service discovery on %s", server); - gpointer pipeline = gabble_disco_pipeline_init (disco, services_cb, - end_cb, disco); - gabble_disco_pipeline_run (pipeline, server); - - g_free (server); - } -} - -const GabbleDiscoItem * -gabble_disco_service_find (GabbleDisco *disco, - const char *type, - const char *category, - const char *feature) -{ - GabbleDiscoPrivate *priv; - GSList *l; - - g_assert (GABBLE_IS_DISCO (disco)); - priv = GABBLE_DISCO_GET_PRIVATE (disco); - - for (l = priv->service_cache; l; l = g_slist_next (l)) - { - GabbleDiscoItem *item = (GabbleDiscoItem *) l->data; - gboolean selected = TRUE; - - if (type != NULL && tp_strdiff (type, item->type)) - selected = FALSE; - - if (category != NULL && tp_strdiff (category, item->category)) - selected = FALSE; - - if (feature != NULL) - { - gpointer k, v; - if (!g_hash_table_lookup_extended (item->features, feature, &k, &v)) - selected = FALSE; - } - - if (selected) - return item; - } - - return NULL; -} diff --git a/src/disco.h b/src/disco.h deleted file mode 100644 index 533e4a212..000000000 --- a/src/disco.h +++ /dev/null @@ -1,139 +0,0 @@ -/* - * disco.h - Headers for Gabble service discovery - * - * Copyright (C) 2006 Collabora Ltd. - * Copyright (C) 2006 Nokia Corporation - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * -- LET'S DISCO!!! \o/ \o_ _o/ /\o/\ _/o/- -\o\_ -- - */ - -#ifndef __GABBLE_DISCO_H__ -#define __GABBLE_DISCO_H__ - -#include <glib-object.h> -#include <loudmouth/loudmouth.h> - -#include "gabble-types.h" - -G_BEGIN_DECLS - -typedef enum -{ - GABBLE_DISCO_TYPE_INFO, - GABBLE_DISCO_TYPE_ITEMS -} GabbleDiscoType; - -typedef struct _GabbleDiscoClass GabbleDiscoClass; -typedef struct _GabbleDiscoRequest GabbleDiscoRequest; - -/** - * GabbleDiscoError: - * @GABBLE_DISCO_ERROR_CANCELLED: The DISCO request was cancelled - * @GABBLE_DISCO_ERROR_TIMEOUT: The DISCO request timed out - * @GABBLE_DISCO_ERROR_UNKNOWN: An unknown error occured - */ -typedef enum -{ - GABBLE_DISCO_ERROR_CANCELLED, - GABBLE_DISCO_ERROR_TIMEOUT, - GABBLE_DISCO_ERROR_UNKNOWN -} GabbleDiscoError; - -GQuark gabble_disco_error_quark (void); -#define GABBLE_DISCO_ERROR gabble_disco_error_quark () - -GType gabble_disco_get_type (void); - -/* TYPE MACROS */ -#define GABBLE_TYPE_DISCO \ - (gabble_disco_get_type ()) -#define GABBLE_DISCO(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_DISCO, GabbleDisco)) -#define GABBLE_DISCO_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_DISCO, GabbleDiscoClass)) -#define GABBLE_IS_DISCO(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_DISCO)) -#define GABBLE_IS_DISCO_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_DISCO)) -#define GABBLE_DISCO_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_DISCO, GabbleDiscoClass)) - -struct _GabbleDiscoClass { - GObjectClass parent_class; -}; - -struct _GabbleDisco { - GObject parent; - gpointer priv; -}; - -typedef void (*GabbleDiscoCb)(GabbleDisco *self, GabbleDiscoRequest *request, - const gchar *jid, const gchar *node, LmMessageNode *query_result, - GError* error, gpointer user_data); - -GabbleDisco *gabble_disco_new (GabbleConnection *); - -GabbleDiscoRequest *gabble_disco_request (GabbleDisco *self, - GabbleDiscoType type, const gchar *jid, const char *node, - GabbleDiscoCb callback, gpointer user_data, GObject *object, - GError **error); -GabbleDiscoRequest *gabble_disco_request_with_timeout (GabbleDisco *self, - GabbleDiscoType type, const gchar *jid, const char *node, - guint timeout, GabbleDiscoCb callback, gpointer user_data, - GObject *object, GError **error); - -void gabble_disco_cancel_request (GabbleDisco *, GabbleDiscoRequest *); - -/* Pipelines */ - -typedef struct _GabbleDiscoItem GabbleDiscoItem; - -struct _GabbleDiscoItem { - const gchar *jid; - const char *name; - const char *type; - const char *category; - GHashTable *features; -}; - -typedef void (*GabbleDiscoPipelineCb)(gpointer pipeline, - GabbleDiscoItem *item, - gpointer user_data); - -typedef void (*GabbleDiscoEndCb)(gpointer pipeline, - gpointer user_data); - -gpointer gabble_disco_pipeline_init (GabbleDisco *disco, - GabbleDiscoPipelineCb callback, - GabbleDiscoEndCb end_callback, - gpointer user_data); - -void gabble_disco_pipeline_run (gpointer self, const char *server); -void gabble_disco_pipeline_destroy (gpointer self); - -/* Service discovery */ - -void gabble_disco_service_discovery (GabbleDisco *disco, const char *server); -const GabbleDiscoItem * -gabble_disco_service_find (GabbleDisco *disco, - const char *type, - const char *category, - const char *feature); - -G_END_DECLS - -#endif diff --git a/src/gabble-connection-manager.c b/src/gabble-connection-manager.c deleted file mode 100644 index 32fbdd18b..000000000 --- a/src/gabble-connection-manager.c +++ /dev/null @@ -1,286 +0,0 @@ -/* - * gabble-connection-manager.c - Source for GabbleConnectionManager - * Copyright (C) 2005-2007 Collabora Ltd. - * Copyright (C) 2005-2007 Nokia Corporation - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "gabble-connection-manager.h" - -#include <dbus/dbus-protocol.h> -#include <dbus/dbus-glib.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include "gabble-connection.h" -#include <telepathy-glib/dbus.h> -#include <telepathy-glib/errors.h> - -G_DEFINE_TYPE(GabbleConnectionManager, - gabble_connection_manager, - TP_TYPE_BASE_CONNECTION_MANAGER) - -/* type definition stuff */ - -static void -gabble_connection_manager_init (GabbleConnectionManager *self) -{ -} - -static TpBaseConnection *_gabble_connection_manager_new_connection ( - TpBaseConnectionManager *self, const gchar *proto, - TpIntSet *params_present, void *parsed_params, GError **error); - -static void -gabble_connection_manager_class_init (GabbleConnectionManagerClass *klass) -{ - TpBaseConnectionManagerClass *base_class = - (TpBaseConnectionManagerClass *)klass; - - base_class->new_connection = _gabble_connection_manager_new_connection; - base_class->cm_dbus_name = "gabble"; - base_class->protocol_params = gabble_protocols; -} - -/* private data */ - -typedef struct _GabbleParams GabbleParams; - -struct _GabbleParams { - gchar *account; - gchar *password; - gchar *server; - gchar *resource; - gint priority; - guint port; - gboolean old_ssl; - gboolean do_register; - gboolean low_bandwidth; - gchar *https_proxy_server; - guint https_proxy_port; - gchar *fallback_conference_server; - gchar *stun_server; - guint stun_port; - gboolean ignore_ssl_errors; - gchar *alias; - gchar *auth_mac; - gchar *auth_btid; -}; - -enum { - JABBER_PARAM_ACCOUNT = 0, - JABBER_PARAM_PASSWORD, - JABBER_PARAM_SERVER, - JABBER_PARAM_RESOURCE, - JABBER_PARAM_PRIORITY, - JABBER_PARAM_PORT, - JABBER_PARAM_OLD_SSL, - JABBER_PARAM_REGISTER, - JABBER_PARAM_LOW_BANDWIDTH, - JABBER_PARAM_HTTPS_PROXY_SERVER, - JABBER_PARAM_HTTPS_PROXY_PORT, - JABBER_PARAM_FALLBACK_CONFERENCE_SERVER, - JABBER_PARAM_STUN_SERVER, - JABBER_PARAM_STUN_PORT, - JABBER_PARAM_IGNORE_SSL_ERRORS, - JABBER_PARAM_ALIAS, - JABBER_PARAM_AUTH_MAC, - JABBER_PARAM_AUTH_BTID, - LAST_JABBER_PARAM -}; - -static const TpCMParamSpec jabber_params[] = { - { "account", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, - TP_CONN_MGR_PARAM_FLAG_REQUIRED | TP_CONN_MGR_PARAM_FLAG_REGISTER, NULL, - G_STRUCT_OFFSET(GabbleParams, account), - /* FIXME: validate the JID according to the RFC */ - tp_cm_param_filter_string_nonempty, NULL }, - { "password", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, - TP_CONN_MGR_PARAM_FLAG_REQUIRED | TP_CONN_MGR_PARAM_FLAG_REGISTER, NULL, - G_STRUCT_OFFSET(GabbleParams, password), NULL, NULL }, - - { "server", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, 0, NULL, - G_STRUCT_OFFSET(GabbleParams, server), - /* FIXME: validate the server properly */ - tp_cm_param_filter_string_nonempty, NULL }, - - { "resource", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, - TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, GABBLE_PARAMS_DEFAULT_RESOURCE, - G_STRUCT_OFFSET(GabbleParams, resource), - /* FIXME: validate the resource according to the RFC */ - tp_cm_param_filter_string_nonempty, NULL }, - - { "priority", DBUS_TYPE_INT16_AS_STRING, G_TYPE_INT, - TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, GINT_TO_POINTER(0), - G_STRUCT_OFFSET(GabbleParams, priority), NULL, NULL }, - - { "port", DBUS_TYPE_UINT16_AS_STRING, G_TYPE_UINT, - TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, - GINT_TO_POINTER(GABBLE_PARAMS_DEFAULT_PORT), - G_STRUCT_OFFSET(GabbleParams, port), - tp_cm_param_filter_uint_nonzero, NULL }, - - { "old-ssl", DBUS_TYPE_BOOLEAN_AS_STRING, G_TYPE_BOOLEAN, - TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, GINT_TO_POINTER(FALSE), - G_STRUCT_OFFSET(GabbleParams, old_ssl), NULL, NULL }, - - { "register", DBUS_TYPE_BOOLEAN_AS_STRING, G_TYPE_BOOLEAN, - TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, GINT_TO_POINTER(FALSE), - G_STRUCT_OFFSET(GabbleParams, do_register), NULL, NULL }, - - { "low-bandwidth", DBUS_TYPE_BOOLEAN_AS_STRING, G_TYPE_BOOLEAN, - TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, GINT_TO_POINTER(FALSE), - G_STRUCT_OFFSET(GabbleParams, low_bandwidth), NULL, NULL }, - - { "https-proxy-server", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, 0, NULL, - G_STRUCT_OFFSET(GabbleParams, https_proxy_server), - /* FIXME: validate properly */ - tp_cm_param_filter_string_nonempty, NULL }, - { "https-proxy-port", DBUS_TYPE_UINT16_AS_STRING, G_TYPE_UINT, - TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, - GINT_TO_POINTER(GABBLE_PARAMS_DEFAULT_HTTPS_PROXY_PORT), - G_STRUCT_OFFSET(GabbleParams, https_proxy_port), - tp_cm_param_filter_uint_nonzero, NULL }, - - { "fallback-conference-server", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, - 0, NULL, G_STRUCT_OFFSET(GabbleParams, fallback_conference_server), - /* FIXME: validate properly */ - tp_cm_param_filter_string_nonempty, NULL }, - - { "stun-server", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, 0, NULL, - G_STRUCT_OFFSET(GabbleParams, stun_server), - /* FIXME: validate properly */ - tp_cm_param_filter_string_nonempty, NULL }, - { "stun-port", DBUS_TYPE_UINT16_AS_STRING, G_TYPE_UINT, - TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, - GINT_TO_POINTER(GABBLE_PARAMS_DEFAULT_STUN_PORT), - G_STRUCT_OFFSET(GabbleParams, stun_port), - tp_cm_param_filter_uint_nonzero, NULL }, - - { "ignore-ssl-errors", DBUS_TYPE_BOOLEAN_AS_STRING, G_TYPE_BOOLEAN, - TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT, GINT_TO_POINTER(FALSE), - G_STRUCT_OFFSET(GabbleParams, ignore_ssl_errors), NULL, NULL }, - - { "alias", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, 0, NULL, - G_STRUCT_OFFSET(GabbleParams, alias), - /* setting a 0-length alias makes no sense */ - tp_cm_param_filter_string_nonempty, NULL }, - - { "mac", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, 0, NULL, - G_STRUCT_OFFSET(GabbleParams, auth_mac), NULL, NULL }, - { "btid", DBUS_TYPE_STRING_AS_STRING, G_TYPE_STRING, 0, NULL, - G_STRUCT_OFFSET(GabbleParams, auth_btid), NULL, NULL }, - - { NULL, NULL, 0, 0, NULL, 0 } -}; - -static void * -alloc_params (void) -{ - return g_slice_new0 (GabbleParams); -} - -static void -free_params (void *p) -{ - GabbleParams *params = (GabbleParams *)p; - - g_free (params->account); - g_free (params->password); - g_free (params->server); - g_free (params->resource); - g_free (params->https_proxy_server); - g_free (params->fallback_conference_server); - g_free (params->stun_server); - g_free (params->alias); - g_free (params->auth_mac); - g_free (params->auth_btid); - - g_slice_free (GabbleParams, params); -} - -const TpCMProtocolSpec gabble_protocols[] = { - { "jabber", jabber_params, alloc_params, free_params }, - { NULL, NULL } -}; - -#define SET_PROPERTY_IF_PARAM_SET(prop, param, member) \ - if (tp_intset_is_member (params_present, param)) \ - { \ - g_object_set (conn, prop, member, NULL); \ - } - -static TpBaseConnection * -_gabble_connection_manager_new_connection (TpBaseConnectionManager *self, - const gchar *proto, - TpIntSet *params_present, - void *parsed_params, - GError **error) -{ - GabbleConnection *conn; - GabbleParams *params = (GabbleParams *)parsed_params; - - g_assert (GABBLE_IS_CONNECTION_MANAGER (self)); - - conn = g_object_new (GABBLE_TYPE_CONNECTION, - "protocol", proto, - "password", params->password, - NULL); - - SET_PROPERTY_IF_PARAM_SET ("connect-server", JABBER_PARAM_SERVER, - params->server); - SET_PROPERTY_IF_PARAM_SET ("resource", JABBER_PARAM_RESOURCE, - params->resource); - SET_PROPERTY_IF_PARAM_SET ("priority", JABBER_PARAM_PRIORITY, - (gint8) CLAMP (params->priority, G_MININT8, G_MAXINT8)); - SET_PROPERTY_IF_PARAM_SET ("port", JABBER_PARAM_PORT, params->port); - SET_PROPERTY_IF_PARAM_SET ("old-ssl", JABBER_PARAM_OLD_SSL, params->old_ssl); - SET_PROPERTY_IF_PARAM_SET ("register", JABBER_PARAM_REGISTER, - params->do_register); - SET_PROPERTY_IF_PARAM_SET ("low-bandwidth", JABBER_PARAM_LOW_BANDWIDTH, - params->low_bandwidth); - SET_PROPERTY_IF_PARAM_SET ("https-proxy-server", - JABBER_PARAM_HTTPS_PROXY_SERVER, - params->https_proxy_server); - SET_PROPERTY_IF_PARAM_SET ("https-proxy-port", JABBER_PARAM_HTTPS_PROXY_PORT, - params->https_proxy_port); - SET_PROPERTY_IF_PARAM_SET ("fallback-conference-server", - JABBER_PARAM_FALLBACK_CONFERENCE_SERVER, - params->fallback_conference_server); - SET_PROPERTY_IF_PARAM_SET ("stun-server", JABBER_PARAM_STUN_SERVER, - params->stun_server); - SET_PROPERTY_IF_PARAM_SET ("stun-port", JABBER_PARAM_STUN_PORT, - params->stun_port); - SET_PROPERTY_IF_PARAM_SET ("ignore-ssl-errors", - JABBER_PARAM_IGNORE_SSL_ERRORS, - params->ignore_ssl_errors); - SET_PROPERTY_IF_PARAM_SET ("alias", JABBER_PARAM_ALIAS, params->alias); - SET_PROPERTY_IF_PARAM_SET ("auth-mac", JABBER_PARAM_AUTH_MAC, - params->auth_mac); - SET_PROPERTY_IF_PARAM_SET ("auth-btid", JABBER_PARAM_AUTH_BTID, - params->auth_btid); - - /* split up account into username, stream-server and resource */ - if (!_gabble_connection_set_properties_from_account (conn, params->account, - error)) - { - g_object_unref (G_OBJECT (conn)); - conn = NULL; - } - - return (TpBaseConnection *)conn; -} diff --git a/src/gabble-connection-manager.h b/src/gabble-connection-manager.h deleted file mode 100644 index 5365ab13b..000000000 --- a/src/gabble-connection-manager.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * gabble-connection-manager.h - Header for GabbleConnectionManager - * Copyright (C) 2005 Collabora Ltd. - * Copyright (C) 2005 Nokia Corporation - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef __GABBLE_CONNECTION_MANAGER_H__ -#define __GABBLE_CONNECTION_MANAGER_H__ - -#include <glib-object.h> -#include <telepathy-glib/base-connection-manager.h> - -G_BEGIN_DECLS - -typedef struct _GabbleConnectionManager GabbleConnectionManager; -typedef struct _GabbleConnectionManagerClass GabbleConnectionManagerClass; - -struct _GabbleConnectionManagerClass { - TpBaseConnectionManagerClass parent_class; -}; - -struct _GabbleConnectionManager { - TpBaseConnectionManager parent; - - gpointer priv; -}; - -extern const TpCMProtocolSpec gabble_protocols[]; - -GType gabble_connection_manager_get_type (void); - -/* TYPE MACROS */ -#define GABBLE_TYPE_CONNECTION_MANAGER \ - (gabble_connection_manager_get_type ()) -#define GABBLE_CONNECTION_MANAGER(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_CONNECTION_MANAGER, \ - GabbleConnectionManager)) -#define GABBLE_CONNECTION_MANAGER_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_CONNECTION_MANAGER, \ - GabbleConnectionManagerClass)) -#define GABBLE_IS_CONNECTION_MANAGER(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_CONNECTION_MANAGER)) -#define GABBLE_IS_CONNECTION_MANAGER_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_CONNECTION_MANAGER)) -#define GABBLE_CONNECTION_MANAGER_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_CONNECTION_MANAGER, \ - GabbleConnectionManagerClass)) - -G_END_DECLS - -#endif /* #ifndef __GABBLE_CONNECTION_MANAGER_H__*/ diff --git a/src/gabble-connection.c b/src/gabble-connection.c deleted file mode 100644 index 2c0c38849..000000000 --- a/src/gabble-connection.c +++ /dev/null @@ -1,2809 +0,0 @@ -/* - * gabble-connection.c - Source for GabbleConnection - * Copyright (C) 2005, 2006 Collabora Ltd. - * Copyright (C) 2005, 2006 Nokia Corporation - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "config.h" - -#include "gabble-connection.h" - -#define DBUS_API_SUBJECT_TO_CHANGE - -#include <dbus/dbus-glib.h> -#include <dbus/dbus-glib-lowlevel.h> -#include <glib-object.h> -#include <loudmouth/loudmouth.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include <telepathy-glib/dbus.h> -#include <telepathy-glib/enums.h> -#include <telepathy-glib/errors.h> -#include <telepathy-glib/interfaces.h> - -#include <telepathy-glib/channel-iface.h> -#include <telepathy-glib/channel-factory-iface.h> - -#include <telepathy-glib/handle-repo-dynamic.h> -#include <telepathy-glib/handle-repo-static.h> - -#include <telepathy-glib/svc-connection.h> - -#define DEBUG_FLAG GABBLE_DEBUG_CONNECTION - -#include "capabilities.h" -#include "conn-aliasing.h" -#include "conn-avatars.h" -#include "conn-presence.h" -#include "debug.h" -#include "disco.h" -#include "presence-cache.h" -#include "presence.h" -#include "gabble-register.h" -#include "im-factory.h" -#include "media-factory.h" -#include "muc-factory.h" -#include "namespaces.h" -#include "roster.h" -#include "util.h" -#include "vcard-manager.h" - -#include "gabble-media-channel.h" -#include "gabble-roomlist-channel.h" - -#define TP_ALIAS_PAIR_TYPE (dbus_g_type_get_struct ("GValueArray", \ - G_TYPE_UINT, G_TYPE_STRING, G_TYPE_INVALID)) -#define TP_CAPABILITY_PAIR_TYPE (dbus_g_type_get_struct ("GValueArray", \ - G_TYPE_STRING, G_TYPE_UINT, G_TYPE_INVALID)) -#define TP_CAPABILITIES_CHANGED_MONSTER_TYPE (dbus_g_type_get_struct \ - ("GValueArray", G_TYPE_UINT, G_TYPE_STRING, G_TYPE_UINT, G_TYPE_UINT, \ - G_TYPE_UINT, G_TYPE_UINT, G_TYPE_INVALID)) -#define TP_GET_CAPABILITIES_MONSTER_TYPE (dbus_g_type_get_struct \ - ("GValueArray", G_TYPE_UINT, G_TYPE_STRING, G_TYPE_UINT, G_TYPE_UINT, \ - G_TYPE_INVALID)) - -static void conn_service_iface_init (gpointer, gpointer); -static void capabilities_service_iface_init (gpointer, gpointer); - -G_DEFINE_TYPE_WITH_CODE(GabbleConnection, - gabble_connection, - TP_TYPE_BASE_CONNECTION, - G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION, - conn_service_iface_init); - G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_ALIASING, - conn_aliasing_iface_init); - G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_AVATARS, - conn_avatars_iface_init); - G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_CAPABILITIES, - capabilities_service_iface_init); - G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_PRESENCE, - conn_presence_iface_init); - ) - -/* properties */ -enum -{ - PROP_CONNECT_SERVER = 1, - PROP_PORT, - PROP_OLD_SSL, - PROP_REGISTER, - PROP_LOW_BANDWIDTH, - PROP_STREAM_SERVER, - PROP_USERNAME, - PROP_PASSWORD, - PROP_RESOURCE, - PROP_PRIORITY, - PROP_HTTPS_PROXY_SERVER, - PROP_HTTPS_PROXY_PORT, - PROP_FALLBACK_CONFERENCE_SERVER, - PROP_STUN_SERVER, - PROP_STUN_PORT, - PROP_IGNORE_SSL_ERRORS, - PROP_ALIAS, - PROP_AUTH_MAC, - PROP_AUTH_BTID, - - LAST_PROPERTY -}; - -/* private structure */ -typedef struct _GabbleConnectionPrivate GabbleConnectionPrivate; - -struct _GabbleConnectionPrivate -{ - LmMessageHandler *iq_disco_cb; - LmMessageHandler *iq_unknown_cb; - LmMessageHandler *stream_error_cb; - - /* connection properties */ - gchar *connect_server; - guint port; - gboolean old_ssl; - - gboolean ignore_ssl_errors; - TpConnectionStatusReason ssl_error; - - gboolean do_register; - - gboolean low_bandwidth; - - gchar *https_proxy_server; - guint16 https_proxy_port; - - gchar *stun_server; - guint16 stun_port; - - gchar *fallback_conference_server; - - /* authentication properties */ - gchar *stream_server; - gchar *username; - gchar *password; - gchar *resource; - gint8 priority; - gchar *alias; - gchar *auth_mac; - gchar *auth_btid; - - /* reference to conference server name */ - const gchar *conference_server; - - /* serial number of current advertised caps */ - guint caps_serial; - - /* gobject housekeeping */ - gboolean dispose_has_run; -}; - -#define GABBLE_CONNECTION_GET_PRIVATE(obj) \ - ((GabbleConnectionPrivate *)obj->priv) - -static void connection_nickname_update_cb (GObject *, TpHandle, gpointer); -static void connection_capabilities_update_cb (GabblePresenceCache *, - TpHandle, GabblePresenceCapabilities, GabblePresenceCapabilities, - gpointer); - -static GPtrArray * -_gabble_connection_create_channel_factories (TpBaseConnection *conn) -{ - GabbleConnection *self = GABBLE_CONNECTION (conn); - - GPtrArray *channel_factories = g_ptr_array_sized_new (4); - - self->roster = gabble_roster_new (self); - g_signal_connect (self->roster, "nickname-update", G_CALLBACK - (connection_nickname_update_cb), self); - - g_ptr_array_add (channel_factories, self->roster); - - g_ptr_array_add (channel_factories, - g_object_new (GABBLE_TYPE_MUC_FACTORY, - "connection", self, - NULL)); - - g_ptr_array_add (channel_factories, - g_object_new (GABBLE_TYPE_MEDIA_FACTORY, - "connection", self, - NULL)); - - g_ptr_array_add (channel_factories, - g_object_new (GABBLE_TYPE_IM_FACTORY, - "connection", self, - NULL)); - - return channel_factories; -} - -static GObject * -gabble_connection_constructor (GType type, - guint n_construct_properties, - GObjectConstructParam *construct_params) -{ - GabbleConnection *self = GABBLE_CONNECTION ( - G_OBJECT_CLASS (gabble_connection_parent_class)->constructor ( - type, n_construct_properties, construct_params)); - - DEBUG("Post-construction: (GabbleConnection *)%p", self); - - self->disco = gabble_disco_new (self); - self->vcard_manager = gabble_vcard_manager_new (self); - g_signal_connect (self->vcard_manager, "nickname-update", G_CALLBACK - (connection_nickname_update_cb), self); - - self->presence_cache = gabble_presence_cache_new (self); - g_signal_connect (self->presence_cache, "nickname-update", G_CALLBACK - (connection_nickname_update_cb), self); - g_signal_connect (self->presence_cache, "capabilities-update", G_CALLBACK - (connection_capabilities_update_cb), self); - - capabilities_fill_cache (self->presence_cache); - - conn_avatars_init (self); - conn_presence_init (self); - - return (GObject *)self; -} - -static void -gabble_connection_init (GabbleConnection *self) -{ - GabbleConnectionPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self, - GABBLE_TYPE_CONNECTION, GabbleConnectionPrivate); - - DEBUG("Initializing (GabbleConnection *)%p", self); - - self->priv = priv; - self->lmconn = lm_connection_new (NULL); - - /* Set default parameters for optional parameters */ - priv->resource = g_strdup (GABBLE_PARAMS_DEFAULT_RESOURCE); - - priv->caps_serial = 1; -} - -static void -gabble_connection_get_property (GObject *object, - guint property_id, - GValue *value, - GParamSpec *pspec) -{ - GabbleConnection *self = (GabbleConnection *) object; - GabbleConnectionPrivate *priv = GABBLE_CONNECTION_GET_PRIVATE (self); - - switch (property_id) { - case PROP_CONNECT_SERVER: - g_value_set_string (value, priv->connect_server); - break; - case PROP_STREAM_SERVER: - g_value_set_string (value, priv->stream_server); - break; - case PROP_PORT: - g_value_set_uint (value, priv->port); - break; - case PROP_OLD_SSL: - g_value_set_boolean (value, priv->old_ssl); - break; - case PROP_REGISTER: - g_value_set_boolean (value, priv->do_register); - break; - case PROP_LOW_BANDWIDTH: - g_value_set_boolean (value, priv->low_bandwidth); - break; - case PROP_USERNAME: - g_value_set_string (value, priv->username); - break; - case PROP_PASSWORD: - g_value_set_string (value, priv->password); - break; - case PROP_RESOURCE: - g_value_set_string (value, priv->resource); - break; - case PROP_PRIORITY: - g_value_set_char (value, priv->priority); - break; - case PROP_HTTPS_PROXY_SERVER: - g_value_set_string (value, priv->https_proxy_server); - break; - case PROP_HTTPS_PROXY_PORT: - g_value_set_uint (value, priv->https_proxy_port); - break; - case PROP_FALLBACK_CONFERENCE_SERVER: - g_value_set_string (value, priv->fallback_conference_server); - break; - case PROP_IGNORE_SSL_ERRORS: - g_value_set_boolean (value, priv->ignore_ssl_errors); - break; - case PROP_ALIAS: - g_value_set_string (value, priv->alias); - break; - case PROP_AUTH_MAC: - g_value_set_string (value, priv->auth_mac); - break; - case PROP_AUTH_BTID: - g_value_set_string (value, priv->auth_btid); - break; - case PROP_STUN_SERVER: - g_value_set_string (value, priv->stun_server); - break; - case PROP_STUN_PORT: - g_value_set_uint (value, priv->stun_port); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } -} - -static void -gabble_connection_set_property (GObject *object, - guint property_id, - const GValue *value, - GParamSpec *pspec) -{ - GabbleConnection *self = (GabbleConnection *) object; - GabbleConnectionPrivate *priv = GABBLE_CONNECTION_GET_PRIVATE (self); - - switch (property_id) { - case PROP_CONNECT_SERVER: - g_free (priv->connect_server); - priv->connect_server = g_value_dup_string (value); - break; - case PROP_PORT: - priv->port = g_value_get_uint (value); - break; - case PROP_OLD_SSL: - priv->old_ssl = g_value_get_boolean (value); - break; - case PROP_REGISTER: - priv->do_register = g_value_get_boolean (value); - break; - case PROP_LOW_BANDWIDTH: - priv->low_bandwidth = g_value_get_boolean (value); - break; - case PROP_STREAM_SERVER: - g_free (priv->stream_server); - priv->stream_server = g_value_dup_string (value); - break; - case PROP_USERNAME: - g_free (priv->username); - priv->username = g_value_dup_string (value); - break; - case PROP_PASSWORD: - g_free (priv->password); - priv->password = g_value_dup_string (value); - break; - case PROP_RESOURCE: - g_free (priv->resource); - priv->resource = g_value_dup_string (value); - break; - case PROP_PRIORITY: - priv->priority = g_value_get_char (value); - break; - case PROP_HTTPS_PROXY_SERVER: - g_free (priv->https_proxy_server); - priv->https_proxy_server = g_value_dup_string (value); - break; - case PROP_HTTPS_PROXY_PORT: - priv->https_proxy_port = g_value_get_uint (value); - break; - case PROP_FALLBACK_CONFERENCE_SERVER: - g_free (priv->fallback_conference_server); - priv->fallback_conference_server = g_value_dup_string (value); - break; - case PROP_IGNORE_SSL_ERRORS: - priv->ignore_ssl_errors = g_value_get_boolean (value); - break; - case PROP_ALIAS: - g_free (priv->alias); - priv->alias = g_value_dup_string (value); - break; - case PROP_AUTH_MAC: - g_free (priv->auth_mac); - priv->auth_mac = g_value_dup_string (value); - break; - case PROP_AUTH_BTID: - g_free (priv->auth_btid); - priv->auth_btid = g_value_dup_string (value); - break; - case PROP_STUN_SERVER: - g_free (priv->stun_server); - priv->stun_server = g_value_dup_string (value); - break; - case PROP_STUN_PORT: - priv->stun_port = g_value_get_uint (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } -} - -static void gabble_connection_dispose (GObject *object); -static void gabble_connection_finalize (GObject *object); -static void connect_callbacks (TpBaseConnection *base); -static void disconnect_callbacks (TpBaseConnection *base); -static void connection_shut_down (TpBaseConnection *base); -static gboolean _gabble_connection_connect (TpBaseConnection *base, - GError **error); - -static gchar * -gabble_connection_get_unique_name (TpBaseConnection *self) -{ - GabbleConnectionPrivate *priv = GABBLE_CONNECTION_GET_PRIVATE ( - GABBLE_CONNECTION (self)); - - return g_strdup_printf ("%s@%s/%s", - priv->username, - priv->stream_server, - priv->resource); -} - -/* must be in the same order as GabbleListHandle in gabble-connection.h */ -static const char *list_handle_strings[] = -{ - "publish", /* GABBLE_LIST_HANDLE_PUBLISH */ - "subscribe", /* GABBLE_LIST_HANDLE_SUBSCRIBE */ - "known", /* GABBLE_LIST_HANDLE_KNOWN */ - "deny", /* GABBLE_LIST_HANDLE_DENY */ - NULL -}; - -/* For the benefit of the unit tests, this will allow the connection to - * be NULL - */ -void -_gabble_connection_create_handle_repos (TpBaseConnection *conn, - TpHandleRepoIface *repos[NUM_TP_HANDLE_TYPES]) -{ - repos[TP_HANDLE_TYPE_CONTACT] = - tp_dynamic_handle_repo_new (TP_HANDLE_TYPE_CONTACT, - gabble_normalize_contact, GUINT_TO_POINTER (GABBLE_JID_ANY)); - repos[TP_HANDLE_TYPE_ROOM] = - tp_dynamic_handle_repo_new (TP_HANDLE_TYPE_ROOM, gabble_normalize_room, - NULL); - repos[TP_HANDLE_TYPE_GROUP] = - tp_dynamic_handle_repo_new (TP_HANDLE_TYPE_GROUP, NULL, NULL); - repos[TP_HANDLE_TYPE_LIST] = - tp_static_handle_repo_new (TP_HANDLE_TYPE_LIST, list_handle_strings); -} - -static void -gabble_connection_class_init (GabbleConnectionClass *gabble_connection_class) -{ - GObjectClass *object_class = G_OBJECT_CLASS (gabble_connection_class); - TpBaseConnectionClass *parent_class = TP_BASE_CONNECTION_CLASS ( - gabble_connection_class); - GParamSpec *param_spec; - static const gchar *interfaces_always_present[] = { - TP_IFACE_CONNECTION_INTERFACE_ALIASING, - TP_IFACE_CONNECTION_INTERFACE_CAPABILITIES, - TP_IFACE_CONNECTION_INTERFACE_PRESENCE, - TP_IFACE_CONNECTION_INTERFACE_AVATARS, - NULL }; - - DEBUG("Initializing (GabbleConnectionClass *)%p", gabble_connection_class); - - object_class->get_property = gabble_connection_get_property; - object_class->set_property = gabble_connection_set_property; - object_class->constructor = gabble_connection_constructor; - - parent_class->create_handle_repos = _gabble_connection_create_handle_repos; - parent_class->get_unique_connection_name = gabble_connection_get_unique_name; - parent_class->create_channel_factories = - _gabble_connection_create_channel_factories; - parent_class->connecting = connect_callbacks; - parent_class->disconnected = disconnect_callbacks; - parent_class->shut_down = connection_shut_down; - parent_class->start_connecting = _gabble_connection_connect; - parent_class->interfaces_always_present = interfaces_always_present; - - g_type_class_add_private (gabble_connection_class, - sizeof (GabbleConnectionPrivate)); - - object_class->dispose = gabble_connection_dispose; - object_class->finalize = gabble_connection_finalize; - - param_spec = g_param_spec_string ("connect-server", "Hostname or IP of Jabber server", - "The server used when establishing a connection.", - NULL, - G_PARAM_READWRITE | - G_PARAM_STATIC_NAME | - G_PARAM_STATIC_BLURB); - g_object_class_install_property (object_class, PROP_CONNECT_SERVER, param_spec); - - param_spec = g_param_spec_uint ("port", "Jabber server port", - "The port used when establishing a connection.", - 0, G_MAXUINT16, GABBLE_PARAMS_DEFAULT_PORT, - G_PARAM_CONSTRUCT | - G_PARAM_READWRITE | - G_PARAM_STATIC_NAME | - G_PARAM_STATIC_BLURB); - g_object_class_install_property (object_class, PROP_PORT, param_spec); - - param_spec = g_param_spec_boolean ("old-ssl", "Old-style SSL tunneled connection", - "Establish the entire connection to the server " - "within an SSL-encrypted tunnel. Note that this " - "is not the same as connecting with TLS, which " - "is not yet supported.", FALSE, - G_PARAM_READWRITE | - G_PARAM_STATIC_NAME | - G_PARAM_STATIC_BLURB); - g_object_class_install_property (object_class, PROP_OLD_SSL, param_spec); - - param_spec = g_param_spec_boolean ("register", "Register account on server", - "Register a new account on server.", FALSE, - G_PARAM_READWRITE | - G_PARAM_STATIC_NAME | - G_PARAM_STATIC_BLURB); - g_object_class_install_property (object_class, PROP_REGISTER, param_spec); - - param_spec = g_param_spec_boolean ("low-bandwidth", "Low bandwidth mode", - "Determines whether we are in low " - "bandwidth mode. This influences " - "polling behaviour.", FALSE, - G_PARAM_READWRITE | - G_PARAM_STATIC_NAME | - G_PARAM_STATIC_BLURB); - g_object_class_install_property (object_class, PROP_LOW_BANDWIDTH, param_spec); - - param_spec = g_param_spec_string ("stream-server", "The server name used to initialise the stream.", - "The server name used when initialising the stream, " - "which is usually the part after the @ in the user's JID.", - NULL, - G_PARAM_READWRITE | - G_PARAM_STATIC_NAME | - G_PARAM_STATIC_BLURB); - g_object_class_install_property (object_class, PROP_STREAM_SERVER, param_spec); - - param_spec = g_param_spec_string ("username", "Jabber username", - "The username used when authenticating.", - NULL, - G_PARAM_READWRITE | - G_PARAM_STATIC_NAME | - G_PARAM_STATIC_BLURB); - g_object_class_install_property (object_class, PROP_USERNAME, param_spec); - - param_spec = g_param_spec_string ("password", "Jabber password", - "The password used when authenticating.", - NULL, - G_PARAM_READWRITE | - G_PARAM_STATIC_NAME | - G_PARAM_STATIC_BLURB); - g_object_class_install_property (object_class, PROP_PASSWORD, param_spec); - - param_spec = g_param_spec_string ("resource", "Jabber resource", - "The Jabber resource used when authenticating.", - "Telepathy", - G_PARAM_READWRITE | - G_PARAM_STATIC_NAME | - G_PARAM_STATIC_BLURB); - g_object_class_install_property (object_class, PROP_RESOURCE, param_spec); - - param_spec = g_param_spec_char ("priority", "Jabber presence priority", - "The default priority used when reporting our presence.", - G_MININT8, G_MAXINT8, 0, - G_PARAM_READWRITE | - G_PARAM_STATIC_NAME | - G_PARAM_STATIC_BLURB); - g_object_class_install_property (object_class, PROP_PRIORITY, param_spec); - - param_spec = g_param_spec_string ("https-proxy-server", "The server name " - "used as an HTTPS proxy server", - "The server name used as an HTTPS proxy " - "server.", - NULL, - G_PARAM_READWRITE | - G_PARAM_STATIC_NAME | - G_PARAM_STATIC_BLURB); - g_object_class_install_property (object_class, PROP_HTTPS_PROXY_SERVER, param_spec); - - param_spec = g_param_spec_uint ("https-proxy-port", "The HTTP proxy server " - "port", "The HTTP proxy server port.", - 0, G_MAXUINT16, GABBLE_PARAMS_DEFAULT_HTTPS_PROXY_PORT, - G_PARAM_CONSTRUCT | - G_PARAM_READWRITE | - G_PARAM_STATIC_NAME | - G_PARAM_STATIC_BLURB); - g_object_class_install_property (object_class, PROP_HTTPS_PROXY_PORT, param_spec); - - param_spec = g_param_spec_string ("fallback-conference-server", - "The conference server used as fallback", - "The conference server used as fallback when " - "everything else fails.", - NULL, - G_PARAM_READWRITE | - G_PARAM_STATIC_NAME | - G_PARAM_STATIC_BLURB); - g_object_class_install_property (object_class, PROP_FALLBACK_CONFERENCE_SERVER, - param_spec); - - param_spec = g_param_spec_string ("stun-server", - "STUN server", - "STUN server.", - NULL, - G_PARAM_READWRITE | - G_PARAM_STATIC_NAME | - G_PARAM_STATIC_BLURB); - g_object_class_install_property (object_class, PROP_STUN_SERVER, param_spec); - - param_spec = g_param_spec_uint ("stun-port", - "STUN port", - "STUN port.", - 0, G_MAXUINT16, GABBLE_PARAMS_DEFAULT_STUN_PORT, - G_PARAM_CONSTRUCT | - G_PARAM_READWRITE | - G_PARAM_STATIC_NAME | - G_PARAM_STATIC_BLURB); - g_object_class_install_property (object_class, PROP_STUN_PORT, param_spec); - - param_spec = g_param_spec_boolean ("ignore-ssl-errors", "Ignore SSL errors", - "Continue connecting even if the server's " - "SSL certificate is invalid or missing.", - FALSE, - G_PARAM_READWRITE | - G_PARAM_STATIC_NAME | - G_PARAM_STATIC_BLURB); - g_object_class_install_property (object_class, PROP_IGNORE_SSL_ERRORS, param_spec); - - param_spec = g_param_spec_string ("alias", - "Alias/nick for local user", - "Alias/nick for local user", - NULL, - G_PARAM_READWRITE | - G_PARAM_STATIC_NAME | - G_PARAM_STATIC_BLURB); - g_object_class_install_property (object_class, PROP_ALIAS, param_spec); - - param_spec = g_param_spec_string ("auth-mac", - "MAC for authorization", - "MAC for authorization", - NULL, - G_PARAM_READWRITE | - G_PARAM_STATIC_NAME | - G_PARAM_STATIC_BLURB); - g_object_class_install_property (object_class, PROP_AUTH_MAC, param_spec); - - param_spec = g_param_spec_string ("auth-btid", - "BTID for authorization", - "BTID for authorization", - NULL, - G_PARAM_READWRITE | - G_PARAM_STATIC_NAME | - G_PARAM_STATIC_BLURB); - g_object_class_install_property (object_class, PROP_AUTH_BTID, param_spec); -} - -static gboolean -_unref_lm_connection (gpointer data) -{ - LmConnection *conn = (LmConnection *) data; - - lm_connection_unref (conn); - return FALSE; -} - -static void -gabble_connection_dispose (GObject *object) -{ - GabbleConnection *self = GABBLE_CONNECTION (object); - TpBaseConnection *base = (TpBaseConnection *)self; - GabbleConnectionPrivate *priv = GABBLE_CONNECTION_GET_PRIVATE (self); - - if (priv->dispose_has_run) - return; - - priv->dispose_has_run = TRUE; - - DEBUG ("called"); - - g_assert ((base->status == TP_CONNECTION_STATUS_DISCONNECTED) || - (base->status == TP_INTERNAL_CONNECTION_STATUS_NEW)); - g_assert (base->self_handle == 0); - - g_object_unref (self->vcard_manager); - self->vcard_manager = NULL; - - /* unreffing channel factories frees the roster */ - self->roster = NULL; - - g_object_unref (self->disco); - self->disco = NULL; - - if (self->self_presence != NULL) - g_object_unref (self->self_presence); - self->self_presence = NULL; - - g_object_unref (self->presence_cache); - self->presence_cache = NULL; - - /* if this is not already the case, we'll crash anyway */ - g_assert (!lm_connection_is_open (self->lmconn)); - - g_assert (priv->iq_disco_cb == NULL); - g_assert (priv->iq_unknown_cb == NULL); - g_assert (priv->stream_error_cb == NULL); - - /* - * The Loudmouth connection can't be unref'd immediately because this - * function might (indirectly) return into Loudmouth code which expects the - * connection to always be there. - */ - g_idle_add (_unref_lm_connection, self->lmconn); - - if (G_OBJECT_CLASS (gabble_connection_parent_class)->dispose) - G_OBJECT_CLASS (gabble_connection_parent_class)->dispose (object); -} - -static void -gabble_connection_finalize (GObject *object) -{ - GabbleConnection *self = GABBLE_CONNECTION (object); - GabbleConnectionPrivate *priv = GABBLE_CONNECTION_GET_PRIVATE (self); - - DEBUG ("called with %p", object); - - g_free (priv->connect_server); - g_free (priv->stream_server); - g_free (priv->username); - g_free (priv->password); - g_free (priv->resource); - - g_free (priv->https_proxy_server); - g_free (priv->stun_server); - g_free (priv->fallback_conference_server); - - g_free (priv->alias); - - g_free (priv->auth_mac); - g_free (priv->auth_btid); - - G_OBJECT_CLASS (gabble_connection_parent_class)->finalize (object); -} - -/** - * _gabble_connection_set_properties_from_account - * - * Parses an account string which may be one of the following forms: - * username@server - * username@server/resource - * and sets the properties for username, stream server and resource - * appropriately. Also sets the connect server to the stream server if one has - * not yet been specified. - */ -gboolean -_gabble_connection_set_properties_from_account (GabbleConnection *conn, - const gchar *account, - GError **error) -{ - GabbleConnectionPrivate *priv; - char *username, *server, *resource; - gboolean result; - - g_assert (GABBLE_IS_CONNECTION (conn)); - g_assert (account != NULL); - - priv = GABBLE_CONNECTION_GET_PRIVATE (conn); - - username = server = resource = NULL; - result = TRUE; - - gabble_decode_jid (account, &username, &server, &resource); - - if (username == NULL || server == NULL || - *username == '\0' || *server == '\0') - { - g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, - "unable to get username and server from account"); - result = FALSE; - goto OUT; - } - - g_object_set (G_OBJECT (conn), - "username", username, - "stream-server", server, - NULL); - - /* only override the default resource if we actually got one */ - if (resource) - g_object_set (G_OBJECT (conn), "resource", resource, NULL); - -OUT: - g_free (username); - g_free (server); - g_free (resource); - - return result; -} - - -/** - * _gabble_connection_send - * - * Send an LmMessage and trap network errors appropriately. - */ -gboolean -_gabble_connection_send (GabbleConnection *conn, LmMessage *msg, GError **error) -{ - GabbleConnectionPrivate *priv; - GError *lmerror = NULL; - - g_assert (GABBLE_IS_CONNECTION (conn)); - - priv = GABBLE_CONNECTION_GET_PRIVATE (conn); - - if (!lm_connection_send (conn->lmconn, msg, &lmerror)) - { - DEBUG ("failed: %s", lmerror->message); - - g_set_error (error, TP_ERRORS, TP_ERROR_NETWORK_ERROR, - "message send failed: %s", lmerror->message); - - g_error_free (lmerror); - - return FALSE; - } - - return TRUE; -} - -typedef struct { - GabbleConnectionMsgReplyFunc reply_func; - - GabbleConnection *conn; - LmMessage *sent_msg; - gpointer user_data; - - GObject *object; - gboolean object_alive; -} GabbleMsgHandlerData; - -static LmHandlerResult -message_send_reply_cb (LmMessageHandler *handler, - LmConnection *connection, - LmMessage *reply_msg, - gpointer user_data) -{ - GabbleMsgHandlerData *handler_data = user_data; - LmMessageSubType sub_type; - - sub_type = lm_message_get_sub_type (reply_msg); - - /* Is it a reply to this message? If we're talking to another loudmouth, - * they can send us messages which have the same ID as ones we send. :-O */ - if (sub_type != LM_MESSAGE_SUB_TYPE_RESULT && - sub_type != LM_MESSAGE_SUB_TYPE_ERROR) - { - return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; - } - - if (handler_data->object_alive) - { - return handler_data->reply_func (handler_data->conn, - handler_data->sent_msg, - reply_msg, - handler_data->object, - handler_data->user_data); - } - - return LM_HANDLER_RESULT_REMOVE_MESSAGE; -} - -static void -message_send_object_destroy_notify_cb (gpointer data, - GObject *where_the_object_was) -{ - GabbleMsgHandlerData *handler_data = data; - - handler_data->object = NULL; - handler_data->object_alive = FALSE; -} - -static void -message_send_handler_destroy_cb (gpointer data) -{ - GabbleMsgHandlerData *handler_data = data; - - lm_message_unref (handler_data->sent_msg); - - if (handler_data->object != NULL) - { - g_object_weak_unref (handler_data->object, - message_send_object_destroy_notify_cb, - handler_data); - } - - g_slice_free (GabbleMsgHandlerData, handler_data); -} - -/** - * _gabble_connection_send_with_reply - * - * Send a tracked LmMessage and trap network errors appropriately. - * - * If object is non-NULL the handler will follow the lifetime of that object, - * which means that if the object is destroyed the callback will not be invoked. - */ -gboolean -_gabble_connection_send_with_reply (GabbleConnection *conn, - LmMessage *msg, - GabbleConnectionMsgReplyFunc reply_func, - GObject *object, - gpointer user_data, - GError **error) -{ - GabbleConnectionPrivate *priv; - LmMessageHandler *handler; - GabbleMsgHandlerData *handler_data; - gboolean ret; - GError *lmerror = NULL; - - g_assert (GABBLE_IS_CONNECTION (conn)); - - priv = GABBLE_CONNECTION_GET_PRIVATE (conn); - - lm_message_ref (msg); - - handler_data = g_slice_new (GabbleMsgHandlerData); - handler_data->reply_func = reply_func; - handler_data->conn = conn; - handler_data->sent_msg = msg; - handler_data->user_data = user_data; - - handler_data->object = object; - handler_data->object_alive = TRUE; - - if (object != NULL) - { - g_object_weak_ref (object, message_send_object_destroy_notify_cb, - handler_data); - } - - handler = lm_message_handler_new (message_send_reply_cb, handler_data, - message_send_handler_destroy_cb); - - ret = lm_connection_send_with_reply (conn->lmconn, msg, handler, &lmerror); - if (!ret) - { - DEBUG ("failed: %s", lmerror->message); - - if (error) - { - g_set_error (error, TP_ERRORS, TP_ERROR_NETWORK_ERROR, - "message send failed: %s", lmerror->message); - } - - g_error_free (lmerror); - } - - lm_message_handler_unref (handler); - - return ret; -} - -static LmHandlerResult connection_iq_disco_cb (LmMessageHandler *, - LmConnection *, LmMessage *, gpointer); -static LmHandlerResult connection_iq_unknown_cb (LmMessageHandler *, - LmConnection *, LmMessage *, gpointer); -static LmHandlerResult connection_stream_error_cb (LmMessageHandler *, - LmConnection *, LmMessage *, gpointer); -static LmSSLResponse connection_ssl_cb (LmSSL *, LmSSLStatus, gpointer); -static void connection_open_cb (LmConnection *, gboolean, gpointer); -static void connection_auth_cb (LmConnection *, gboolean, gpointer); -static void connection_disco_cb (GabbleDisco *, GabbleDiscoRequest *, - const gchar *, const gchar *, LmMessageNode *, GError *, gpointer); -static void connection_disconnected_cb (LmConnection *, LmDisconnectReason, - gpointer); - - -static gboolean -do_connect (GabbleConnection *conn, GError **error) -{ - GError *lmerror = NULL; - - DEBUG ("calling lm_connection_open"); - - if (!lm_connection_open (conn->lmconn, connection_open_cb, - conn, NULL, &lmerror)) - { - DEBUG ("lm_connection_open failed %s", lmerror->message); - - g_set_error (error, TP_ERRORS, TP_ERROR_NETWORK_ERROR, - "lm_connection_open failed: %s", lmerror->message); - - g_error_free (lmerror); - - return FALSE; - } - - return TRUE; -} - -static void -connect_callbacks (TpBaseConnection *base) -{ - GabbleConnection *conn = GABBLE_CONNECTION (base); - GabbleConnectionPrivate *priv = GABBLE_CONNECTION_GET_PRIVATE (conn); - - g_assert (priv->iq_disco_cb == NULL); - g_assert (priv->iq_unknown_cb == NULL); - g_assert (priv->stream_error_cb == NULL); - - priv->iq_disco_cb = lm_message_handler_new (connection_iq_disco_cb, - conn, NULL); - lm_connection_register_message_handler (conn->lmconn, priv->iq_disco_cb, - LM_MESSAGE_TYPE_IQ, - LM_HANDLER_PRIORITY_NORMAL); - - priv->iq_unknown_cb = lm_message_handler_new (connection_iq_unknown_cb, - conn, NULL); - lm_connection_register_message_handler (conn->lmconn, priv->iq_unknown_cb, - LM_MESSAGE_TYPE_IQ, - LM_HANDLER_PRIORITY_LAST); - - priv->stream_error_cb = lm_message_handler_new (connection_stream_error_cb, - conn, NULL); - lm_connection_register_message_handler (conn->lmconn, priv->stream_error_cb, - LM_MESSAGE_TYPE_STREAM_ERROR, - LM_HANDLER_PRIORITY_LAST); -} - -static void -disconnect_callbacks (TpBaseConnection *base) -{ - GabbleConnection *conn = GABBLE_CONNECTION (base); - GabbleConnectionPrivate *priv = GABBLE_CONNECTION_GET_PRIVATE (conn); - - g_assert (priv->iq_disco_cb != NULL); - g_assert (priv->iq_unknown_cb != NULL); - g_assert (priv->stream_error_cb != NULL); - - lm_connection_unregister_message_handler (conn->lmconn, priv->iq_disco_cb, - LM_MESSAGE_TYPE_IQ); - lm_message_handler_unref (priv->iq_disco_cb); - priv->iq_disco_cb = NULL; - - lm_connection_unregister_message_handler (conn->lmconn, priv->iq_unknown_cb, - LM_MESSAGE_TYPE_IQ); - lm_message_handler_unref (priv->iq_unknown_cb); - priv->iq_unknown_cb = NULL; - - lm_connection_unregister_message_handler (conn->lmconn, - priv->stream_error_cb, LM_MESSAGE_TYPE_STREAM_ERROR); - lm_message_handler_unref (priv->stream_error_cb); - priv->stream_error_cb = NULL; -} - -/** - * _gabble_connection_connect - * - * Use the stored server & authentication details to commence - * the stages for connecting to the server and authenticating. Will - * re-use an existing LmConnection if it is present, or create it - * if necessary. - * - * Stage 1 is _gabble_connection_connect calling lm_connection_open - * Stage 2 is connection_open_cb calling lm_connection_authenticate - * Stage 3 is connection_auth_cb initiating service discovery - * Stage 4 is connection_disco_cb advertising initial presence, requesting - * the roster and setting the CONNECTED state - */ -static gboolean -_gabble_connection_connect (TpBaseConnection *base, - GError **error) -{ - GabbleConnection *conn = GABBLE_CONNECTION (base); - GabbleConnectionPrivate *priv = GABBLE_CONNECTION_GET_PRIVATE (conn); - TpHandleRepoIface *contact_handles = tp_base_connection_get_handles (base, - TP_HANDLE_TYPE_CONTACT); - char *jid; - - g_assert (priv->port > 0 && priv->port <= G_MAXUINT16); - g_assert (priv->stream_server != NULL); - g_assert (priv->username != NULL); - g_assert (priv->password != NULL); - g_assert (priv->resource != NULL); - g_assert (lm_connection_is_open (conn->lmconn) == FALSE); - - jid = g_strdup_printf ("%s@%s", priv->username, priv->stream_server); - lm_connection_set_jid (conn->lmconn, jid); - - base->self_handle = tp_handle_ensure (contact_handles, jid, NULL, error); - g_free (jid); - - if (base->self_handle == 0) - { - return FALSE; - } - - /* set initial presence */ - conn->self_presence = gabble_presence_new (); - gabble_presence_update (conn->self_presence, priv->resource, - GABBLE_PRESENCE_AVAILABLE, NULL, priv->priority); - - /* set initial capabilities */ - gabble_presence_set_capabilities (conn->self_presence, priv->resource, - capabilities_get_initial_caps (), priv->caps_serial++); - - /* always override server and port if one was forced upon us */ - if (priv->connect_server != NULL) - { - lm_connection_set_server (conn->lmconn, priv->connect_server); - lm_connection_set_port (conn->lmconn, priv->port); - } - /* otherwise set the server & port to the stream server, - * if one didn't appear from a SRV lookup */ - else if (lm_connection_get_server (conn->lmconn) == NULL) - { - lm_connection_set_server (conn->lmconn, priv->stream_server); - lm_connection_set_port (conn->lmconn, priv->port); - } - - if (priv->https_proxy_server) - { - LmProxy *proxy; - - proxy = lm_proxy_new_with_server (LM_PROXY_TYPE_HTTP, - priv->https_proxy_server, priv->https_proxy_port); - - lm_connection_set_proxy (conn->lmconn, proxy); - - lm_proxy_unref (proxy); - } - - if (priv->old_ssl) - { - LmSSL *ssl = lm_ssl_new (NULL, connection_ssl_cb, conn, NULL); - lm_connection_set_ssl (conn->lmconn, ssl); - lm_ssl_unref (ssl); - } - - /* send whitespace to the server every 30 seconds */ - lm_connection_set_keep_alive_rate (conn->lmconn, 30); - - lm_connection_set_disconnect_function (conn->lmconn, - connection_disconnected_cb, - conn, - NULL); - - return do_connect (conn, error); -} - - - -static void -connection_disconnected_cb (LmConnection *lmconn, - LmDisconnectReason lm_reason, - gpointer user_data) -{ - GabbleConnection *conn = GABBLE_CONNECTION (user_data); - TpBaseConnection *base = (TpBaseConnection *)user_data; - - g_assert (conn->lmconn == lmconn); - - DEBUG ("called with reason %u", lm_reason); - - /* if we were expecting this disconnection, we're done so can tell - * the connection manager to unref us. otherwise it's a network error - * or some other screw up we didn't expect, so we emit the status - * change */ - if (base->status == TP_CONNECTION_STATUS_DISCONNECTED) - { - DEBUG ("expected; emitting DISCONNECTED"); - tp_base_connection_finish_shutdown ((TpBaseConnection *)conn); - } - else - { - DEBUG ("unexpected; calling tp_base_connection_change_status"); - tp_base_connection_change_status ((TpBaseConnection *)conn, - TP_CONNECTION_STATUS_DISCONNECTED, - TP_CONNECTION_STATUS_REASON_NETWORK_ERROR); - } -} - - -static void -connection_shut_down (TpBaseConnection *base) -{ - GabbleConnection *conn = GABBLE_CONNECTION (base); - - g_assert (GABBLE_IS_CONNECTION (conn)); - - /* If we're shutting down by user request, we don't want to be - * unreffed until the LM connection actually closes; the event handler - * will tell the base class that shutdown has finished. - * - * On the other hand, if we're shutting down because the connection - * suffered a network error, the LM connection will already be closed, - * so just tell the base class to finish shutting down immediately. - */ - if (lm_connection_is_open (conn->lmconn)) - { - DEBUG ("still open; calling lm_connection_close"); - lm_connection_close (conn->lmconn, NULL); - } - else - { - /* lm_connection_is_open() returns FALSE if LmConnection is in the - * middle of connecting, so call this just in case */ - lm_connection_cancel_open (conn->lmconn); - DEBUG ("closed; emitting DISCONNECTED"); - tp_base_connection_finish_shutdown (base); - } -} - -GabbleConnectionAliasSource -_gabble_connection_get_cached_alias (GabbleConnection *conn, - TpHandle handle, - gchar **alias) -{ - TpBaseConnection *base = (TpBaseConnection *)conn; - GabbleConnectionPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (conn, - GABBLE_TYPE_CONNECTION, GabbleConnectionPrivate); - TpHandleRepoIface *contact_handles = tp_base_connection_get_handles (base, - TP_HANDLE_TYPE_CONTACT); - GabbleConnectionAliasSource ret = GABBLE_CONNECTION_ALIAS_NONE; - GabblePresence *pres; - const gchar *tmp; - gchar *user = NULL, *resource = NULL; - - g_return_val_if_fail (NULL != conn, GABBLE_CONNECTION_ALIAS_NONE); - g_return_val_if_fail (GABBLE_IS_CONNECTION (conn), GABBLE_CONNECTION_ALIAS_NONE); - g_return_val_if_fail (tp_handle_is_valid (contact_handles, handle, NULL), - GABBLE_CONNECTION_ALIAS_NONE); - - tmp = gabble_roster_handle_get_name (conn->roster, handle); - if (NULL != tmp) - { - ret = GABBLE_CONNECTION_ALIAS_FROM_ROSTER; - - if (NULL != alias) - *alias = g_strdup (tmp); - - goto OUT; - } - - pres = gabble_presence_cache_get (conn->presence_cache, handle); - if (NULL != pres && NULL != pres->nickname) - { - ret = GABBLE_CONNECTION_ALIAS_FROM_PRESENCE; - - if (NULL != alias) - *alias = g_strdup (pres->nickname); - - goto OUT; - } - - /* XXX: should this be more important than the ones from presence? */ - /* if it's our own handle, use alias passed to the connmgr, if any */ - if (handle == base->self_handle && priv->alias != NULL) - { - ret = GABBLE_CONNECTION_ALIAS_FROM_CONNMGR; - - if (NULL != alias) - *alias = g_strdup (priv->alias); - - goto OUT; - } - - /* if we've seen a nickname in their vCard, use that */ - tmp = gabble_vcard_manager_get_cached_alias (conn->vcard_manager, handle); - if (NULL != tmp) - { - ret = GABBLE_CONNECTION_ALIAS_FROM_VCARD; - - if (NULL != alias) - *alias = g_strdup (tmp); - - goto OUT; - } - - /* fallback to JID */ - tmp = tp_handle_inspect (contact_handles, handle); - g_assert (NULL != tmp); - - gabble_decode_jid (tmp, &user, NULL, &resource); - - /* MUC handles have the nickname in the resource */ - if (NULL != resource) - { - ret = GABBLE_CONNECTION_ALIAS_FROM_JID; - - if (NULL != alias) - { - *alias = resource; - resource = NULL; - } - - goto OUT; - } - - /* otherwise just take their local part */ - if (NULL != user) - { - ret = GABBLE_CONNECTION_ALIAS_FROM_JID; - - if (NULL != alias) - { - *alias = user; - user = NULL; - } - - goto OUT; - } - -OUT: - g_free (user); - g_free (resource); - return ret; -} - -static void -connection_nickname_update_cb (GObject *object, - TpHandle handle, - gpointer user_data) -{ - GabbleConnection *conn = GABBLE_CONNECTION (user_data); - GabbleConnectionAliasSource signal_source, current_source; - gchar *alias = NULL; - GPtrArray *aliases; - GValue entry = { 0, }; - - if (object == G_OBJECT (conn->roster)) - { - signal_source = GABBLE_CONNECTION_ALIAS_FROM_ROSTER; - } - else if (object == G_OBJECT (conn->presence_cache)) - { - signal_source = GABBLE_CONNECTION_ALIAS_FROM_PRESENCE; - } - else if (object == G_OBJECT (conn->vcard_manager)) - { - signal_source = GABBLE_CONNECTION_ALIAS_FROM_VCARD; - } - else - { - g_assert_not_reached (); - return; - } - - current_source = _gabble_connection_get_cached_alias (conn, handle, &alias); - - g_assert (current_source != GABBLE_CONNECTION_ALIAS_NONE); - - /* if the active alias for this handle is already known and from - * a higher priority, this signal is not interesting so we do - * nothing */ - if (signal_source < current_source) - { - DEBUG ("ignoring boring alias change for handle %u, signal from %u " - "but source %u has alias \"%s\"", handle, signal_source, - current_source, alias); - goto OUT; - } - - g_value_init (&entry, TP_ALIAS_PAIR_TYPE); - g_value_take_boxed (&entry, dbus_g_type_specialized_construct - (TP_ALIAS_PAIR_TYPE)); - - dbus_g_type_struct_set (&entry, - 0, handle, - 1, alias, - G_MAXUINT); - - aliases = g_ptr_array_sized_new (1); - g_ptr_array_add (aliases, g_value_get_boxed (&entry)); - - - tp_svc_connection_interface_aliasing_emit_aliases_changed (conn, aliases); - - g_value_unset (&entry); - g_ptr_array_free (aliases, TRUE); - -OUT: - g_free (alias); -} - -/** - * _gabble_connection_signal_own_presence: - * @self: A #GabbleConnection - * @error: pointer in which to return a GError in case of failure. - * - * Signal the user's stored presence to the jabber server - * - * Retuns: FALSE if an error occurred - */ -gboolean -_gabble_connection_signal_own_presence (GabbleConnection *self, GError **error) -{ - GabbleConnectionPrivate *priv = GABBLE_CONNECTION_GET_PRIVATE (self); - GabblePresence *presence = self->self_presence; - LmMessage *message = gabble_presence_as_message (presence, priv->resource); - LmMessageNode *node = lm_message_get_node (message); - gboolean ret; - gchar *ext_string = NULL; - GSList *features, *i; - - if (presence->status == GABBLE_PRESENCE_HIDDEN) - { - if ((self->features & GABBLE_CONNECTION_FEATURES_PRESENCE_INVISIBLE) != 0) - lm_message_node_set_attribute (node, "type", "invisible"); - } - - /* TODO: why is this not part of presence -> msg? */ - features = capabilities_get_features (presence->caps); - - for (i = features; NULL != i; i = i->next) - { - const Feature *feat = (const Feature *) i->data; - - if ((NULL != feat->bundle) && tp_strdiff (VERSION, feat->bundle)) - { - if (NULL != ext_string) - { - gchar *tmp = ext_string; - ext_string = g_strdup_printf ("%s %s", ext_string, feat->bundle); - g_free (tmp); - } - else - { - ext_string = g_strdup (feat->bundle); - } - } - } - - node = lm_message_node_add_child (node, "c", NULL); - lm_message_node_set_attributes ( - node, - "xmlns", NS_CAPS, - "node", NS_GABBLE_CAPS, - "ver", VERSION, - NULL); - - if (NULL != ext_string) - lm_message_node_set_attribute (node, "ext", ext_string); - - ret = _gabble_connection_send (self, message, error); - - lm_message_unref (message); - - g_free (ext_string); - g_slist_free (features); - - return ret; -} - -static LmMessage *_lm_iq_message_make_result (LmMessage *iq_message); - -/** - * _gabble_connection_send_iq_result - * - * Function used to acknowledge an IQ stanza. - */ -void -_gabble_connection_acknowledge_set_iq (GabbleConnection *conn, - LmMessage *iq) -{ - LmMessage *result; - - g_assert (LM_MESSAGE_TYPE_IQ == lm_message_get_type (iq)); - g_assert (LM_MESSAGE_SUB_TYPE_SET == lm_message_get_sub_type (iq)); - - result = _lm_iq_message_make_result (iq); - - if (NULL != result) - { - _gabble_connection_send (conn, result, NULL); - lm_message_unref (result); - } -} - - -/** - * _gabble_connection_send_iq_error - * - * Function used to acknowledge an IQ stanza with an error. - */ -void -_gabble_connection_send_iq_error (GabbleConnection *conn, - LmMessage *message, - GabbleXmppError error, - const gchar *errmsg) -{ - const gchar *to, *id; - LmMessage *msg; - LmMessageNode *iq_node; - - iq_node = lm_message_get_node (message); - to = lm_message_node_get_attribute (iq_node, "from"); - id = lm_message_node_get_attribute (iq_node, "id"); - - if (id == NULL) - { - NODE_DEBUG (iq_node, "can't acknowledge IQ with no id"); - return; - } - - msg = lm_message_new_with_sub_type (to, LM_MESSAGE_TYPE_IQ, - LM_MESSAGE_SUB_TYPE_ERROR); - - lm_message_node_set_attribute (msg->node, "id", id); - - lm_message_node_steal_children (msg->node, iq_node); - - gabble_xmpp_error_to_node (error, msg->node, errmsg); - - _gabble_connection_send (conn, msg, NULL); - - lm_message_unref (msg); -} - -static LmMessage * -_lm_iq_message_make_result (LmMessage *iq_message) -{ - LmMessage *result; - LmMessageNode *iq, *result_iq; - const gchar *from_jid, *id; - - g_assert (lm_message_get_type (iq_message) == LM_MESSAGE_TYPE_IQ); - g_assert (lm_message_get_sub_type (iq_message) == LM_MESSAGE_SUB_TYPE_GET || - lm_message_get_sub_type (iq_message) == LM_MESSAGE_SUB_TYPE_SET); - - iq = lm_message_get_node (iq_message); - id = lm_message_node_get_attribute (iq, "id"); - - if (id == NULL) - { - NODE_DEBUG (iq, "can't acknowledge IQ with no id"); - return NULL; - } - - from_jid = lm_message_node_get_attribute (iq, "from"); - - result = lm_message_new_with_sub_type (from_jid, LM_MESSAGE_TYPE_IQ, - LM_MESSAGE_SUB_TYPE_RESULT); - result_iq = lm_message_get_node (result); - lm_message_node_set_attribute (result_iq, "id", id); - - return result; -} - -/** - * connection_iq_disco_cb - * - * Called by loudmouth when we get an incoming <iq>. This handler handles - * disco-related IQs. - */ -static LmHandlerResult -connection_iq_disco_cb (LmMessageHandler *handler, - LmConnection *connection, - LmMessage *message, - gpointer user_data) -{ - GabbleConnection *self = GABBLE_CONNECTION (user_data); - LmMessage *result; - LmMessageNode *iq, *result_iq, *query, *result_query; - const gchar *node, *suffix; - GSList *features; - GSList *i; - - if (lm_message_get_sub_type (message) != LM_MESSAGE_SUB_TYPE_GET) - return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; - - iq = lm_message_get_node (message); - query = lm_message_node_get_child_with_namespace (iq, "query", - NS_DISCO_INFO); - - if (!query) - return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; - - node = lm_message_node_get_attribute (query, "node"); - - if (node && ( - 0 != strncmp (node, NS_GABBLE_CAPS "#", strlen (NS_GABBLE_CAPS) + 1) || - strlen (node) < strlen (NS_GABBLE_CAPS) + 2)) - { - NODE_DEBUG (iq, "got iq disco query with unexpected node attribute"); - return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; - } - - if (node == NULL) - suffix = NULL; - else - suffix = node + strlen (NS_GABBLE_CAPS) + 1; - - result = _lm_iq_message_make_result (message); - result_iq = lm_message_get_node (result); - result_query = lm_message_node_add_child (result_iq, "query", NULL); - lm_message_node_set_attribute (result_query, "xmlns", NS_DISCO_INFO); - - if (node) - lm_message_node_set_attribute (result_query, "node", node); - - DEBUG ("got disco request for bundle %s, caps are %x", node, - self->self_presence->caps); - features = capabilities_get_features (self->self_presence->caps); - - g_debug ("%s: caps now %u", G_STRFUNC, self->self_presence->caps); - - for (i = features; NULL != i; i = i->next) - { - const Feature *feature = (const Feature *) i->data; - - if (NULL == node || !tp_strdiff (suffix, feature->bundle)) - { - LmMessageNode *node = lm_message_node_add_child (result_query, - "feature", NULL); - lm_message_node_set_attribute (node, "var", feature->ns); - } - } - - NODE_DEBUG (result_iq, "sending disco response"); - - if (!lm_connection_send (self->lmconn, result, NULL)) - { - DEBUG ("sending disco response failed"); - } - - lm_message_unref (result); - g_slist_free (features); - - return LM_HANDLER_RESULT_REMOVE_MESSAGE; -} - -/** - * connection_iq_unknown_cb - * - * Called by loudmouth when we get an incoming <iq>. This handler is - * at a lower priority than the others, and should reply with an error - * about unsupported get/set attempts. - */ -static LmHandlerResult -connection_iq_unknown_cb (LmMessageHandler *handler, - LmConnection *connection, - LmMessage *message, - gpointer user_data) -{ - GabbleConnection *conn = GABBLE_CONNECTION (user_data); - - g_assert (connection == conn->lmconn); - - NODE_DEBUG (message->node, "got unknown iq"); - - switch (lm_message_get_sub_type (message)) - { - case LM_MESSAGE_SUB_TYPE_GET: - case LM_MESSAGE_SUB_TYPE_SET: - _gabble_connection_send_iq_error (conn, message, - XMPP_ERROR_SERVICE_UNAVAILABLE, NULL); - break; - default: - break; - } - - return LM_HANDLER_RESULT_REMOVE_MESSAGE; -} - -/** - * connection_stream_error_cb - * - * Called by loudmouth when we get stream error, which means that - * we're about to close the connection. The message contains the reason - * for the connection hangup. - */ -static LmHandlerResult -connection_stream_error_cb (LmMessageHandler *handler, - LmConnection *connection, - LmMessage *message, - gpointer user_data) -{ - GabbleConnection *conn = GABBLE_CONNECTION (user_data); - LmMessageNode *conflict_node; - - g_assert (connection == conn->lmconn); - - NODE_DEBUG (message->node, "got stream error"); - - conflict_node = lm_message_node_get_child (message->node, "conflict"); - if (conflict_node) - { - DEBUG ("found conflict node, emiting status change"); - - /* Another client with the same resource just - * appeared, we're going down. */ - tp_base_connection_change_status ((TpBaseConnection *)conn, - TP_CONNECTION_STATUS_DISCONNECTED, - TP_CONNECTION_STATUS_REASON_NAME_IN_USE); - return LM_HANDLER_RESULT_REMOVE_MESSAGE; - } - - return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; -} - - -/** - * connection_ssl_cb - * - * If we're doing old SSL, this function gets called if the certificate - * is dodgy. - */ -static LmSSLResponse -connection_ssl_cb (LmSSL *lmssl, - LmSSLStatus status, - gpointer data) -{ - GabbleConnection *conn = GABBLE_CONNECTION (data); - GabbleConnectionPrivate *priv = GABBLE_CONNECTION_GET_PRIVATE (conn); - const char *reason; - TpConnectionStatusReason tp_reason; - - switch (status) { - case LM_SSL_STATUS_NO_CERT_FOUND: - reason = "The server doesn't provide a certificate."; - tp_reason = TP_CONNECTION_STATUS_REASON_CERT_NOT_PROVIDED; - break; - case LM_SSL_STATUS_UNTRUSTED_CERT: - reason = "The certificate can not be trusted."; - tp_reason = TP_CONNECTION_STATUS_REASON_CERT_UNTRUSTED; - break; - case LM_SSL_STATUS_CERT_EXPIRED: - reason = "The certificate has expired."; - tp_reason = TP_CONNECTION_STATUS_REASON_CERT_EXPIRED; - break; - case LM_SSL_STATUS_CERT_NOT_ACTIVATED: - reason = "The certificate has not been activated."; - tp_reason = TP_CONNECTION_STATUS_REASON_CERT_NOT_ACTIVATED; - break; - case LM_SSL_STATUS_CERT_HOSTNAME_MISMATCH: - reason = "The server hostname doesn't match the one in the certificate."; - tp_reason = TP_CONNECTION_STATUS_REASON_CERT_HOSTNAME_MISMATCH; - break; - case LM_SSL_STATUS_CERT_FINGERPRINT_MISMATCH: - reason = "The fingerprint doesn't match the expected value."; - tp_reason = TP_CONNECTION_STATUS_REASON_CERT_FINGERPRINT_MISMATCH; - break; - case LM_SSL_STATUS_GENERIC_ERROR: - reason = "An unknown SSL error occurred."; - tp_reason = TP_CONNECTION_STATUS_REASON_CERT_OTHER_ERROR; - break; - default: - g_assert_not_reached (); - reason = "Unknown SSL error code from Loudmouth."; - tp_reason = TP_CONNECTION_STATUS_REASON_ENCRYPTION_ERROR; - break; - } - - DEBUG ("called: %s", reason); - - if (priv->ignore_ssl_errors) - { - return LM_SSL_RESPONSE_CONTINUE; - } - else - { - priv->ssl_error = tp_reason; - return LM_SSL_RESPONSE_STOP; - } -} - -static void -do_auth (GabbleConnection *conn) -{ - GabbleConnectionPrivate *priv = GABBLE_CONNECTION_GET_PRIVATE (conn); - GError *error = NULL; - - DEBUG ("authenticating with username: %s, password: <hidden>, resource: %s", - priv->username, priv->resource); - - if (!lm_connection_authenticate (conn->lmconn, priv->username, - priv->password, priv->resource, connection_auth_cb, conn, NULL, - &error)) - { - DEBUG ("failed: %s", error->message); - g_error_free (error); - - /* the reason this function can fail is through network errors, - * authentication failures are reported to our auth_cb */ - tp_base_connection_change_status ((TpBaseConnection *)conn, - TP_CONNECTION_STATUS_DISCONNECTED, - TP_CONNECTION_STATUS_REASON_NETWORK_ERROR); - } -} - -static void -registration_finished_cb (GabbleRegister *reg, - gboolean success, - gint err_code, - const gchar *err_msg, - gpointer user_data) -{ - GabbleConnection *conn = GABBLE_CONNECTION (user_data); - TpBaseConnection *base = (TpBaseConnection *)conn; - - if (base->status != TP_CONNECTION_STATUS_CONNECTING) - { - g_assert (base->status == TP_CONNECTION_STATUS_DISCONNECTED); - return; - } - - DEBUG ("%s", (success) ? "succeeded" : "failed"); - - g_object_unref (reg); - - if (success) - { - do_auth (conn); - } - else - { - DEBUG ("err_code = %d, err_msg = '%s'", - err_code, err_msg); - - tp_base_connection_change_status ((TpBaseConnection *)conn, - TP_CONNECTION_STATUS_DISCONNECTED, - (err_code == TP_ERROR_INVALID_ARGUMENT) ? - TP_CONNECTION_STATUS_REASON_NAME_IN_USE : - TP_CONNECTION_STATUS_REASON_AUTHENTICATION_FAILED); - } -} - -static void -do_register (GabbleConnection *conn) -{ - GabbleRegister *reg; - - reg = gabble_register_new (conn); - - g_signal_connect (reg, "finished", (GCallback) registration_finished_cb, - conn); - - gabble_register_start (reg); -} - -/** - * connection_open_cb - * - * Stage 2 of connecting, this function is called by loudmouth after the - * result of the non-blocking lm_connection_open call is known. It makes - * a request to authenticate the user with the server, or optionally - * registers user on the server first. - */ -static void -connection_open_cb (LmConnection *lmconn, - gboolean success, - gpointer data) -{ - GabbleConnection *conn = GABBLE_CONNECTION (data); - GabbleConnectionPrivate *priv = GABBLE_CONNECTION_GET_PRIVATE (conn); - TpBaseConnection *base = (TpBaseConnection *)conn; - - if ((base->status != TP_CONNECTION_STATUS_CONNECTING) && - (base->status != TP_INTERNAL_CONNECTION_STATUS_NEW)) - { - g_assert (base->status == TP_CONNECTION_STATUS_DISCONNECTED); - return; - } - - g_assert (priv); - g_assert (lmconn == conn->lmconn); - - if (!success) - { - if (lm_connection_get_proxy (lmconn)) - { - DEBUG ("failed, retrying without proxy"); - - lm_connection_set_proxy (lmconn, NULL); - - if (do_connect (conn, NULL)) - { - return; - } - } - else - { - DEBUG ("failed"); - } - - if (priv->ssl_error) - { - tp_base_connection_change_status ((TpBaseConnection *)conn, - TP_CONNECTION_STATUS_DISCONNECTED, - priv->ssl_error); - } - else - { - tp_base_connection_change_status ((TpBaseConnection *)conn, - TP_CONNECTION_STATUS_DISCONNECTED, - TP_CONNECTION_STATUS_REASON_NETWORK_ERROR); - } - - return; - } - - if (!priv->do_register) - do_auth (conn); - else - do_register (conn); -} - -/** - * connection_auth_cb - * - * Stage 3 of connecting, this function is called by loudmouth after the - * result of the non-blocking lm_connection_authenticate call is known. - * It sends a discovery request to find the server's features. - */ -static void -connection_auth_cb (LmConnection *lmconn, - gboolean success, - gpointer data) -{ - GabbleConnection *conn = GABBLE_CONNECTION (data); - TpBaseConnection *base = (TpBaseConnection *)conn; - GabbleConnectionPrivate *priv = GABBLE_CONNECTION_GET_PRIVATE (conn); - GError *error = NULL; - - if (base->status != TP_CONNECTION_STATUS_CONNECTING) - { - g_assert (base->status == TP_CONNECTION_STATUS_DISCONNECTED); - return; - } - - g_assert (priv); - g_assert (lmconn == conn->lmconn); - - if (!success) - { - DEBUG ("failed"); - - tp_base_connection_change_status ((TpBaseConnection *)conn, - TP_CONNECTION_STATUS_DISCONNECTED, - TP_CONNECTION_STATUS_REASON_AUTHENTICATION_FAILED); - - return; - } - - if (!gabble_disco_request_with_timeout (conn->disco, GABBLE_DISCO_TYPE_INFO, - priv->stream_server, NULL, 5000, - connection_disco_cb, conn, - G_OBJECT (conn), &error)) - { - DEBUG ("sending disco request failed: %s", - error->message); - - g_error_free (error); - - tp_base_connection_change_status ((TpBaseConnection *)conn, - TP_CONNECTION_STATUS_DISCONNECTED, - TP_CONNECTION_STATUS_REASON_NETWORK_ERROR); - } -} - -/** - * connection_disco_cb - * - * Stage 4 of connecting, this function is called by GabbleDisco after the - * result of the non-blocking server feature discovery call is known. It sends - * the user's initial presence to the server, marking them as available, - * and requests the roster. - */ -static void -connection_disco_cb (GabbleDisco *disco, - GabbleDiscoRequest *request, - const gchar *jid, - const gchar *node, - LmMessageNode *result, - GError *disco_error, - gpointer user_data) -{ - GabbleConnection *conn = user_data; - TpBaseConnection *base = (TpBaseConnection *)conn; - GabbleConnectionPrivate *priv; - GError *error = NULL; - - if (base->status != TP_CONNECTION_STATUS_CONNECTING) - { - g_assert (base->status == TP_CONNECTION_STATUS_DISCONNECTED); - return; - } - - g_assert (GABBLE_IS_CONNECTION (conn)); - priv = GABBLE_CONNECTION_GET_PRIVATE (conn); - - if (disco_error) - { - DEBUG ("got disco error, setting no features: %s", disco_error->message); - } - else - { - LmMessageNode *iter; - - NODE_DEBUG (result, "got"); - - for (iter = result->children; iter != NULL; iter = iter->next) - { - if (0 == strcmp (iter->name, "identity")) - { - const gchar *category = lm_message_node_get_attribute (iter, - "category"); - const gchar *type = lm_message_node_get_attribute (iter, "type"); - - if (!tp_strdiff (category, "pubsub") && - !tp_strdiff (type, "pep")) - /* XXX: should we also check for specific PubSub <feature>s? */ - conn->features |= GABBLE_CONNECTION_FEATURES_PEP; - } - else if (0 == strcmp (iter->name, "feature")) - { - const gchar *var = lm_message_node_get_attribute (iter, "var"); - - if (var == NULL) - continue; - - if (0 == strcmp (var, NS_GOOGLE_JINGLE_INFO)) - conn->features |= GABBLE_CONNECTION_FEATURES_GOOGLE_JINGLE_INFO; - else if (0 == strcmp (var, NS_GOOGLE_ROSTER)) - conn->features |= GABBLE_CONNECTION_FEATURES_GOOGLE_ROSTER; - else if (0 == strcmp (var, NS_PRESENCE_INVISIBLE)) - conn->features |= GABBLE_CONNECTION_FEATURES_PRESENCE_INVISIBLE; - else if (0 == strcmp (var, NS_PRIVACY)) - conn->features |= GABBLE_CONNECTION_FEATURES_PRIVACY; - } - } - - DEBUG ("set features flags to %d", conn->features); - } - - /* send presence to the server to indicate availability */ - /* TODO: some way for the user to set this */ - if (!_gabble_connection_signal_own_presence (conn, &error)) - { - DEBUG ("sending initial presence failed: %s", error->message); - goto ERROR; - } - - /* go go gadget on-line */ - tp_base_connection_change_status (base, - TP_CONNECTION_STATUS_CONNECTED, TP_CONNECTION_STATUS_REASON_REQUESTED); - - return; - -ERROR: - if (error) - g_error_free (error); - - tp_base_connection_change_status (base, - TP_CONNECTION_STATUS_DISCONNECTED, - TP_CONNECTION_STATUS_REASON_NETWORK_ERROR); - - return; -} - - -/**************************************************************************** - * D-BUS EXPORTED METHODS * - ****************************************************************************/ - - -static void -_emit_capabilities_changed (GabbleConnection *conn, - TpHandle handle, - GabblePresenceCapabilities old_caps, - GabblePresenceCapabilities new_caps) -{ - GPtrArray *caps_arr; - const CapabilityConversionData *ccd; - guint i; - - if (old_caps == new_caps) - return; - - caps_arr = g_ptr_array_new (); - - for (ccd = capabilities_conversions; NULL != ccd->iface; ccd++) - { - if (ccd->c2tf_fn (old_caps | new_caps)) - { - GValue caps_monster_struct = {0, }; - guint old_tpflags = ccd->c2tf_fn (old_caps); - guint old_caps = old_tpflags ? - TP_CONNECTION_CAPABILITY_FLAG_CREATE | - TP_CONNECTION_CAPABILITY_FLAG_INVITE : 0; - guint new_tpflags = ccd->c2tf_fn (new_caps); - guint new_caps = new_tpflags ? - TP_CONNECTION_CAPABILITY_FLAG_CREATE | - TP_CONNECTION_CAPABILITY_FLAG_INVITE : 0; - - if (0 == (old_tpflags ^ new_tpflags)) - continue; - - g_value_init (&caps_monster_struct, - TP_CAPABILITIES_CHANGED_MONSTER_TYPE); - g_value_take_boxed (&caps_monster_struct, - dbus_g_type_specialized_construct - (TP_CAPABILITIES_CHANGED_MONSTER_TYPE)); - - dbus_g_type_struct_set (&caps_monster_struct, - 0, handle, - 1, ccd->iface, - 2, old_caps, - 3, new_caps, - 4, old_tpflags, - 5, new_tpflags, - G_MAXUINT); - - g_ptr_array_add (caps_arr, g_value_get_boxed (&caps_monster_struct)); - } - } - - if (caps_arr->len) - tp_svc_connection_interface_capabilities_emit_capabilities_changed ( - conn, caps_arr); - - - for (i = 0; i < caps_arr->len; i++) - { - g_boxed_free (TP_CAPABILITIES_CHANGED_MONSTER_TYPE, - g_ptr_array_index (caps_arr, i)); - } - g_ptr_array_free (caps_arr, TRUE); -} - -static void -connection_capabilities_update_cb (GabblePresenceCache *cache, - TpHandle handle, - GabblePresenceCapabilities old_caps, - GabblePresenceCapabilities new_caps, - gpointer user_data) -{ - GabbleConnection *conn = GABBLE_CONNECTION (user_data); - - _emit_capabilities_changed (conn, handle, old_caps, new_caps); -} - -/** - * gabble_connection_advertise_capabilities - * - * Implements D-Bus method AdvertiseCapabilities - * on interface org.freedesktop.Telepathy.Connection.Interface.Capabilities - * - * @error: Used to return a pointer to a GError detailing any error - * that occurred, D-Bus will throw the error only if this - * function returns FALSE. - * - * Returns: TRUE if successful, FALSE if an error was thrown. - */ -static void -gabble_connection_advertise_capabilities (TpSvcConnectionInterfaceCapabilities *iface, - const GPtrArray *add, - const gchar **remove, - DBusGMethodInvocation *context) -{ - GabbleConnection *self = GABBLE_CONNECTION (iface); - TpBaseConnection *base = (TpBaseConnection *)self; - guint i; - GabblePresence *pres = self->self_presence; - GabblePresenceCapabilities add_caps = 0, remove_caps = 0, caps, save_caps; - GabbleConnectionPrivate *priv = GABBLE_CONNECTION_GET_PRIVATE (self); - const CapabilityConversionData *ccd; - GPtrArray *ret; - GError *error = NULL; - - TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); - - DEBUG ("caps before: %x", pres->caps); - - for (i = 0; i < add->len; i++) - { - GValue iface_flags_pair = {0, }; - gchar *iface; - guint flags; - - g_value_init (&iface_flags_pair, TP_CAPABILITY_PAIR_TYPE); - g_value_set_static_boxed (&iface_flags_pair, g_ptr_array_index (add, i)); - - dbus_g_type_struct_get (&iface_flags_pair, - 0, &iface, - 1, &flags, - G_MAXUINT); - - for (ccd = capabilities_conversions; NULL != ccd->iface; ccd++) - if (g_str_equal (iface, ccd->iface)) - add_caps |= ccd->tf2c_fn (flags); - - g_free (iface); - } - - for (i = 0; NULL != remove[i]; i++) - { - for (ccd = capabilities_conversions; NULL != ccd->iface; ccd++) - if (g_str_equal (remove[i], ccd->iface)) - remove_caps |= ccd->tf2c_fn (~0); - } - - save_caps = caps = pres->caps; - - caps |= add_caps; - caps ^= (caps & remove_caps); - - DEBUG ("caps to add: %x", add_caps); - DEBUG ("caps to remove: %x", remove_caps); - DEBUG ("caps after: %x", caps); - - if (caps ^ save_caps) - { - DEBUG ("before != after, changing"); - gabble_presence_set_capabilities (pres, priv->resource, caps, - priv->caps_serial++); - DEBUG ("set caps: %x", pres->caps); - } - - ret = g_ptr_array_new (); - - for (ccd = capabilities_conversions; NULL != ccd->iface; ccd++) - { - if (ccd->c2tf_fn (pres->caps)) - { - GValue iface_flags_pair = {0, }; - - g_value_init (&iface_flags_pair, TP_CAPABILITY_PAIR_TYPE); - g_value_take_boxed (&iface_flags_pair, - dbus_g_type_specialized_construct (TP_CAPABILITY_PAIR_TYPE)); - - dbus_g_type_struct_set (&iface_flags_pair, - 0, ccd->iface, - 1, ccd->c2tf_fn (pres->caps), - G_MAXUINT); - - g_ptr_array_add (ret, g_value_get_boxed (&iface_flags_pair)); - } - } - - if (caps ^ save_caps) - { - if (!_gabble_connection_signal_own_presence (self, &error)) - { - dbus_g_method_return_error (context, error); - return; - } - - _emit_capabilities_changed (self, base->self_handle, - save_caps, caps); - } - - tp_svc_connection_interface_capabilities_return_from_advertise_capabilities ( - context, ret); - g_ptr_array_free (ret, TRUE); -} - -static const gchar *assumed_caps[] = -{ - TP_IFACE_CHANNEL_TYPE_TEXT, - TP_IFACE_CHANNEL_INTERFACE_GROUP, - NULL -}; - - -/** - * gabble_connection_get_capabilities - * - * Implements D-Bus method GetCapabilities - * on interface org.freedesktop.Telepathy.Connection.Interface.Capabilities - * - * @error: Used to return a pointer to a GError detailing any error - * that occurred, D-Bus will throw the error only if this - * function returns FALSE. - * - * Returns: TRUE if successful, FALSE if an error was thrown. - */ -static void -gabble_connection_get_capabilities (TpSvcConnectionInterfaceCapabilities *iface, - const GArray *handles, - DBusGMethodInvocation *context) -{ - GabbleConnection *self = GABBLE_CONNECTION (iface); - TpBaseConnection *base = (TpBaseConnection *)self; - TpHandleRepoIface *contact_handles = tp_base_connection_get_handles (base, - TP_HANDLE_TYPE_CONTACT); - guint i; - GPtrArray *ret; - GError *error = NULL; - - TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); - - if (!tp_handles_are_valid (contact_handles, handles, TRUE, &error)) - { - dbus_g_method_return_error (context, error); - g_error_free (error); - } - - ret = g_ptr_array_new (); - - for (i = 0; i < handles->len; i++) - { - TpHandle handle = g_array_index (handles, guint, i); - GabblePresence *pres; - const CapabilityConversionData *ccd; - guint typeflags; - const gchar **assumed; - - if (0 == handle) - { - /* FIXME report the magical channel types available on the - * connection itself */ - continue; - } - - if (handle == base->self_handle) - pres = self->self_presence; - else - pres = gabble_presence_cache_get (self->presence_cache, handle); - - if (NULL != pres) - for (ccd = capabilities_conversions; NULL != ccd->iface; ccd++) - { - typeflags = ccd->c2tf_fn (pres->caps); - - if (typeflags) - { - GValue monster = {0, }; - - g_value_init (&monster, TP_GET_CAPABILITIES_MONSTER_TYPE); - g_value_take_boxed (&monster, - dbus_g_type_specialized_construct ( - TP_GET_CAPABILITIES_MONSTER_TYPE)); - - dbus_g_type_struct_set (&monster, - 0, handle, - 1, ccd->iface, - 2, TP_CONNECTION_CAPABILITY_FLAG_CREATE | - TP_CONNECTION_CAPABILITY_FLAG_INVITE, - 3, typeflags, - G_MAXUINT); - - g_ptr_array_add (ret, g_value_get_boxed (&monster)); - } - } - - for (assumed = assumed_caps; NULL != *assumed; assumed++) - { - GValue monster = {0, }; - - g_value_init (&monster, TP_GET_CAPABILITIES_MONSTER_TYPE); - g_value_take_boxed (&monster, - dbus_g_type_specialized_construct (TP_GET_CAPABILITIES_MONSTER_TYPE)); - - dbus_g_type_struct_set (&monster, - 0, handle, - 1, *assumed, - 2, TP_CONNECTION_CAPABILITY_FLAG_CREATE | - TP_CONNECTION_CAPABILITY_FLAG_INVITE, - 3, 0, - G_MAXUINT); - - g_ptr_array_add (ret, g_value_get_boxed (&monster)); - } - } - - tp_svc_connection_interface_capabilities_return_from_get_capabilities ( - context, ret); - g_ptr_array_free (ret, TRUE); -} - - -const char * -_gabble_connection_find_conference_server (GabbleConnection *conn) -{ - GabbleConnectionPrivate *priv; - - g_assert (GABBLE_IS_CONNECTION (conn)); - - priv = GABBLE_CONNECTION_GET_PRIVATE (conn); - - if (priv->conference_server == NULL) - { - /* Find first server that has NS_MUC feature */ - const GabbleDiscoItem *item = gabble_disco_service_find (conn->disco, - NULL, NULL, NS_MUC); - if (item != NULL) - priv->conference_server = item->jid; - } - - if (priv->conference_server == NULL) - priv->conference_server = priv->fallback_conference_server; - - return priv->conference_server; -} - - -static gchar * -_gabble_connection_get_canonical_room_name (GabbleConnection *conn, - const gchar *name) -{ - const gchar *server; - - g_assert (GABBLE_IS_CONNECTION (conn)); - - if (index (name, '@')) - return g_strdup (name); - - server = _gabble_connection_find_conference_server (conn); - - if (server == NULL) - return NULL; - - return g_strdup_printf ("%s@%s", name, server); -} - - -typedef struct _RoomVerifyContext RoomVerifyContext; - -typedef struct { - GabbleConnection *conn; - DBusGMethodInvocation *invocation; - gboolean errored; - guint count; - GArray *handles; - RoomVerifyContext *contexts; -} RoomVerifyBatch; - -struct _RoomVerifyContext { - gchar *jid; - guint index; - RoomVerifyBatch *batch; - GabbleDiscoRequest *request; -}; - -static void -room_verify_batch_free (RoomVerifyBatch *batch) -{ - TpBaseConnection *base = (TpBaseConnection *)(batch->conn); - TpHandleRepoIface *room_handles = tp_base_connection_get_handles (base, - TP_HANDLE_TYPE_ROOM); - guint i; - - tp_handles_unref (room_handles, batch->handles); - g_array_free (batch->handles, TRUE); - for (i = 0; i < batch->count; i++) - { - g_free (batch->contexts[i].jid); - } - g_free (batch->contexts); - g_slice_free (RoomVerifyBatch, batch); -} - -/* Frees the error and the batch. */ -static void -room_verify_batch_raise_error (RoomVerifyBatch *batch, - GError *error) -{ - guint i; - - dbus_g_method_return_error (batch->invocation, error); - g_error_free (error); - batch->errored = TRUE; - for (i = 0; i < batch->count; i++) - { - if (batch->contexts[i].request) - { - gabble_disco_cancel_request (batch->conn->disco, - batch->contexts[i].request); - } - } - room_verify_batch_free (batch); -} - -static RoomVerifyBatch * -room_verify_batch_new (GabbleConnection *conn, - DBusGMethodInvocation *invocation, - guint count, - const gchar **jids) -{ - TpBaseConnection *base = (TpBaseConnection *)conn; - TpHandleRepoIface *room_handles = tp_base_connection_get_handles (base, - TP_HANDLE_TYPE_ROOM); - RoomVerifyBatch *batch = g_slice_new (RoomVerifyBatch); - guint i; - - batch->errored = FALSE; - batch->conn = conn; - batch->invocation = invocation; - batch->count = count; - batch->handles = g_array_sized_new (FALSE, FALSE, sizeof (TpHandle), count); - batch->contexts = g_new0(RoomVerifyContext, count); - for (i = 0; i < count; i++) - { - const gchar *name = jids[i]; - gchar *qualified_name; - TpHandle handle; - - batch->contexts[i].index = i; - batch->contexts[i].batch = batch; - - qualified_name = _gabble_connection_get_canonical_room_name (conn, name); - - if (!qualified_name) - { - GError *error = g_error_new (TP_ERRORS, TP_ERROR_NOT_AVAILABLE, - "requested room handle %s does not specify a server, but we " - "have not discovered any local conference servers and no " - "fallback was provided", name); - DEBUG ("%s", error->message); - room_verify_batch_raise_error (batch, error); - return NULL; - } - - batch->contexts[i].jid = qualified_name; - - /* has the handle been verified before? */ - handle = tp_handle_lookup (room_handles, qualified_name, NULL, NULL); - if (handle) - tp_handle_ref (room_handles, handle); - g_array_append_val (batch->handles, handle); - } - - return batch; -} - -/* If all handles in the array have been disco'd or got from cache, -free the batch and return TRUE. Else return FALSE. */ -static gboolean -room_verify_batch_try_return (RoomVerifyBatch *batch) -{ - guint i; - TpHandleRepoIface *room_handles = tp_base_connection_get_handles ( - (TpBaseConnection *)batch->conn, TP_HANDLE_TYPE_ROOM); - gchar *sender; - GError *error = NULL; - - for (i = 0; i < batch->count; i++) - { - if (!g_array_index (batch->handles, TpHandle, i)) - { - /* we're not ready yet */ - return FALSE; - } - } - - sender = dbus_g_method_get_sender (batch->invocation); - if (!tp_handles_client_hold (room_handles, sender, batch->handles, &error)) - { - g_assert (error != NULL); - } - g_free (sender); - - if (error == NULL) - { - tp_svc_connection_return_from_request_handles (batch->invocation, - batch->handles); - } - else - { - dbus_g_method_return_error (batch->invocation, error); - g_error_free (error); - } - - room_verify_batch_free (batch); - return TRUE; -} - -static void -room_jid_disco_cb (GabbleDisco *disco, - GabbleDiscoRequest *request, - const gchar *jid, - const gchar *node, - LmMessageNode *query_result, - GError *error, - gpointer user_data) -{ - RoomVerifyContext *rvctx = user_data; - RoomVerifyBatch *batch = rvctx->batch; - TpHandleRepoIface *room_handles = tp_base_connection_get_handles ( - (TpBaseConnection *)batch->conn, TP_HANDLE_TYPE_ROOM); - LmMessageNode *lm_node; - gboolean found = FALSE; - TpHandle handle; - - /* stop the request getting cancelled after it's already finished */ - rvctx->request = NULL; - - /* if an error is being handled already, quietly go away */ - if (batch->errored) - { - return; - } - - if (error != NULL) - { - DEBUG ("disco reply error %s", error->message); - - /* disco will free the old error, _raise_error will free the new one */ - error = g_error_new (TP_ERRORS, TP_ERROR_NOT_AVAILABLE, - "can't retrieve room info: %s", error->message); - - room_verify_batch_raise_error (batch, error); - - return; - } - - for (lm_node = query_result->children; lm_node; lm_node = lm_node->next) - { - const gchar *var; - - if (tp_strdiff (lm_node->name, "feature")) - continue; - - var = lm_message_node_get_attribute (lm_node, "var"); - - /* for servers who consider schema compliance to be an optional bonus */ - if (var == NULL) - var = lm_message_node_get_attribute (lm_node, "type"); - - if (!tp_strdiff (var, NS_MUC)) - { - found = TRUE; - break; - } - } - - if (!found) - { - DEBUG ("no MUC support for service name in jid %s", rvctx->jid); - - error = g_error_new (TP_ERRORS, TP_ERROR_NOT_AVAILABLE, - "specified server doesn't support MUC"); - - room_verify_batch_raise_error (batch, error); - - return; - } - - /* this refs the handle, so we're putting a ref in batch->handles */ - handle = tp_handle_ensure (room_handles, rvctx->jid, NULL, &error); - if (handle == 0) - { - room_verify_batch_raise_error (batch, error); - } - - DEBUG ("disco reported MUC support for service name in jid %s", rvctx->jid); - g_array_index (batch->handles, TpHandle, rvctx->index) = handle; - - /* if this was the last callback to be run, send off the result */ - room_verify_batch_try_return (batch); -} - -/** - * room_jid_verify: - * - * Utility function that verifies that the service name of - * the specified jid exists and reports MUC support. - */ -static gboolean -room_jid_verify (RoomVerifyBatch *batch, - guint index, - DBusGMethodInvocation *context) -{ - gchar *room, *service; - gboolean ret; - GError *error = NULL; - - room = service = NULL; - gabble_decode_jid (batch->contexts[index].jid, &room, &service, NULL); - - if (room == NULL || *room == '\0' || service == NULL || *service == '\0') - { - g_set_error (&error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, - "unable to get room name and service from JID %s", - batch->contexts[index].jid); - - ret = FALSE; - - goto out; - } - - ret = (gabble_disco_request (batch->conn->disco, GABBLE_DISCO_TYPE_INFO, - service, NULL, room_jid_disco_cb, - batch->contexts + index, - G_OBJECT (batch->conn), &error) != NULL); - -out: - if (!ret) - { - room_verify_batch_raise_error (batch, error); - } - - g_free (room); - g_free (service); - - return ret; -} - - -/** - * gabble_connection_request_handles - * - * Implements D-Bus method RequestHandles - * on interface org.freedesktop.Telepathy.Connection - * - * @context: The D-Bus invocation context to use to return values - * or throw an error. - */ -static void -gabble_connection_request_handles (TpSvcConnection *iface, - guint handle_type, - const gchar **names, - DBusGMethodInvocation *context) -{ - GabbleConnection *self = GABBLE_CONNECTION (iface); - TpBaseConnection *base = (TpBaseConnection *)self; - - g_assert (GABBLE_IS_CONNECTION (self)); - - TP_BASE_CONNECTION_ERROR_IF_NOT_CONNECTED (base, context); - - if (handle_type == TP_HANDLE_TYPE_ROOM) - { - RoomVerifyBatch *batch = NULL; - guint count = 0, i; - const gchar **cur_name; - - for (cur_name = names; *cur_name != NULL; cur_name++) - { - count++; - } - - batch = room_verify_batch_new (self, context, count, names); - if (!batch) - { - /* an error occurred while setting up the batch, and we returned - error to dbus */ - return; - } - - /* have all the handles been verified already? If so, nothing to do */ - if (room_verify_batch_try_return (batch)) - { - return; - } - - for (i = 0; i < count; i++) - { - if (!room_jid_verify (batch, i, context)) - { - return; - } - } - - /* we've set the verification process going - the callback will handle - returning or raising error */ - return; - } - - /* else it's either an invalid type, or a type we can verify immediately - - * in either case, let the superclass do it */ - tp_base_connection_dbus_request_handles (iface, handle_type, names, context); -} - - -/* We reimplement RequestHandles to be able to do async validation on - * room handles */ -static void -conn_service_iface_init (gpointer g_iface, gpointer iface_data) -{ - TpSvcConnectionClass *klass = (TpSvcConnectionClass *)g_iface; - -#define IMPLEMENT(x) tp_svc_connection_implement_##x (klass, \ - gabble_connection_##x) - IMPLEMENT(request_handles); -#undef IMPLEMENT -} - -static void -capabilities_service_iface_init (gpointer g_iface, gpointer iface_data) -{ - TpSvcConnectionInterfaceCapabilitiesClass *klass = - (TpSvcConnectionInterfaceCapabilitiesClass *)g_iface; - -#define IMPLEMENT(x) tp_svc_connection_interface_capabilities_implement_##x (\ - klass, gabble_connection_##x) - IMPLEMENT(advertise_capabilities); - IMPLEMENT(get_capabilities); -#undef IMPLEMENT -} - diff --git a/src/gabble-connection.h b/src/gabble-connection.h deleted file mode 100644 index d844c13fb..000000000 --- a/src/gabble-connection.h +++ /dev/null @@ -1,177 +0,0 @@ -/* - * gabble-connection.h - Header for GabbleConnection - * Copyright (C) 2005 Collabora Ltd. - * Copyright (C) 2005 Nokia Corporation - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef __GABBLE_CONNECTION_H__ -#define __GABBLE_CONNECTION_H__ - -#include <dbus/dbus-glib.h> -#include <glib-object.h> -#include <loudmouth/loudmouth.h> - -#include "gabble-types.h" -#include "gabble-error.h" -#include <telepathy-glib/base-connection.h> - -G_BEGIN_DECLS - -/* Default parameters for optional parameters */ -#define GABBLE_PARAMS_DEFAULT_RESOURCE "Telepathy" -#define GABBLE_PARAMS_DEFAULT_PORT 5222 -#define GABBLE_PARAMS_DEFAULT_HTTPS_PROXY_PORT 443 -#define GABBLE_PARAMS_DEFAULT_STUN_PORT 3478 - -/* order must match array of statuses in conn-presence.c */ -/* in increasing order of presence */ -typedef enum -{ - GABBLE_PRESENCE_OFFLINE = 0, - GABBLE_PRESENCE_HIDDEN, - GABBLE_PRESENCE_XA, - GABBLE_PRESENCE_AWAY, - GABBLE_PRESENCE_DND, - GABBLE_PRESENCE_AVAILABLE, - GABBLE_PRESENCE_CHAT, - NUM_GABBLE_PRESENCES -} GabblePresenceId; - -typedef enum -{ - GABBLE_CONNECTION_FEATURES_NONE = 0, - GABBLE_CONNECTION_FEATURES_GOOGLE_JINGLE_INFO = 1 << 0, - GABBLE_CONNECTION_FEATURES_GOOGLE_ROSTER = 1 << 1, - GABBLE_CONNECTION_FEATURES_PRESENCE_INVISIBLE = 1 << 2, - GABBLE_CONNECTION_FEATURES_PRIVACY = 1 << 3, - GABBLE_CONNECTION_FEATURES_PEP = 1 << 4, -} GabbleConnectionFeatures; - -typedef struct _GabbleConnectionClass GabbleConnectionClass; - -typedef LmHandlerResult (*GabbleConnectionMsgReplyFunc) (GabbleConnection *conn, - LmMessage *sent_msg, - LmMessage *reply_msg, - GObject *object, - gpointer user_data); - -/* must be in the same order as the list_handle_strings in - * gabble-connection.c */ -typedef enum -{ - GABBLE_LIST_HANDLE_PUBLISH = 1, - GABBLE_LIST_HANDLE_SUBSCRIBE, - GABBLE_LIST_HANDLE_KNOWN, - GABBLE_LIST_HANDLE_DENY -} GabbleListHandle; - -typedef enum { - /* The JID could be a "global" JID, or a MUC room member. We'll assume - * that it's a global JID (and remove the resource) unless we've seen - * that JID in a MUC before. - */ - GABBLE_JID_ANY = 0, - /* The JID is definitely global. Remove the resource. */ - GABBLE_JID_GLOBAL, - /* The JID is definitely a room member. Assert that there is a "resource" - * (nickname) and don't remove it. */ - GABBLE_JID_ROOM_MEMBER -} GabbleNormalizeContactJIDMode; - -struct _GabbleConnectionClass { - TpBaseConnectionClass parent_class; -}; - -struct _GabbleConnection { - TpBaseConnection parent; - - /* loudmouth connection */ - LmConnection *lmconn; - - /* roster */ - GabbleRoster *roster; - - /* DISCO! */ - GabbleDisco *disco; - - /* connection feature flags */ - GabbleConnectionFeatures features; - - /* presence */ - GabblePresenceCache *presence_cache; - GabblePresence *self_presence; - - /* vCard lookup helper */ - GabbleVCardManager *vcard_manager; - - gpointer priv; -}; - -typedef enum { - GABBLE_CONNECTION_ALIAS_NONE = 0, - GABBLE_CONNECTION_ALIAS_FROM_JID, - GABBLE_CONNECTION_ALIAS_FROM_VCARD, - GABBLE_CONNECTION_ALIAS_FROM_CONNMGR, - GABBLE_CONNECTION_ALIAS_FROM_PRESENCE, - GABBLE_CONNECTION_ALIAS_FROM_ROSTER -} GabbleConnectionAliasSource; - -GType gabble_connection_get_type (void); - -/* TYPE MACROS */ -#define GABBLE_TYPE_CONNECTION \ - (gabble_connection_get_type ()) -#define GABBLE_CONNECTION(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_CONNECTION, GabbleConnection)) -#define GABBLE_CONNECTION_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_CONNECTION, \ - GabbleConnectionClass)) -#define GABBLE_IS_CONNECTION(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_CONNECTION)) -#define GABBLE_IS_CONNECTION_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_CONNECTION)) -#define GABBLE_CONNECTION_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_CONNECTION, \ - GabbleConnectionClass)) - -gboolean _gabble_connection_set_properties_from_account ( - GabbleConnection *conn, const gchar *account, GError **error); -gboolean _gabble_connection_send (GabbleConnection *conn, LmMessage *msg, - GError **error); -gboolean _gabble_connection_send_with_reply (GabbleConnection *conn, - LmMessage *msg, GabbleConnectionMsgReplyFunc reply_func, GObject *object, - gpointer user_data, GError **error); -void _gabble_connection_acknowledge_set_iq (GabbleConnection *conn, - LmMessage *iq); -void _gabble_connection_send_iq_error (GabbleConnection *conn, - LmMessage *message, GabbleXmppError error, const gchar *errmsg); - -GabbleConnectionAliasSource _gabble_connection_get_cached_alias ( - GabbleConnection *, TpHandle, gchar **); - -const char *_gabble_connection_find_conference_server (GabbleConnection *); -gboolean _gabble_connection_signal_own_presence (GabbleConnection *, - GError **); - -/* extern only for the benefit of the unit tests */ -void _gabble_connection_create_handle_repos (TpBaseConnection *conn, - TpHandleRepoIface *repos[NUM_TP_HANDLE_TYPES]); - - -G_END_DECLS - -#endif /* #ifndef __GABBLE_CONNECTION_H__*/ diff --git a/src/gabble-error.c b/src/gabble-error.c deleted file mode 100644 index e49412dd8..000000000 --- a/src/gabble-error.c +++ /dev/null @@ -1,426 +0,0 @@ -/* - * gabble-error.c - Source for Gabble's error handling API - * Copyright (C) 2006 Collabora Ltd. - * Copyright (C) 2006 Nokia Corporation - * @author Ole Andre Vadla Ravnaas <ole.andre.ravnaas@collabora.co.uk> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "gabble-error.h" - -#include <stdlib.h> -#include <stdio.h> - -#include "namespaces.h" - -#define MAX_LEGACY_ERRORS 3 - -typedef struct { - const gchar *name; - const gchar *description; - const gchar *type; - guint specialises; - const gchar *namespace; - const guint16 legacy_errors[MAX_LEGACY_ERRORS]; -} XmppErrorSpec; - -static const XmppErrorSpec xmpp_errors[NUM_XMPP_ERRORS] = -{ - { - "redirect", - "the recipient or server is redirecting requests for this information " - "to another entity", - "modify", - 0, - NULL, - { 302, 0, }, - }, - - { - "gone", - "the recipient or server can no longer be contacted at this address", - "modify", - 0, - NULL, - { 302, 0, }, - }, - - { - "bad-request", - "the sender has sent XML that is malformed or that cannot be processed", - "modify", - 0, - NULL, - { 400, 0, }, - }, - { - "unexpected-request", - "the recipient or server understood the request but was not expecting " - "it at this time", - "wait", - 0, - NULL, - { 400, 0, }, - }, - { - "jid-malformed", - "the sending entity has provided or communicated an XMPP address or " - "aspect thereof (e.g., a resource identifier) that does not adhere " - "to the syntax defined in Addressing Scheme (Section 3)", - "modify", - 0, - NULL, - { 400, 0, }, - }, - - { - "not-authorized", - "the sender must provide proper credentials before being allowed to " - "perform the action, or has provided improper credentials", - "auth", - 0, - NULL, - { 401, 0, }, - }, - - { - "payment-required", - "the requesting entity is not authorized to access the requested " - "service because payment is required", - "auth", - 0, - NULL, - { 402, 0, }, - }, - - { - "forbidden", - "the requesting entity does not possess the required permissions to " - "perform the action", - "auth", - 0, - NULL, - { 403, 0, }, - }, - - { - "item-not-found", - "the addressed JID or item requested cannot be found", - "cancel", - 0, - NULL, - { 404, 0, }, - }, - { - "recipient-unavailable", - "the intended recipient is temporarily unavailable", - "wait", - 0, - NULL, - { 404, 0, }, - }, - { - "remote-server-not-found", - "a remote server or service specified as part or all of the JID of the " - "intended recipient (or required to fulfill a request) could not be " - "contacted within a reasonable amount of time", - "cancel", - 0, - NULL, - { 404, 0, }, - }, - - { - "not-allowed", - "the recipient or server does not allow any entity to perform the action", - "cancel", - 0, - NULL, - { 405, 0, }, - }, - - { - "not-acceptable", - "the recipient or server understands the request but is refusing to " - "process it because it does not meet criteria defined by the recipient " - "or server (e.g., a local policy regarding acceptable words in messages)", - "modify", - 0, - NULL, - { 406, 0, }, - }, - - { - "registration-required", - "the requesting entity is not authorized to access the requested service " - "because registration is required", - "auth", - 0, - NULL, - { 407, 0, }, - }, - { - "subscription-required", - "the requesting entity is not authorized to access the requested service " - "because a subscription is required", - "auth", - 0, - NULL, - { 407, 0, }, - }, - - { - "remote-server-timeout", - "a remote server or service specified as part or all of the JID of the " - "intended recipient (or required to fulfill a request) could not be " - "contacted within a reasonable amount of time", - "wait", - 0, - NULL, - { 408, 504, 0, }, - }, - - { - "conflict", - "access cannot be granted because an existing resource or session exists " - "with the same name or address", - "cancel", - 0, - NULL, - { 409, 0, }, - }, - - { - "internal-server-error", - "the server could not process the stanza because of a misconfiguration " - "or an otherwise-undefined internal server error", - "wait", - 0, - NULL, - { 500, 0, }, - }, - { - "undefined-condition", - "application-specific condition", - NULL, - 0, - NULL, - { 500, 0, }, - }, - { - "resource-constraint", - "the server or recipient lacks the system resources necessary to service " - "the request", - "wait", - 0, - NULL, - { 500, 0, }, - }, - - { - "feature-not-implemented", - "the feature requested is not implemented by the recipient or server and " - "therefore cannot be processed", - "cancel", - 0, - NULL, - { 501, 0, }, - }, - - { - "service-unavailable", - "the server or recipient does not currently provide the requested " - "service", - "cancel", - 0, - NULL, - { 502, 503, 510, }, - }, - - { - "out-of-order", - "the request cannot occur at this point in the state machine", - "cancel", - XMPP_ERROR_UNEXPECTED_REQUEST, - NS_JINGLE_ERRORS, - { 0, }, - }, - - { - "unknown-session", - "the 'sid' attribute specifies a session that is unknown to the " - "recipient", - "cancel", - XMPP_ERROR_BAD_REQUEST, - NS_JINGLE_ERRORS, - { 0, }, - }, - - { - "unsupported-transports", - "the recipient does not support any of the desired content transport " - "methods", - "cancel", - XMPP_ERROR_FEATURE_NOT_IMPLEMENTED, - NS_JINGLE_ERRORS, - { 0, }, - }, - - { - "unsupported-content", - "the recipient does not support any of the desired content description" - "formats", - "cancel", - XMPP_ERROR_FEATURE_NOT_IMPLEMENTED, - NS_JINGLE_ERRORS, - { 0, }, - }, -}; - -GQuark -gabble_xmpp_error_quark (void) -{ - static GQuark quark = 0; - if (!quark) - quark = g_quark_from_static_string ("gabble-xmpp-error"); - return quark; -} - -GabbleXmppError -gabble_xmpp_error_from_node (LmMessageNode *error_node) -{ - gint i, j; - const gchar *error_code_str; - - /* First, try to look it up the modern way */ - if (error_node->children) - { - for (i = 0; i < NUM_XMPP_ERRORS; i++) - { - if (lm_message_node_get_child (error_node, xmpp_errors[i].name)) - { - return i; - } - } - } - - /* Ok, do it the legacy way */ - error_code_str = lm_message_node_get_attribute (error_node, "code"); - if (error_code_str) - { - gint error_code; - - error_code = atoi (error_code_str); - - for (i = 0; i < NUM_XMPP_ERRORS; i++) - { - const XmppErrorSpec *spec = &xmpp_errors[i]; - - for (j = 0; j < MAX_LEGACY_ERRORS; j++) - { - gint cur_code = spec->legacy_errors[j]; - if (cur_code == 0) - break; - - if (cur_code == error_code) - return i; - } - } - } - - return INVALID_XMPP_ERROR; -} - -GError * -gabble_xmpp_error_to_g_error (GabbleXmppError error) -{ - if (error >= NUM_XMPP_ERRORS) - return NULL; - - return g_error_new (GABBLE_XMPP_ERROR, - error, - xmpp_errors[error].description); -} - -/* - * See RFC 3920: 4.7 Stream Errors, 9.3 Stanza Errors. - */ -LmMessageNode * -gabble_xmpp_error_to_node (GabbleXmppError error, - LmMessageNode *parent_node, - const gchar *errmsg) -{ - const XmppErrorSpec *spec, *extra; - LmMessageNode *error_node, *node; - gchar str[6]; - - if (error >= NUM_XMPP_ERRORS) - return NULL; - - if (xmpp_errors[error].specialises) - { - extra = &xmpp_errors[error]; - spec = &xmpp_errors[extra->specialises]; - } - else - { - extra = NULL; - spec = &xmpp_errors[error]; - } - - error_node = lm_message_node_add_child (parent_node, "error", NULL); - - sprintf (str, "%d", spec->legacy_errors[0]); - lm_message_node_set_attribute (error_node, "code", str); - - if (spec->type) - { - lm_message_node_set_attribute (error_node, "type", spec->type); - } - - node = lm_message_node_add_child (error_node, spec->name, NULL); - lm_message_node_set_attribute (node, "xmlns", NS_XMPP_STANZAS); - - if (extra != NULL) - { - node = lm_message_node_add_child (error_node, extra->name, NULL); - lm_message_node_set_attribute (node, "xmlns", extra->namespace); - } - - if (NULL != errmsg) - lm_message_node_add_child (error_node, "text", errmsg); - - return error_node; -} - -const gchar * -gabble_xmpp_error_string (GabbleXmppError error) -{ - if (error < NUM_XMPP_ERRORS) - return xmpp_errors[error].name; - else - return NULL; -} - -const gchar * -gabble_xmpp_error_description (GabbleXmppError error) -{ - if (error < NUM_XMPP_ERRORS) - return xmpp_errors[error].description; - else - return NULL; -} - diff --git a/src/gabble-error.h b/src/gabble-error.h deleted file mode 100644 index 970961a2c..000000000 --- a/src/gabble-error.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * gabble-error.h - Header for Gabble's error handling API - * Copyright (C) 2006 Collabora Ltd. - * Copyright (C) 2006 Nokia Corporation - * @author Ole Andre Vadla Ravnaas <ole.andre.ravnaas@collabora.co.uk> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef __GABBLE_ERROR_H__ -#define __GABBLE_ERROR_H__ - -#include <glib.h> -#include <loudmouth/loudmouth.h> - -typedef enum { - XMPP_ERROR_REDIRECT = 0, /* 302 */ - XMPP_ERROR_GONE, /* 302 */ - - XMPP_ERROR_BAD_REQUEST, /* 400 */ - XMPP_ERROR_UNEXPECTED_REQUEST, /* 400 */ - XMPP_ERROR_JID_MALFORMED, /* 400 */ - - XMPP_ERROR_NOT_AUTHORIZED, /* 401 */ - - XMPP_ERROR_PAYMENT_REQUIRED, /* 402 */ - - XMPP_ERROR_FORBIDDEN, /* 403 */ - - XMPP_ERROR_ITEM_NOT_FOUND, /* 404 */ - XMPP_ERROR_RECIPIENT_UNAVAILABLE, /* 404 */ - XMPP_ERROR_REMOTE_SERVER_NOT_FOUND, /* 404 */ - - XMPP_ERROR_NOT_ALLOWED, /* 405 */ - - XMPP_ERROR_NOT_ACCEPTABLE, /* 406 */ - - XMPP_ERROR_REGISTRATION_REQUIRED, /* 407 */ - XMPP_ERROR_SUBSCRIPTION_REQUIRED, /* 407 */ - - XMPP_ERROR_REMOTE_SERVER_TIMEOUT, /* 408, 504 */ - - XMPP_ERROR_CONFLICT, /* 409 */ - - XMPP_ERROR_INTERNAL_SERVER_ERROR, /* 500 */ - XMPP_ERROR_UNDEFINED_CONDITION, /* 500 */ - XMPP_ERROR_RESOURCE_CONSTRAINT, /* 500 */ - - XMPP_ERROR_FEATURE_NOT_IMPLEMENTED, /* 501 */ - - XMPP_ERROR_SERVICE_UNAVAILABLE, /* 502, 503, 510 */ - - XMPP_ERROR_JINGLE_OUT_OF_ORDER, - XMPP_ERROR_JINGLE_UNKOWN_SESSION, - XMPP_ERROR_JINGLE_UNSUPPORTED_CONTENT, - XMPP_ERROR_JINGLE_UNSUPPORTED_TRANSPORT, - - NUM_XMPP_ERRORS, - - INVALID_XMPP_ERROR, -} GabbleXmppError; - -GQuark gabble_xmpp_error_quark (void); -#define GABBLE_XMPP_ERROR gabble_xmpp_error_quark () - -GabbleXmppError gabble_xmpp_error_from_node (LmMessageNode *error_node); -GError *gabble_xmpp_error_to_g_error (GabbleXmppError error); -LmMessageNode *gabble_xmpp_error_to_node (GabbleXmppError error, - LmMessageNode *parent_node, const gchar *errmsg); -const gchar *gabble_xmpp_error_string (GabbleXmppError error); -const gchar *gabble_xmpp_error_description (GabbleXmppError error); - -#endif /* __GABBLE_ERROR_H__ */ diff --git a/src/gabble-im-channel.c b/src/gabble-im-channel.c deleted file mode 100644 index 961435639..000000000 --- a/src/gabble-im-channel.c +++ /dev/null @@ -1,606 +0,0 @@ -/* - * gabble-im-channel.c - Source for GabbleIMChannel - * Copyright (C) 2005 Collabora Ltd. - * Copyright (C) 2005 Nokia Corporation - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "gabble-im-channel.h" - -#include <dbus/dbus-glib.h> -#include <loudmouth/loudmouth.h> -#include <stdio.h> -#include <string.h> -#include <time.h> - -#define DEBUG_FLAG GABBLE_DEBUG_IM - -#include "debug.h" -#include "disco.h" -#include "gabble-connection.h" -#include "presence.h" -#include "presence-cache.h" -#include "roster.h" -#include <telepathy-glib/dbus.h> -#include <telepathy-glib/enums.h> -#include <telepathy-glib/errors.h> -#include <telepathy-glib/interfaces.h> -#include <telepathy-glib/channel-iface.h> -#include <telepathy-glib/svc-channel.h> - -static void channel_iface_init (gpointer, gpointer); -static void text_iface_init (gpointer, gpointer); -static void chat_state_iface_init (gpointer, gpointer); - -G_DEFINE_TYPE_WITH_CODE (GabbleIMChannel, gabble_im_channel, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL, channel_iface_init); - G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_TYPE_TEXT, text_iface_init); - G_IMPLEMENT_INTERFACE (TP_TYPE_CHANNEL_IFACE, NULL); - G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_CHAT_STATE, - chat_state_iface_init)); - - -/* properties */ -enum -{ - PROP_OBJECT_PATH = 1, - PROP_CHANNEL_TYPE, - PROP_HANDLE_TYPE, - PROP_HANDLE, - PROP_CONNECTION, - LAST_PROPERTY -}; - -/* private structure */ -typedef struct _GabbleIMChannelPrivate GabbleIMChannelPrivate; - -struct _GabbleIMChannelPrivate -{ - GabbleConnection *conn; - char *object_path; - TpHandle handle; - - gchar *peer_jid; - - gboolean closed; - gboolean dispose_has_run; -}; - -#define GABBLE_IM_CHANNEL_GET_PRIVATE(obj) \ - ((GabbleIMChannelPrivate *)obj->priv) - -static void -gabble_im_channel_init (GabbleIMChannel *self) -{ - GabbleIMChannelPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self, - GABBLE_TYPE_IM_CHANNEL, GabbleIMChannelPrivate); - - self->priv = priv; -} - -static GObject * -gabble_im_channel_constructor (GType type, guint n_props, - GObjectConstructParam *props) -{ - GObject *obj; - GabbleIMChannelPrivate *priv; - TpBaseConnection *conn; - DBusGConnection *bus; - gboolean send_nick; - TpHandleRepoIface *contact_handles; - - obj = G_OBJECT_CLASS (gabble_im_channel_parent_class)-> - constructor (type, n_props, props); - priv = GABBLE_IM_CHANNEL_GET_PRIVATE (GABBLE_IM_CHANNEL (obj)); - conn = (TpBaseConnection *)priv->conn; - contact_handles = tp_base_connection_get_handles (conn, - TP_HANDLE_TYPE_CONTACT); - - tp_handle_ref (contact_handles, priv->handle); - - priv->peer_jid = g_strdup (tp_handle_inspect (contact_handles, - priv->handle)); - - bus = tp_get_bus (); - dbus_g_connection_register_g_object (bus, priv->object_path, obj); - - if (gabble_roster_handle_get_subscription (priv->conn->roster, priv->handle) - & GABBLE_ROSTER_SUBSCRIPTION_FROM) - send_nick = FALSE; - else - send_nick = TRUE; - - gabble_text_mixin_init (obj, G_STRUCT_OFFSET (GabbleIMChannel, text), - contact_handles, send_nick); - - tp_text_mixin_set_message_types (obj, - TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL, - TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION, - TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE, - G_MAXUINT); - - return obj; -} - -static void -gabble_im_channel_get_property (GObject *object, - guint property_id, - GValue *value, - GParamSpec *pspec) -{ - GabbleIMChannel *chan = GABBLE_IM_CHANNEL (object); - GabbleIMChannelPrivate *priv = GABBLE_IM_CHANNEL_GET_PRIVATE (chan); - - switch (property_id) { - case PROP_OBJECT_PATH: - g_value_set_string (value, priv->object_path); - break; - case PROP_CHANNEL_TYPE: - g_value_set_static_string (value, TP_IFACE_CHANNEL_TYPE_TEXT); - break; - case PROP_HANDLE_TYPE: - g_value_set_uint (value, TP_HANDLE_TYPE_CONTACT); - break; - case PROP_HANDLE: - g_value_set_uint (value, priv->handle); - break; - case PROP_CONNECTION: - g_value_set_object (value, priv->conn); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } -} - -static void -gabble_im_channel_set_property (GObject *object, - guint property_id, - const GValue *value, - GParamSpec *pspec) -{ - GabbleIMChannel *chan = GABBLE_IM_CHANNEL (object); - GabbleIMChannelPrivate *priv = GABBLE_IM_CHANNEL_GET_PRIVATE (chan); - - switch (property_id) { - case PROP_OBJECT_PATH: - g_free (priv->object_path); - priv->object_path = g_value_dup_string (value); - break; - case PROP_HANDLE: - /* we don't ref it here because we don't necessarily have access to the - * contact repo yet - instead we ref it in the constructor. - */ - priv->handle = g_value_get_uint (value); - break; - case PROP_HANDLE_TYPE: - /* this property is writable in the interface, but not actually - * meaningfully changable on this channel, so we do nothing */ - break; - case PROP_CONNECTION: - priv->conn = g_value_get_object (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } -} - -static void gabble_im_channel_dispose (GObject *object); -static void gabble_im_channel_finalize (GObject *object); - -static void -gabble_im_channel_class_init (GabbleIMChannelClass *gabble_im_channel_class) -{ - GObjectClass *object_class = G_OBJECT_CLASS (gabble_im_channel_class); - GParamSpec *param_spec; - - g_type_class_add_private (gabble_im_channel_class, - sizeof (GabbleIMChannelPrivate)); - - object_class->constructor = gabble_im_channel_constructor; - - object_class->get_property = gabble_im_channel_get_property; - object_class->set_property = gabble_im_channel_set_property; - - object_class->dispose = gabble_im_channel_dispose; - object_class->finalize = gabble_im_channel_finalize; - - g_object_class_override_property (object_class, PROP_OBJECT_PATH, - "object-path"); - g_object_class_override_property (object_class, PROP_CHANNEL_TYPE, - "channel-type"); - g_object_class_override_property (object_class, PROP_HANDLE_TYPE, - "handle-type"); - g_object_class_override_property (object_class, PROP_HANDLE, "handle"); - - param_spec = g_param_spec_object ("connection", "GabbleConnection object", - "Gabble connection object that owns this " - "IM channel object.", - GABBLE_TYPE_CONNECTION, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | - G_PARAM_STATIC_NICK | - G_PARAM_STATIC_BLURB); - g_object_class_install_property (object_class, PROP_CONNECTION, param_spec); - - tp_text_mixin_class_init (object_class, G_STRUCT_OFFSET (GabbleIMChannelClass, text_class)); -} - -static void -gabble_im_channel_dispose (GObject *object) -{ - GabbleIMChannel *self = GABBLE_IM_CHANNEL (object); - GabbleIMChannelPrivate *priv = GABBLE_IM_CHANNEL_GET_PRIVATE (self); - GabblePresence *presence; - GabbleRosterSubscription subscription; - gboolean cap_chat_states = FALSE; - - if (priv->dispose_has_run) - return; - - priv->dispose_has_run = TRUE; - - subscription = gabble_roster_handle_get_subscription (priv->conn->roster, - priv->handle); - - presence = gabble_presence_cache_get (priv->conn->presence_cache, - priv->handle); - - if (presence && presence->caps & PRESENCE_CAP_CHAT_STATES) - { - cap_chat_states = TRUE; - } - - if ((GABBLE_ROSTER_SUBSCRIPTION_TO & subscription) == 0) - { - if (NULL != presence) - { - presence->keep_unavailable = FALSE; - gabble_presence_cache_maybe_remove (priv->conn->presence_cache, - priv->handle); - } - } - - if (!priv->closed) - { - if (cap_chat_states) - { - /* Set the chat state of the channel on gone - * (Channel.Interface.ChatState) */ - gabble_text_mixin_send (G_OBJECT (self), - TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE, 0, - TP_CHANNEL_CHAT_STATE_GONE, priv->peer_jid, NULL, priv->conn, - FALSE /* emit_signal */, NULL); - } - - tp_svc_channel_emit_closed (self); - } - - if (G_OBJECT_CLASS (gabble_im_channel_parent_class)->dispose) - G_OBJECT_CLASS (gabble_im_channel_parent_class)->dispose (object); -} - -static void -gabble_im_channel_finalize (GObject *object) -{ - GabbleIMChannel *self = GABBLE_IM_CHANNEL (object); - GabbleIMChannelPrivate *priv = GABBLE_IM_CHANNEL_GET_PRIVATE (self); - TpBaseConnection *conn = (TpBaseConnection *)priv->conn; - TpHandleRepoIface *contact_handles = tp_base_connection_get_handles (conn, - TP_HANDLE_TYPE_CONTACT); - - /* free any data held directly by the object here */ - - DEBUG ("%p", object); - - tp_handle_unref (contact_handles, priv->handle); - - g_free (priv->object_path); - g_free (priv->peer_jid); - - tp_text_mixin_finalize (object); - - G_OBJECT_CLASS (gabble_im_channel_parent_class)->finalize (object); -} - -/** - * _gabble_im_channel_receive - * - */ -void -_gabble_im_channel_receive (GabbleIMChannel *chan, - TpChannelTextMessageType type, - TpHandle sender, - const char *from, - time_t timestamp, - const char *text) -{ - GabbleIMChannelPrivate *priv; - - g_assert (GABBLE_IS_IM_CHANNEL (chan)); - priv = GABBLE_IM_CHANNEL_GET_PRIVATE (chan); - - /* update peer's full JID if it's changed */ - if (0 != strcmp (from, priv->peer_jid)) - { - g_free (priv->peer_jid); - priv->peer_jid = g_strdup (from); - } - - if (timestamp == 0) - timestamp = time (NULL); - - tp_text_mixin_receive (G_OBJECT (chan), type, sender, timestamp, text); -} - -/** - * _gabble_im_channel_state_receive - * - * Send the D-BUS signal ChatStateChanged - * on org.freedesktop.Telepathy.Channel.Interface.ChatState - */ - -void -_gabble_im_channel_state_receive (GabbleIMChannel *chan, - guint state) -{ - GabbleIMChannelPrivate *priv; - - g_assert (state < NUM_TP_CHANNEL_CHAT_STATES); - g_assert (GABBLE_IS_IM_CHANNEL (chan)); - priv = GABBLE_IM_CHANNEL_GET_PRIVATE (chan); - - tp_svc_channel_interface_chat_state_emit_chat_state_changed ( - (TpSvcChannelInterfaceChatState*)chan, - priv->handle, state); -} - -/** - * gabble_im_channel_close - * - * Implements D-Bus method Close - * on interface org.freedesktop.Telepathy.Channel - */ -static void -gabble_im_channel_close (TpSvcChannel *iface, - DBusGMethodInvocation *context) -{ - GabbleIMChannel *self = GABBLE_IM_CHANNEL (iface); - GabbleIMChannelPrivate *priv; - GabblePresence *presence; - - g_assert (GABBLE_IS_IM_CHANNEL (self)); - - DEBUG ("called on %p", self); - - priv = GABBLE_IM_CHANNEL_GET_PRIVATE (self); - - presence = gabble_presence_cache_get (priv->conn->presence_cache, - priv->handle); - - if (!priv->closed) - { - tp_svc_channel_emit_closed (self); - - if (presence && (presence->caps & PRESENCE_CAP_CHAT_STATES)) - { - /* Set the chat state of the channel on gone - * (Channel.Interface.ChatState) */ - gabble_text_mixin_send (G_OBJECT (self), - TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE, 0, - TP_CHANNEL_CHAT_STATE_GONE, priv->peer_jid, NULL, priv->conn, - FALSE /* emit_signal */, NULL); - } - - priv->closed = TRUE; - } - - tp_svc_channel_return_from_close (context); -} - - -/** - * gabble_im_channel_get_channel_type - * - * Implements D-Bus method GetChannelType - * on interface org.freedesktop.Telepathy.Channel - */ -static void -gabble_im_channel_get_channel_type (TpSvcChannel *iface, - DBusGMethodInvocation *context) -{ - tp_svc_channel_return_from_get_channel_type (context, - TP_IFACE_CHANNEL_TYPE_TEXT); -} - - -/** - * gabble_im_channel_get_handle - * - * Implements D-Bus method GetHandle - * on interface org.freedesktop.Telepathy.Channel - */ -static void -gabble_im_channel_get_handle (TpSvcChannel *iface, - DBusGMethodInvocation *context) -{ - GabbleIMChannel *self = GABBLE_IM_CHANNEL (iface); - GabbleIMChannelPrivate *priv; - - g_assert (GABBLE_IS_IM_CHANNEL (self)); - priv = GABBLE_IM_CHANNEL_GET_PRIVATE (self); - - tp_svc_channel_return_from_get_handle (context, TP_HANDLE_TYPE_CONTACT, - priv->handle); -} - - -/** - * gabble_im_channel_get_interfaces - * - * Implements D-Bus method GetInterfaces - * on interface org.freedesktop.Telepathy.Channel - */ -static void -gabble_im_channel_get_interfaces (TpSvcChannel *iface, - DBusGMethodInvocation *context) -{ - const char *interfaces[] = { - TP_IFACE_CHANNEL_INTERFACE_CHAT_STATE, - NULL }; - - tp_svc_channel_return_from_get_interfaces (context, interfaces); -} - - -/** - * gabble_im_channel_send - * - * Implements D-Bus method Send - * on interface org.freedesktop.Telepathy.Channel.Type.Text - */ -static void -gabble_im_channel_send (TpSvcChannelTypeText *iface, - guint type, - const gchar *text, - DBusGMethodInvocation *context) -{ - GabbleIMChannel *self = GABBLE_IM_CHANNEL (iface); - GabbleIMChannelPrivate *priv; - GabblePresence *presence; - gint state = -1; - GError *error = NULL; - - g_assert (GABBLE_IS_IM_CHANNEL (self)); - priv = GABBLE_IM_CHANNEL_GET_PRIVATE (self); - - presence = gabble_presence_cache_get (priv->conn->presence_cache, - priv->handle); - - if (presence && (presence->caps & PRESENCE_CAP_CHAT_STATES)) - { - state = TP_CHANNEL_CHAT_STATE_ACTIVE; - } - - if (!gabble_text_mixin_send (G_OBJECT (self), type, 0, - state, priv->peer_jid, text, priv->conn, - TRUE /* emit_signal */, &error)) - { - dbus_g_method_return_error (context, error); - g_error_free (error); - - return; - } - - tp_svc_channel_type_text_return_from_send (context); -} - -/** - * gabble_im_channel_set_chat_state - * - * Implements D-Bus method SetChatState - * on interface org.freedesktop.Telepathy.Channel.Interface.ChatState - */ -static void -gabble_im_channel_set_chat_state (TpSvcChannelInterfaceChatState *iface, - guint state, - DBusGMethodInvocation *context) -{ - GabbleIMChannel *self = GABBLE_IM_CHANNEL (iface); - GabbleIMChannelPrivate *priv; - GabblePresence *presence; - GError *error = NULL; - - g_assert (GABBLE_IS_IM_CHANNEL (self)); - priv = GABBLE_IM_CHANNEL_GET_PRIVATE (self); - - presence = gabble_presence_cache_get (priv->conn->presence_cache, - priv->handle); - - if (presence && (presence->caps & PRESENCE_CAP_CHAT_STATES)) - { - if (state >= NUM_TP_CHANNEL_CHAT_STATES) - { - DEBUG ("invalid state %u", state); - - g_set_error (&error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, - "invalid state: %u", state); - } - - if (state == TP_CHANNEL_CHAT_STATE_GONE) - { - /* We cannot explicitly set the Gone state */ - DEBUG ("you may not explicitly set the Gone state"); - - g_set_error (&error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, - "you may not explicitly set the Gone state"); - } - - if (error != NULL || !gabble_text_mixin_send (G_OBJECT (self), - TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE, 0, state, priv->peer_jid, NULL, - priv->conn, FALSE /* emit_signal */, &error)) - { - dbus_g_method_return_error (context, error); - g_error_free (error); - - return; - } - - /* Send the ChatStateChanged signal for the local user */ - tp_svc_channel_interface_chat_state_emit_chat_state_changed (iface, - priv->conn->parent.self_handle, state); - } - - tp_svc_channel_interface_chat_state_return_from_set_chat_state (context); -} - -static void -channel_iface_init (gpointer g_iface, gpointer iface_data) -{ - TpSvcChannelClass *klass = (TpSvcChannelClass *)g_iface; - -#define IMPLEMENT(x) tp_svc_channel_implement_##x (\ - klass, gabble_im_channel_##x) - IMPLEMENT(close); - IMPLEMENT(get_channel_type); - IMPLEMENT(get_handle); - IMPLEMENT(get_interfaces); -#undef IMPLEMENT -} - -static void -text_iface_init (gpointer g_iface, gpointer iface_data) -{ - TpSvcChannelTypeTextClass *klass = (TpSvcChannelTypeTextClass *)g_iface; - - tp_text_mixin_iface_init (g_iface, iface_data); -#define IMPLEMENT(x) tp_svc_channel_type_text_implement_##x (\ - klass, gabble_im_channel_##x) - IMPLEMENT(send); -#undef IMPLEMENT -} - -static void -chat_state_iface_init (gpointer g_iface, gpointer iface_data) -{ - TpSvcChannelInterfaceChatStateClass *klass = - (TpSvcChannelInterfaceChatStateClass *)g_iface; -#define IMPLEMENT(x) tp_svc_channel_interface_chat_state_implement_##x (\ - klass, gabble_im_channel_##x) - IMPLEMENT(set_chat_state); -#undef IMPLEMENT -} diff --git a/src/gabble-im-channel.h b/src/gabble-im-channel.h deleted file mode 100644 index 7c5e2244c..000000000 --- a/src/gabble-im-channel.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * gabble-im-channel.h - Header for GabbleIMChannel - * Copyright (C) 2005 Collabora Ltd. - * Copyright (C) 2005 Nokia Corporation - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef __GABBLE_IM_CHANNEL_H__ -#define __GABBLE_IM_CHANNEL_H__ - -#include <glib-object.h> -#include <time.h> - -#include <telepathy-glib/enums.h> -#include "text-mixin.h" - -G_BEGIN_DECLS - -typedef struct _GabbleIMChannel GabbleIMChannel; -typedef struct _GabbleIMChannelClass GabbleIMChannelClass; - -struct _GabbleIMChannelClass { - GObjectClass parent_class; - - GabbleTextMixinClass text_class; -}; - -struct _GabbleIMChannel { - GObject parent; - - GabbleTextMixin text; - - gpointer priv; -}; - -GType gabble_im_channel_get_type (void); - -/* TYPE MACROS */ -#define GABBLE_TYPE_IM_CHANNEL \ - (gabble_im_channel_get_type ()) -#define GABBLE_IM_CHANNEL(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_IM_CHANNEL, GabbleIMChannel)) -#define GABBLE_IM_CHANNEL_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_IM_CHANNEL,\ - GabbleIMChannelClass)) -#define GABBLE_IS_IM_CHANNEL(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_IM_CHANNEL)) -#define GABBLE_IS_IM_CHANNEL_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_IM_CHANNEL)) -#define GABBLE_IM_CHANNEL_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_IM_CHANNEL, \ - GabbleIMChannelClass)) - -void _gabble_im_channel_receive (GabbleIMChannel *chan, - TpChannelTextMessageType type, TpHandle sender, const char *from, - time_t timestamp, const char *text); -void _gabble_im_channel_state_receive (GabbleIMChannel *chan, guint state); - -G_END_DECLS - -#endif /* #ifndef __GABBLE_IM_CHANNEL_H__*/ diff --git a/src/gabble-media-channel.c b/src/gabble-media-channel.c deleted file mode 100644 index c852289cd..000000000 --- a/src/gabble-media-channel.c +++ /dev/null @@ -1,1466 +0,0 @@ -/* - * gabble-media-channel.c - Source for GabbleMediaChannel - * Copyright (C) 2006 Collabora Ltd. - * Copyright (C) 2006 Nokia Corporation - * @author Ole Andre Vadla Ravnaas <ole.andre.ravnaas@collabora.co.uk> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "gabble-media-channel.h" - -#include <dbus/dbus-glib.h> -#include <stdio.h> -#include <stdlib.h> - -#define DEBUG_FLAG GABBLE_DEBUG_MEDIA - -#include "debug.h" -#include "gabble-connection.h" -#include "gabble-media-session.h" -#include "presence.h" -#include "presence-cache.h" - -#include <telepathy-glib/dbus.h> -#include <telepathy-glib/errors.h> -#include <telepathy-glib/interfaces.h> -#include <telepathy-glib/channel-iface.h> -#include <telepathy-glib/svc-channel.h> -#include <telepathy-glib/svc-properties-interface.h> - -#include "gabble-media-session.h" -#include "gabble-media-stream.h" - -#include "media-factory.h" - -#define TP_SESSION_HANDLER_SET_TYPE (dbus_g_type_get_struct ("GValueArray", \ - DBUS_TYPE_G_OBJECT_PATH, \ - G_TYPE_STRING, \ - G_TYPE_INVALID)) - -#define TP_CHANNEL_STREAM_TYPE (dbus_g_type_get_struct ("GValueArray", \ - G_TYPE_UINT, \ - G_TYPE_UINT, \ - G_TYPE_UINT, \ - G_TYPE_UINT, \ - G_TYPE_UINT, \ - G_TYPE_UINT, \ - G_TYPE_INVALID)) - -static void channel_iface_init (gpointer, gpointer); -static void media_signalling_iface_init (gpointer, gpointer); -static void streamed_media_iface_init (gpointer, gpointer); - -G_DEFINE_TYPE_WITH_CODE (GabbleMediaChannel, gabble_media_channel, - G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL, channel_iface_init); - G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_GROUP, - tp_group_mixin_iface_init); - G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_MEDIA_SIGNALLING, - media_signalling_iface_init); - G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_TYPE_STREAMED_MEDIA, - streamed_media_iface_init); - G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_PROPERTIES_INTERFACE, - tp_properties_mixin_iface_init); - G_IMPLEMENT_INTERFACE (TP_TYPE_CHANNEL_IFACE, NULL)); - -/* properties */ -enum -{ - PROP_OBJECT_PATH = 1, - PROP_CHANNEL_TYPE, - PROP_HANDLE_TYPE, - PROP_HANDLE, - PROP_CONNECTION, - PROP_CREATOR, - PROP_FACTORY, - /* TP properties (see also below) */ - PROP_NAT_TRAVERSAL, - PROP_STUN_SERVER, - PROP_STUN_PORT, - PROP_GTALK_P2P_RELAY_TOKEN, - LAST_PROPERTY -}; - -/* TP properties */ -enum -{ - CHAN_PROP_NAT_TRAVERSAL = 0, - CHAN_PROP_STUN_SERVER, - CHAN_PROP_STUN_PORT, - CHAN_PROP_GTALK_P2P_RELAY_TOKEN, - NUM_CHAN_PROPS, - INVALID_CHAN_PROP -}; - -const TpPropertySignature channel_property_signatures[NUM_CHAN_PROPS] = { - { "nat-traversal", G_TYPE_STRING }, - { "stun-server", G_TYPE_STRING }, - { "stun-port", G_TYPE_UINT }, - { "gtalk-p2p-relay-token", G_TYPE_STRING } -}; - -/* private structure */ -typedef struct _GabbleMediaChannelPrivate GabbleMediaChannelPrivate; - -struct _GabbleMediaChannelPrivate -{ - GabbleConnection *conn; - gchar *object_path; - TpHandle creator; - - GabbleMediaFactory *factory; - GabbleMediaSession *session; - GPtrArray *streams; - - guint next_stream_id; - - gboolean closed; - gboolean dispose_has_run; -}; - -#define GABBLE_MEDIA_CHANNEL_GET_PRIVATE(obj) \ - ((GabbleMediaChannelPrivate *)obj->priv) - -static void -gabble_media_channel_init (GabbleMediaChannel *self) -{ - GabbleMediaChannelPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self, - GABBLE_TYPE_MEDIA_CHANNEL, GabbleMediaChannelPrivate); - - self->priv = priv; - - priv->next_stream_id = 1; - - /* initialize properties mixin */ - tp_properties_mixin_init (G_OBJECT (self), G_STRUCT_OFFSET ( - GabbleMediaChannel, properties)); -} - -static GObject * -gabble_media_channel_constructor (GType type, guint n_props, - GObjectConstructParam *props) -{ - GObject *obj; - GabbleMediaChannelPrivate *priv; - TpBaseConnection *conn; - DBusGConnection *bus; - TpIntSet *set; - TpHandleRepoIface *contact_handles; - - obj = G_OBJECT_CLASS (gabble_media_channel_parent_class)-> - constructor (type, n_props, props); - - priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (GABBLE_MEDIA_CHANNEL (obj)); - conn = (TpBaseConnection *)priv->conn; - contact_handles = tp_base_connection_get_handles (conn, - TP_HANDLE_TYPE_CONTACT); - - /* register object on the bus */ - bus = tp_get_bus (); - dbus_g_connection_register_g_object (bus, priv->object_path, obj); - - tp_group_mixin_init (obj, G_STRUCT_OFFSET (GabbleMediaChannel, group), - contact_handles, conn->self_handle); - - /* automatically add creator to channel */ - set = tp_intset_new (); - tp_intset_add (set, priv->creator); - - tp_group_mixin_change_members (obj, "", set, NULL, NULL, NULL, 0, 0); - - tp_intset_destroy (set); - - /* allow member adding */ - tp_group_mixin_change_flags (obj, TP_CHANNEL_GROUP_FLAG_CAN_ADD, 0); - - return obj; -} - -static void session_state_changed_cb (GabbleMediaSession *session, - GParamSpec *arg1, GabbleMediaChannel *channel); -static void session_stream_added_cb (GabbleMediaSession *session, - GabbleMediaStream *stream, GabbleMediaChannel *chan); -static void session_terminated_cb (GabbleMediaSession *session, - guint terminator, guint reason, gpointer user_data); - -/** - * create_session - * - * Creates a GabbleMediaSession object for given peer. - * - * If sid is set to NULL a unique sid is generated and - * the "initiator" property of the newly created - * GabbleMediaSession is set to our own handle. - */ -static GabbleMediaSession* -create_session (GabbleMediaChannel *channel, - TpHandle peer, - const gchar *peer_resource, - const gchar *sid) -{ - GabbleMediaChannelPrivate *priv; - GabbleMediaSession *session; - gchar *object_path; - JingleInitiator initiator; - - g_assert (GABBLE_IS_MEDIA_CHANNEL (channel)); - - priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (channel); - - g_assert (priv->session == NULL); - - object_path = g_strdup_printf ("%s/MediaSession%u", priv->object_path, peer); - - if (sid == NULL) - { - initiator = INITIATOR_LOCAL; - sid = _gabble_media_factory_allocate_sid (priv->factory, channel); - } - else - { - initiator = INITIATOR_REMOTE; - _gabble_media_factory_register_sid (priv->factory, sid, channel); - } - - session = g_object_new (GABBLE_TYPE_MEDIA_SESSION, - "connection", priv->conn, - "media-channel", channel, - "object-path", object_path, - "session-id", sid, - "initiator", initiator, - "peer", peer, - "peer-resource", peer_resource, - NULL); - - g_signal_connect (session, "notify::state", - (GCallback) session_state_changed_cb, channel); - g_signal_connect (session, "stream-added", - (GCallback) session_stream_added_cb, channel); - g_signal_connect (session, "terminated", - (GCallback) session_terminated_cb, channel); - - priv->session = session; - - priv->streams = g_ptr_array_sized_new (1); - - tp_svc_channel_interface_media_signalling_emit_new_session_handler ( - channel, object_path, "rtp"); - - g_free (object_path); - - return session; -} - -gboolean -_gabble_media_channel_dispatch_session_action (GabbleMediaChannel *chan, - TpHandle peer, - const gchar *peer_resource, - const gchar *sid, - LmMessage *message, - LmMessageNode *session_node, - const gchar *action, - GError **error) -{ - GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (chan); - GabbleMediaSession *session = priv->session; - gboolean session_is_new = FALSE; - - if (session == NULL) - { - TpGroupMixin *mixin = TP_GROUP_MIXIN (chan); - TpIntSet *set; - - session = create_session (chan, peer, peer_resource, sid); - session_is_new = TRUE; - - /* make us local pending */ - set = tp_intset_new (); - tp_intset_add (set, mixin->self_handle); - - tp_group_mixin_change_members ((GObject *)chan, - "", NULL, NULL, set, NULL, 0, 0); - - tp_intset_destroy (set); - - /* and update flags accordingly */ - tp_group_mixin_change_flags ((GObject *)chan, - TP_CHANNEL_GROUP_FLAG_CAN_ADD | TP_CHANNEL_GROUP_FLAG_CAN_REMOVE, - 0); - } - - g_object_ref (session); - - if (_gabble_media_session_handle_action (session, message, session_node, - action, error)) - { - g_object_unref (session); - return TRUE; - } - else - { - if (session_is_new) - _gabble_media_session_terminate (session, INITIATOR_LOCAL, - TP_CHANNEL_GROUP_CHANGE_REASON_ERROR); - - g_object_unref (session); - return FALSE; - } -} - -static void -gabble_media_channel_get_property (GObject *object, - guint property_id, - GValue *value, - GParamSpec *pspec) -{ - GabbleMediaChannel *chan = GABBLE_MEDIA_CHANNEL (object); - GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (chan); - const gchar *param_name; - guint tp_property_id; - - switch (property_id) { - case PROP_OBJECT_PATH: - g_value_set_string (value, priv->object_path); - break; - case PROP_CHANNEL_TYPE: - g_value_set_static_string (value, TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA); - break; - case PROP_HANDLE_TYPE: - g_value_set_uint (value, TP_HANDLE_TYPE_NONE); - break; - case PROP_HANDLE: - g_value_set_uint (value, 0); - break; - case PROP_CONNECTION: - g_value_set_object (value, priv->conn); - break; - case PROP_CREATOR: - g_value_set_uint (value, priv->creator); - break; - case PROP_FACTORY: - g_value_set_object (value, priv->factory); - break; - default: - param_name = g_param_spec_get_name (pspec); - - if (tp_properties_mixin_has_property (object, param_name, - &tp_property_id)) - { - GValue *tp_property_value = - chan->properties.properties[tp_property_id].value; - - if (tp_property_value) - { - g_value_copy (tp_property_value, value); - return; - } - } - - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } -} - -static void -gabble_media_channel_set_property (GObject *object, - guint property_id, - const GValue *value, - GParamSpec *pspec) -{ - GabbleMediaChannel *chan = GABBLE_MEDIA_CHANNEL (object); - GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (chan); - const gchar *param_name; - guint tp_property_id; - - switch (property_id) { - case PROP_OBJECT_PATH: - g_free (priv->object_path); - priv->object_path = g_value_dup_string (value); - break; - case PROP_HANDLE: - /* this property is writable in the interface, but not actually - * meaningfully changable on this channel, so we do nothing */ - break; - case PROP_HANDLE_TYPE: - /* this property is writable in the interface, but not actually - * meaningfully changable on this channel, so we do nothing */ - break; - case PROP_CONNECTION: - priv->conn = g_value_get_object (value); - break; - case PROP_CREATOR: - priv->creator = g_value_get_uint (value); - break; - case PROP_FACTORY: - priv->factory = g_value_get_object (value); - break; - default: - param_name = g_param_spec_get_name (pspec); - - if (tp_properties_mixin_has_property (object, param_name, - &tp_property_id)) - { - tp_properties_mixin_change_value (object, tp_property_id, value, - NULL); - tp_properties_mixin_change_flags (object, tp_property_id, - TP_PROPERTY_FLAG_READ, - 0, NULL); - - return; - } - - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } -} - -static void gabble_media_channel_dispose (GObject *object); -static void gabble_media_channel_finalize (GObject *object); -static gboolean gabble_media_channel_remove_member (GObject *obj, - TpHandle handle, const gchar *message, GError **error); - -static void -gabble_media_channel_class_init (GabbleMediaChannelClass *gabble_media_channel_class) -{ - GObjectClass *object_class = G_OBJECT_CLASS (gabble_media_channel_class); - GParamSpec *param_spec; - - g_type_class_add_private (gabble_media_channel_class, - sizeof (GabbleMediaChannelPrivate)); - - object_class->constructor = gabble_media_channel_constructor; - - object_class->get_property = gabble_media_channel_get_property; - object_class->set_property = gabble_media_channel_set_property; - - object_class->dispose = gabble_media_channel_dispose; - object_class->finalize = gabble_media_channel_finalize; - - tp_group_mixin_class_init (object_class, - G_STRUCT_OFFSET (GabbleMediaChannelClass, group_class), - _gabble_media_channel_add_member, gabble_media_channel_remove_member); - - g_object_class_override_property (object_class, PROP_OBJECT_PATH, - "object-path"); - g_object_class_override_property (object_class, PROP_CHANNEL_TYPE, - "channel-type"); - g_object_class_override_property (object_class, PROP_HANDLE_TYPE, - "handle-type"); - g_object_class_override_property (object_class, PROP_HANDLE, "handle"); - - param_spec = g_param_spec_object ("connection", "GabbleConnection object", - "Gabble connection object that owns this " - "media channel object.", - GABBLE_TYPE_CONNECTION, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | - G_PARAM_STATIC_NICK | - G_PARAM_STATIC_BLURB); - g_object_class_install_property (object_class, PROP_CONNECTION, param_spec); - - param_spec = g_param_spec_uint ("creator", "Channel creator", - "The TpHandle representing the contact " - "who created the channel.", - 0, G_MAXUINT32, 0, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | - G_PARAM_STATIC_NAME | - G_PARAM_STATIC_BLURB); - g_object_class_install_property (object_class, PROP_CREATOR, param_spec); - - param_spec = g_param_spec_object ("factory", "GabbleMediaFactory object", - "The factory that created this object.", - GABBLE_TYPE_MEDIA_FACTORY, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | - G_PARAM_STATIC_NICK | - G_PARAM_STATIC_BLURB); - g_object_class_install_property (object_class, PROP_FACTORY, param_spec); - - param_spec = g_param_spec_string ("nat-traversal", - "NAT traversal", - "NAT traversal mechanism.", - "gtalk-p2p", - G_PARAM_CONSTRUCT | - G_PARAM_READWRITE | - G_PARAM_STATIC_NAME | - G_PARAM_STATIC_BLURB); - g_object_class_install_property (object_class, PROP_NAT_TRAVERSAL, - param_spec); - - param_spec = g_param_spec_string ("stun-server", - "STUN server", - "IP or address of STUN server.", - NULL, - G_PARAM_READWRITE | - G_PARAM_STATIC_NAME | - G_PARAM_STATIC_BLURB); - g_object_class_install_property (object_class, PROP_STUN_SERVER, param_spec); - - param_spec = g_param_spec_uint ("stun-port", - "STUN port", - "UDP port of STUN server.", - 0, G_MAXUINT16, 0, - G_PARAM_READWRITE | - G_PARAM_STATIC_NAME | - G_PARAM_STATIC_BLURB); - g_object_class_install_property (object_class, PROP_STUN_PORT, param_spec); - - param_spec = g_param_spec_string ("gtalk-p2p-relay-token", - "GTalk P2P Relay Token", - "Magic token to authenticate with the " - "Google Talk relay server.", - NULL, - G_PARAM_READWRITE | - G_PARAM_STATIC_NAME | - G_PARAM_STATIC_BLURB); - g_object_class_install_property (object_class, PROP_GTALK_P2P_RELAY_TOKEN, - param_spec); - - tp_properties_mixin_class_init (object_class, - G_STRUCT_OFFSET (GabbleMediaChannelClass, properties_class), - channel_property_signatures, NUM_CHAN_PROPS, NULL); -} - -void -gabble_media_channel_dispose (GObject *object) -{ - GabbleMediaChannel *self = GABBLE_MEDIA_CHANNEL (object); - GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (self); - - if (priv->dispose_has_run) - return; - - priv->dispose_has_run = TRUE; - - /** In this we set the state to ENDED, then the callback unrefs - * the session - */ - - if (!priv->closed) - gabble_media_channel_close (self); - - g_assert (priv->closed); - g_assert (priv->session == NULL); - g_assert (priv->streams == NULL); - - if (G_OBJECT_CLASS (gabble_media_channel_parent_class)->dispose) - G_OBJECT_CLASS (gabble_media_channel_parent_class)->dispose (object); -} - -void -gabble_media_channel_finalize (GObject *object) -{ - GabbleMediaChannel *self = GABBLE_MEDIA_CHANNEL (object); - GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (self); - - g_free (priv->object_path); - - tp_group_mixin_finalize (object); - - G_OBJECT_CLASS (gabble_media_channel_parent_class)->finalize (object); -} - - -/** - * gabble_media_channel_close - * - * Implements D-Bus method Close - * on interface org.freedesktop.Telepathy.Channel - */ -static void -gabble_media_channel_close_async (TpSvcChannel *iface, - DBusGMethodInvocation *context) -{ - GabbleMediaChannel *self = GABBLE_MEDIA_CHANNEL (iface); - - gabble_media_channel_close (self); - tp_svc_channel_return_from_close (context); -} - -void -gabble_media_channel_close (GabbleMediaChannel *self) -{ - GabbleMediaChannelPrivate *priv; - - DEBUG ("called on %p", self); - - g_assert (GABBLE_IS_MEDIA_CHANNEL (self)); - - priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (self); - - if (priv->closed) - { - return; - } - - priv->closed = TRUE; - - if (priv->session) - { - _gabble_media_session_terminate (priv->session, INITIATOR_LOCAL, - TP_CHANNEL_GROUP_CHANGE_REASON_NONE); - } - - tp_svc_channel_emit_closed (self); -} - - -/** - * gabble_media_channel_get_channel_type - * - * Implements D-Bus method GetChannelType - * on interface org.freedesktop.Telepathy.Channel - */ -static void -gabble_media_channel_get_channel_type (TpSvcChannel *iface, - DBusGMethodInvocation *context) -{ - tp_svc_channel_return_from_get_channel_type (context, - TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA); -} - - -/** - * gabble_media_channel_get_handle - * - * Implements D-Bus method GetHandle - * on interface org.freedesktop.Telepathy.Channel - */ -static void -gabble_media_channel_get_handle (TpSvcChannel *iface, - DBusGMethodInvocation *context) -{ - tp_svc_channel_return_from_get_handle (context, 0, 0); -} - - -/** - * gabble_media_channel_get_interfaces - * - * Implements D-Bus method GetInterfaces - * on interface org.freedesktop.Telepathy.Channel - */ -static void -gabble_media_channel_get_interfaces (TpSvcChannel *iface, - DBusGMethodInvocation *context) -{ - const gchar *interfaces[] = { - TP_IFACE_CHANNEL_INTERFACE_GROUP, - TP_IFACE_CHANNEL_INTERFACE_MEDIA_SIGNALLING, - TP_IFACE_PROPERTIES_INTERFACE, - NULL - }; - - tp_svc_channel_return_from_get_interfaces (context, interfaces); -} - - -/** - * gabble_media_channel_get_session_handlers - * - * Implements D-Bus method GetSessionHandlers - * on interface org.freedesktop.Telepathy.Channel.Interface.MediaSignalling - */ -static void -gabble_media_channel_get_session_handlers (TpSvcChannelInterfaceMediaSignalling *iface, - DBusGMethodInvocation *context) -{ - GabbleMediaChannel *self = GABBLE_MEDIA_CHANNEL (iface); - GabbleMediaChannelPrivate *priv; - GPtrArray *ret; - - g_assert (GABBLE_IS_MEDIA_CHANNEL (self)); - - priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (self); - - if (priv->session) - { - GValue handler = { 0, }; - TpHandle member; - gchar *path; - - g_value_init (&handler, TP_SESSION_HANDLER_SET_TYPE); - g_value_take_boxed (&handler, - dbus_g_type_specialized_construct (TP_SESSION_HANDLER_SET_TYPE)); - - g_object_get (priv->session, - "peer", &member, - "object-path", &path, - NULL); - - dbus_g_type_struct_set (&handler, - 0, path, - 1, "rtp", - G_MAXUINT); - - g_free (path); - - ret = g_ptr_array_sized_new (1); - g_ptr_array_add (ret, g_value_get_boxed (&handler)); - } - else - { - ret = g_ptr_array_sized_new (0); - } - - tp_svc_channel_interface_media_signalling_return_from_get_session_handlers ( - context, ret); - g_ptr_array_free (ret, TRUE); -} - - -static GPtrArray * -make_stream_list (GabbleMediaChannel *self, - GPtrArray *streams) -{ - GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (self); - GPtrArray *ret; - guint i; - - ret = g_ptr_array_sized_new (streams->len); - - for (i = 0; i < streams->len; i++) - { - GabbleMediaStream *stream = g_ptr_array_index (streams, i); - GValue entry = { 0, }; - guint id; - TpHandle peer; - TpMediaStreamType type; - TpMediaStreamState connection_state; - CombinedStreamDirection combined_direction; - - g_object_get (stream, - "id", &id, - "media-type", &type, - "connection-state", &connection_state, - "combined-direction", &combined_direction, - NULL); - - g_object_get (priv->session, "peer", &peer, NULL); - - g_value_init (&entry, TP_CHANNEL_STREAM_TYPE); - g_value_take_boxed (&entry, - dbus_g_type_specialized_construct (TP_CHANNEL_STREAM_TYPE)); - - dbus_g_type_struct_set (&entry, - 0, id, - 1, peer, - 2, type, - 3, connection_state, - 4, COMBINED_DIRECTION_GET_DIRECTION (combined_direction), - 5, COMBINED_DIRECTION_GET_PENDING_SEND (combined_direction), - G_MAXUINT); - - g_ptr_array_add (ret, g_value_get_boxed (&entry)); - } - - return ret; -} - -/** - * gabble_media_channel_list_streams - * - * Implements D-Bus method ListStreams - * on interface org.freedesktop.Telepathy.Channel.Type.StreamedMedia - */ -static void -gabble_media_channel_list_streams (TpSvcChannelTypeStreamedMedia *iface, - DBusGMethodInvocation *context) -{ - GabbleMediaChannel *self = GABBLE_MEDIA_CHANNEL (iface); - GabbleMediaChannelPrivate *priv; - GPtrArray *ret; - - g_assert (GABBLE_IS_MEDIA_CHANNEL (self)); - - priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (self); - - /* no session yet? return an empty array */ - if (priv->session == NULL) - { - ret = g_ptr_array_new (); - } - else - { - ret = make_stream_list (self, priv->streams); - } - - tp_svc_channel_type_streamed_media_return_from_list_streams (context, ret); - g_ptr_array_free (ret, TRUE); -} - - -static GabbleMediaStream * -_find_stream_by_id (GabbleMediaChannel *chan, guint stream_id) -{ - GabbleMediaChannelPrivate *priv; - guint i; - - g_assert (GABBLE_IS_MEDIA_CHANNEL (chan)); - - priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (chan); - - for (i = 0; i < priv->streams->len; i++) - { - GabbleMediaStream *stream = g_ptr_array_index (priv->streams, i); - guint id; - - g_object_get (stream, "id", &id, NULL); - if (id == stream_id) - return stream; - } - - return NULL; -} - -/** - * gabble_media_channel_remove_streams - * - * Implements DBus method RemoveStreams - * on interface org.freedesktop.Telepathy.Channel.Type.StreamedMedia - */ -static void -gabble_media_channel_remove_streams (TpSvcChannelTypeStreamedMedia *iface, - const GArray * streams, - DBusGMethodInvocation *context) -{ - GabbleMediaChannel *obj = GABBLE_MEDIA_CHANNEL (iface); - GabbleMediaChannelPrivate *priv; - GPtrArray *stream_objs; - GError *error = NULL; - guint i; - - g_assert (GABBLE_IS_MEDIA_CHANNEL (obj)); - - priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (obj); - - stream_objs = g_ptr_array_sized_new (streams->len); - - /* check that all stream ids are valid and at the same time build an array - * of stream objects so we don't have to look them up again after verifying - * all stream identifiers. */ - for (i = 0; i < streams->len; i++) - { - guint id = g_array_index (streams, guint, i); - GabbleMediaStream *stream; - guint j; - - stream = _find_stream_by_id (obj, id); - if (stream == NULL) - { - g_set_error (&error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, - "given stream id %u does not exist", id); - goto OUT; - } - - /* make sure we don't allow the client to repeatedly remove the same - stream */ - for (j = 0; j < stream_objs->len; j++) - { - GabbleMediaStream *tmp = g_ptr_array_index (stream_objs, j); - - if (tmp == stream) - { - stream = NULL; - break; - } - } - - if (stream != NULL) - g_ptr_array_add (stream_objs, stream); - } - - /* groovy, it's all good dude, let's remove them */ - if (stream_objs->len > 0) - _gabble_media_session_remove_streams (priv->session, (GabbleMediaStream **) - stream_objs->pdata, stream_objs->len); - -OUT: - g_ptr_array_free (stream_objs, TRUE); - - if (error) - { - dbus_g_method_return_error (context, error); - g_error_free (error); - } - else - { - tp_svc_channel_type_streamed_media_return_from_remove_streams (context); - } -} - - -/** - * gabble_media_channel_request_stream_direction - * - * Implements D-Bus method RequestStreamDirection - * on interface org.freedesktop.Telepathy.Channel.Type.StreamedMedia - */ -static void -gabble_media_channel_request_stream_direction (TpSvcChannelTypeStreamedMedia *iface, - guint stream_id, - guint stream_direction, - DBusGMethodInvocation *context) -{ - GabbleMediaChannel *self = GABBLE_MEDIA_CHANNEL (iface); - GabbleMediaChannelPrivate *priv; - GabbleMediaStream *stream; - GError *error = NULL; - - g_assert (GABBLE_IS_MEDIA_CHANNEL (self)); - - priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (self); - - if (stream_direction > TP_MEDIA_STREAM_DIRECTION_BIDIRECTIONAL) - { - g_set_error (&error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, - "given stream direction %u is not valid", stream_direction); - dbus_g_method_return_error (context, error); - g_error_free (error); - return; - } - - stream = _find_stream_by_id (self, stream_id); - if (stream == NULL) - { - g_set_error (&error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, - "given stream id %u does not exist", stream_id); - dbus_g_method_return_error (context, error); - g_error_free (error); - return; - } - - /* streams with no session? I think not... */ - g_assert (priv->session != NULL); - - if (_gabble_media_session_request_stream_direction (priv->session, stream, - stream_direction, &error)) - { - tp_svc_channel_type_streamed_media_return_from_request_stream_direction ( - context); - } - else - { - dbus_g_method_return_error (context, error); - g_error_free (error); - } -} - - -/** - * gabble_media_channel_request_streams - * - * Implements D-Bus method RequestStreams - * on interface org.freedesktop.Telepathy.Channel.Type.StreamedMedia - */ -static void -gabble_media_channel_request_streams (TpSvcChannelTypeStreamedMedia *iface, - guint contact_handle, - const GArray *types, - DBusGMethodInvocation *context) -{ - GabbleMediaChannel *self = GABBLE_MEDIA_CHANNEL (iface); - GabbleMediaChannelPrivate *priv; - TpBaseConnection *conn; - GPtrArray *streams; - GError *error = NULL; - GPtrArray *ret; - TpHandleRepoIface *contact_handles; - - g_assert (GABBLE_IS_MEDIA_CHANNEL (self)); - - priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (self); - conn = (TpBaseConnection *)priv->conn; - contact_handles = tp_base_connection_get_handles (conn, - TP_HANDLE_TYPE_CONTACT); - - if (!tp_handle_is_valid (contact_handles, contact_handle, &error)) - { - dbus_g_method_return_error (context, error); - g_error_free (error); - return; - } - - if (!tp_handle_set_is_member (self->group.members, contact_handle) && - !tp_handle_set_is_member (self->group.remote_pending, contact_handle)) - { - g_set_error (&error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, - "given handle %u is not a member of the channel", contact_handle); - dbus_g_method_return_error (context, error); - g_error_free (error); - return; - } - - /* if the person is a channel member, we should have a session */ - g_assert (priv->session != NULL); - - if (!_gabble_media_session_request_streams (priv->session, types, &streams, - &error)) - { - dbus_g_method_return_error (context, error); - g_error_free (error); - return; - } - - ret = make_stream_list (self, streams); - - g_ptr_array_free (streams, TRUE); - - tp_svc_channel_type_streamed_media_return_from_request_streams (context, ret); - g_ptr_array_free (ret, TRUE); -} - - -gboolean -_gabble_media_channel_add_member (GObject *obj, - TpHandle handle, - const gchar *message, - GError **error) -{ - GabbleMediaChannel *chan = GABBLE_MEDIA_CHANNEL (obj); - GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (chan); - TpGroupMixin *mixin = TP_GROUP_MIXIN (obj); -#ifdef ENABLE_DEBUG - TpBaseConnection *conn = (TpBaseConnection *)priv->conn; - TpHandleRepoIface *contact_handles = tp_base_connection_get_handles (conn, - TP_HANDLE_TYPE_CONTACT); -#endif - - /* did we create this channel? */ - if (priv->creator == mixin->self_handle) - { - GabblePresence *presence; - TpIntSet *set; - - /* yes: check the peer's capabilities */ - - presence = gabble_presence_cache_get (priv->conn->presence_cache, handle); - - if (presence == NULL) - { - DEBUG ("failed to add contact %d (%s) to media channel: " - "no presence available", handle, - tp_handle_inspect (contact_handles, handle)); - goto NO_CAPS; - } - - if (!(presence->caps & PRESENCE_CAP_GOOGLE_VOICE || - presence->caps & PRESENCE_CAP_JINGLE_DESCRIPTION_AUDIO || - presence->caps & PRESENCE_CAP_JINGLE_DESCRIPTION_VIDEO)) - { - DEBUG ("failed to add contact %d (%s) to media channel: " - "caps %x aren't sufficient", handle, - tp_handle_inspect (contact_handles, handle), - presence->caps); - goto NO_CAPS; - } - - /* yes: invite the peer */ - - /* create a new session */ - create_session (chan, handle, NULL, NULL); - - /* make the peer remote pending */ - set = tp_intset_new (); - tp_intset_add (set, handle); - - tp_group_mixin_change_members (obj, "", NULL, NULL, NULL, set, 0, 0); - - tp_intset_destroy (set); - - /* and update flags accordingly */ - tp_group_mixin_change_flags (obj, - TP_CHANNEL_GROUP_FLAG_CAN_REMOVE | TP_CHANNEL_GROUP_FLAG_CAN_RESCIND, - TP_CHANNEL_GROUP_FLAG_CAN_ADD); - - return TRUE; - } - else - { - /* no: has a session been created, is the handle being added ours, - * and are we in local pending? */ - - if (priv->session && - handle == mixin->self_handle && - tp_handle_set_is_member (mixin->local_pending, handle)) - { - /* yes: accept the request */ - - TpIntSet *set; - - /* make us a member */ - set = tp_intset_new (); - tp_intset_add (set, handle); - - tp_group_mixin_change_members (obj, - "", set, NULL, NULL, NULL, 0, 0); - - tp_intset_destroy (set); - - /* update flags */ - tp_group_mixin_change_flags (obj, - 0, TP_CHANNEL_GROUP_FLAG_CAN_ADD); - - /* signal acceptance */ - _gabble_media_session_accept (priv->session); - - return TRUE; - } - } - - g_set_error (error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE, - "handle %u cannot be added in the current state", handle); - return FALSE; - -NO_CAPS: - g_set_error (error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE, - "handle %u has no media capabilities", handle); - return FALSE; -} - -static gboolean -gabble_media_channel_remove_member (GObject *obj, - TpHandle handle, - const gchar *message, - GError **error) -{ - GabbleMediaChannel *chan = GABBLE_MEDIA_CHANNEL (obj); - GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (chan); - TpGroupMixin *mixin = TP_GROUP_MIXIN (obj); - TpIntSet *set; - - if (priv->session == NULL) - { - g_set_error (error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE, - "handle %u cannot be removed in the current state", handle); - - return FALSE; - } - - if (priv->creator != mixin->self_handle && - handle != mixin->self_handle) - { - g_set_error (error, TP_ERRORS, TP_ERROR_PERMISSION_DENIED, - "handle %u cannot be removed because you are not the creator of the" - " channel", handle); - - return FALSE; - } - - _gabble_media_session_terminate (priv->session, INITIATOR_LOCAL, - TP_CHANNEL_GROUP_CHANGE_REASON_NONE); - - /* remove the member */ - set = tp_intset_new (); - tp_intset_add (set, handle); - - tp_group_mixin_change_members (obj, "", NULL, set, NULL, NULL, 0, 0); - - tp_intset_destroy (set); - - /* and update flags accordingly */ - tp_group_mixin_change_flags (obj, TP_CHANNEL_GROUP_FLAG_CAN_ADD, - TP_CHANNEL_GROUP_FLAG_CAN_REMOVE | TP_CHANNEL_GROUP_FLAG_CAN_RESCIND); - - return TRUE; -} - -static void -session_terminated_cb (GabbleMediaSession *session, - guint terminator, - guint reason, - gpointer user_data) -{ - GabbleMediaChannel *channel = (GabbleMediaChannel *) user_data; - GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (channel); - TpGroupMixin *mixin = TP_GROUP_MIXIN (channel); - gchar *sid; - JingleSessionState state; - TpHandle peer; - TpIntSet *set; - - g_object_get (session, - "state", &state, - "peer", &peer, - NULL); - - set = tp_intset_new (); - - /* remove us and the peer from the member list */ - tp_intset_add (set, mixin->self_handle); - tp_intset_add (set, peer); - - tp_group_mixin_change_members ((GObject *)channel, - "", NULL, set, NULL, NULL, terminator, reason); - - /* update flags accordingly -- allow adding, deny removal */ - tp_group_mixin_change_flags ((GObject *)channel, - TP_CHANNEL_GROUP_FLAG_CAN_ADD, - TP_CHANNEL_GROUP_FLAG_CAN_REMOVE); - - /* free the session ID */ - g_object_get (priv->session, "session-id", &sid, NULL); - _gabble_media_factory_free_sid (priv->factory, sid); - g_free (sid); - - /* unref streams */ - if (priv->streams != NULL) - { - GPtrArray *tmp = priv->streams; - - /* move priv->streams aside so that the stream_close_cb - * doesn't double unref */ - priv->streams = NULL; - g_ptr_array_foreach (tmp, (GFunc) g_object_unref, NULL); - g_ptr_array_free (tmp, TRUE); - } - - /* remove the session */ - g_object_unref (priv->session); - priv->session = NULL; - - /* close the channel */ - gabble_media_channel_close (channel); -} - - -static void -session_state_changed_cb (GabbleMediaSession *session, - GParamSpec *arg1, - GabbleMediaChannel *channel) -{ - GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (channel); - TpGroupMixin *mixin = TP_GROUP_MIXIN (channel); - JingleSessionState state; - TpHandle peer; - TpIntSet *set; - - g_object_get (session, - "state", &state, - "peer", &peer, - NULL); - - if (state != JS_STATE_ACTIVE) - return; - - if (priv->creator != mixin->self_handle) - return; - - set = tp_intset_new (); - - /* add the peer to the member list */ - tp_intset_add (set, peer); - - tp_group_mixin_change_members ((GObject *)channel, - "", set, NULL, NULL, NULL, 0, 0); - - /* update flags accordingly -- allow removal, deny adding and rescinding */ - tp_group_mixin_change_flags ((GObject *)channel, - TP_CHANNEL_GROUP_FLAG_CAN_REMOVE, - TP_CHANNEL_GROUP_FLAG_CAN_ADD | TP_CHANNEL_GROUP_FLAG_CAN_RESCIND); - - tp_intset_destroy (set); -} - -static void -stream_close_cb (GabbleMediaStream *stream, - GabbleMediaChannel *chan) -{ - GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (chan); - guint id; - - g_object_get (stream, "id", &id, NULL); - - tp_svc_channel_type_streamed_media_emit_stream_removed (chan, id); - - if (priv->streams != NULL) - { - g_ptr_array_remove (priv->streams, stream); - g_object_unref (stream); - } -} - -static void -stream_error_cb (GabbleMediaStream *stream, - TpMediaStreamError errno, - const gchar *message, - GabbleMediaChannel *chan) -{ - GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (chan); - guint id; - - /* emit signal */ - g_object_get (stream, "id", &id, NULL); - tp_svc_channel_type_streamed_media_emit_stream_error (chan, id, errno, - message); - - /* remove stream from session */ - _gabble_media_session_remove_streams (priv->session, &stream, 1); -} - -static void -stream_state_changed_cb (GabbleMediaStream *stream, - GParamSpec *pspec, - GabbleMediaChannel *chan) -{ - guint id; - TpMediaStreamState connection_state; - - g_object_get (stream, - "id", &id, - "connection-state", &connection_state, - NULL); - - tp_svc_channel_type_streamed_media_emit_stream_state_changed (chan, - id, connection_state); -} - -static void -stream_direction_changed_cb (GabbleMediaStream *stream, - GParamSpec *pspec, - GabbleMediaChannel *chan) -{ - guint id; - CombinedStreamDirection combined; - TpMediaStreamDirection direction; - TpMediaStreamPendingSend pending_send; - - g_object_get (stream, - "id", &id, - "combined-direction", &combined, - NULL); - - direction = COMBINED_DIRECTION_GET_DIRECTION (combined); - pending_send = COMBINED_DIRECTION_GET_PENDING_SEND (combined); - - tp_svc_channel_type_streamed_media_emit_stream_direction_changed ( - chan, id, direction, pending_send); -} - -static void -session_stream_added_cb (GabbleMediaSession *session, - GabbleMediaStream *stream, - GabbleMediaChannel *chan) -{ - GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (chan); - - guint id, handle, type; - - /* keep track of the stream */ - g_object_ref (stream); - g_ptr_array_add (priv->streams, stream); - - g_signal_connect (stream, "close", - (GCallback) stream_close_cb, chan); - g_signal_connect (stream, "error", - (GCallback) stream_error_cb, chan); - g_signal_connect (stream, "notify::connection-state", - (GCallback) stream_state_changed_cb, chan); - g_signal_connect (stream, "notify::combined-direction", - (GCallback) stream_direction_changed_cb, chan); - - /* emit StreamAdded */ - g_object_get (session, "peer", &handle, NULL); - g_object_get (stream, "id", &id, "media-type", &type, NULL); - - tp_svc_channel_type_streamed_media_emit_stream_added ( - chan, id, handle, type); -} - -guint -_gabble_media_channel_get_stream_id (GabbleMediaChannel *chan) -{ - GabbleMediaChannelPrivate *priv = GABBLE_MEDIA_CHANNEL_GET_PRIVATE (chan); - - return priv->next_stream_id++; -} - -#define AUDIO_CAPS \ - ( PRESENCE_CAP_GOOGLE_VOICE | PRESENCE_CAP_JINGLE_DESCRIPTION_AUDIO ) - -#define VIDEO_CAPS \ - ( PRESENCE_CAP_JINGLE_DESCRIPTION_VIDEO ) - -GabblePresenceCapabilities -_gabble_media_channel_typeflags_to_caps (TpChannelMediaCapabilities flags) -{ - GabblePresenceCapabilities caps = 0; - - if (flags & TP_CHANNEL_MEDIA_CAPABILITY_AUDIO) - caps |= AUDIO_CAPS; - - if (flags & TP_CHANNEL_MEDIA_CAPABILITY_VIDEO) - caps |= VIDEO_CAPS; - - return caps; -} - -TpChannelMediaCapabilities -_gabble_media_channel_caps_to_typeflags (GabblePresenceCapabilities caps) -{ - TpChannelMediaCapabilities typeflags = 0; - - if (caps & AUDIO_CAPS) - typeflags |= TP_CHANNEL_MEDIA_CAPABILITY_AUDIO; - - if (caps & VIDEO_CAPS) - typeflags |= TP_CHANNEL_MEDIA_CAPABILITY_VIDEO; - - return typeflags; -} - - -static void -channel_iface_init (gpointer g_iface, gpointer iface_data) -{ - TpSvcChannelClass *klass = (TpSvcChannelClass *)g_iface; - -#define IMPLEMENT(x, suffix) tp_svc_channel_implement_##x (\ - klass, gabble_media_channel_##x##suffix) - IMPLEMENT(close,_async); - IMPLEMENT(get_channel_type,); - IMPLEMENT(get_handle,); - IMPLEMENT(get_interfaces,); -#undef IMPLEMENT -} - -static void -streamed_media_iface_init (gpointer g_iface, gpointer iface_data) -{ - TpSvcChannelTypeStreamedMediaClass *klass = - (TpSvcChannelTypeStreamedMediaClass *)g_iface; - -#define IMPLEMENT(x) tp_svc_channel_type_streamed_media_implement_##x (\ - klass, gabble_media_channel_##x) - IMPLEMENT(list_streams); - IMPLEMENT(remove_streams); - IMPLEMENT(request_stream_direction); - IMPLEMENT(request_streams); -#undef IMPLEMENT -} - -static void -media_signalling_iface_init (gpointer g_iface, gpointer iface_data) -{ - TpSvcChannelInterfaceMediaSignallingClass *klass = - (TpSvcChannelInterfaceMediaSignallingClass *)g_iface; - -#define IMPLEMENT(x) tp_svc_channel_interface_media_signalling_implement_##x (\ - klass, gabble_media_channel_##x) - IMPLEMENT(get_session_handlers); -#undef IMPLEMENT -} diff --git a/src/gabble-media-channel.h b/src/gabble-media-channel.h deleted file mode 100644 index e71fe1e0f..000000000 --- a/src/gabble-media-channel.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - * gabble-media-channel.h - Header for GabbleMediaChannel - * Copyright (C) 2006 Collabora Ltd. - * Copyright (C) 2006 Nokia Corporation - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef __GABBLE_MEDIA_CHANNEL_H__ -#define __GABBLE_MEDIA_CHANNEL_H__ - -#include <glib-object.h> - -#include <telepathy-glib/group-mixin.h> -#include <telepathy-glib/properties-mixin.h> - -#include "gabble-media-session.h" -#include "presence.h" - -G_BEGIN_DECLS - -typedef struct _GabbleMediaChannel GabbleMediaChannel; -typedef struct _GabbleMediaChannelClass GabbleMediaChannelClass; - -struct _GabbleMediaChannelClass { - GObjectClass parent_class; - - TpGroupMixinClass group_class; - TpPropertiesMixinClass properties_class; -}; - -struct _GabbleMediaChannel { - GObject parent; - - TpGroupMixin group; - TpPropertiesMixin properties; - - gpointer priv; -}; - -GType gabble_media_channel_get_type (void); - -/* TYPE MACROS */ -#define GABBLE_TYPE_MEDIA_CHANNEL \ - (gabble_media_channel_get_type ()) -#define GABBLE_MEDIA_CHANNEL(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_MEDIA_CHANNEL,\ - GabbleMediaChannel)) -#define GABBLE_MEDIA_CHANNEL_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_MEDIA_CHANNEL,\ - GabbleMediaChannelClass)) -#define GABBLE_IS_MEDIA_CHANNEL(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_MEDIA_CHANNEL)) -#define GABBLE_IS_MEDIA_CHANNEL_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_MEDIA_CHANNEL)) -#define GABBLE_MEDIA_CHANNEL_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_MEDIA_CHANNEL, \ - GabbleMediaChannelClass)) - -gboolean -_gabble_media_channel_add_member (GObject *obj, - TpHandle handle, - const gchar *message, - GError **error); - -gboolean -_gabble_media_channel_dispatch_session_action (GabbleMediaChannel *chan, - TpHandle peer, - const gchar *peer_resource, - const gchar *sid, - LmMessage *message, - LmMessageNode *session_node, - const gchar *action, - GError **error); - -void -_gabble_media_channel_stream_state (GabbleMediaChannel *chan, - guint state); - -guint -_gabble_media_channel_get_stream_id (GabbleMediaChannel *chan); - -GabblePresenceCapabilities -_gabble_media_channel_typeflags_to_caps (TpChannelMediaCapabilities flags); - -TpChannelMediaCapabilities -_gabble_media_channel_caps_to_typeflags (GabblePresenceCapabilities caps); - -void gabble_media_channel_close (GabbleMediaChannel *); - -G_END_DECLS - -#endif /* #ifndef __GABBLE_MEDIA_CHANNEL_H__*/ diff --git a/src/gabble-media-session.c b/src/gabble-media-session.c deleted file mode 100644 index f0345533e..000000000 --- a/src/gabble-media-session.c +++ /dev/null @@ -1,2928 +0,0 @@ -/* - * gabble-media-session.c - Source for GabbleMediaSession - * Copyright (C) 2006 Collabora Ltd. - * Copyright (C) 2006 Nokia Corporation - * @author Ole Andre Vadla Ravnaas <ole.andre.ravnaas@collabora.co.uk> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "gabble-media-session.h" - -#include <dbus/dbus-glib.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> - -#define DEBUG_FLAG GABBLE_DEBUG_MEDIA - -#include <telepathy-glib/debug-ansi.h> -#include "debug.h" -#include "namespaces.h" -#include "util.h" - -#include <telepathy-glib/dbus.h> -#include <telepathy-glib/errors.h> -#include <telepathy-glib/svc-media-interfaces.h> - -#include "gabble-connection.h" -#include "gabble-media-channel.h" -#include "gabble-media-stream.h" -#include "presence-cache.h" -#include "presence.h" - -#include "gabble-signals-marshal.h" - -static void session_handler_iface_init (gpointer, gpointer); - -G_DEFINE_TYPE_WITH_CODE(GabbleMediaSession, - gabble_media_session, - G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_MEDIA_SESSION_HANDLER, - session_handler_iface_init) - ) - -#define DEFAULT_SESSION_TIMEOUT 50000 - -#define GTALK_STREAM_NAME "gtalk" - -/* 99 streams gives us a max name length of 8 (videoXX\0 or audioXX\0) */ -#define MAX_STREAMS 99 -#define MAX_STREAM_NAME_LEN 8 - -/* signal enum */ -enum -{ - STREAM_ADDED, - TERMINATED, - LAST_SIGNAL -}; - -static guint signals[LAST_SIGNAL] = {0}; - -/* properties */ -enum -{ - PROP_CONNECTION = 1, - PROP_MEDIA_CHANNEL, - PROP_OBJECT_PATH, - PROP_SESSION_ID, - PROP_INITIATOR, - PROP_PEER, - PROP_PEER_RESOURCE, - PROP_STATE, - LAST_PROPERTY -}; - -/* private structure */ -typedef struct _GabbleMediaSessionPrivate GabbleMediaSessionPrivate; - -struct _GabbleMediaSessionPrivate -{ - GabbleConnection *conn; - GabbleMediaChannel *channel; - GabbleMediaSessionMode mode; - gchar *object_path; - - GPtrArray *streams; - GPtrArray *remove_requests; - - gchar *id; - TpHandle peer; - gchar *peer_resource; - - JingleSessionState state; - gboolean ready; - gboolean locally_accepted; - gboolean terminated; - - guint timer_id; - - gboolean dispose_has_run; -}; - -#define GABBLE_MEDIA_SESSION_GET_PRIVATE(obj) \ - ((GabbleMediaSessionPrivate *)obj->priv) - -typedef struct { - gchar *name; - gchar *attributes; -} SessionStateDescription; - -static const SessionStateDescription session_states[] = -{ - { "JS_STATE_PENDING_CREATED", - TP_ANSI_BOLD_ON TP_ANSI_FG_BLACK TP_ANSI_BG_WHITE }, - { "JS_STATE_PENDING_INITIATE_SENT", - TP_ANSI_BOLD_ON TP_ANSI_BG_CYAN }, - { "JS_STATE_PENDING_INITIATED", - TP_ANSI_BOLD_ON TP_ANSI_BG_MAGENTA }, - { "JS_STATE_PENDING_ACCEPT_SENT", - TP_ANSI_BOLD_ON TP_ANSI_BG_CYAN }, - { "JS_STATE_ACTIVE", - TP_ANSI_BOLD_ON TP_ANSI_BG_BLUE }, - { "JS_STATE_ENDED", - TP_ANSI_BG_RED } -}; - -static void -gabble_media_session_init (GabbleMediaSession *self) -{ - GabbleMediaSessionPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self, - GABBLE_TYPE_MEDIA_SESSION, GabbleMediaSessionPrivate); - - self->priv = priv; - - priv->mode = MODE_JINGLE; - priv->state = JS_STATE_PENDING_CREATED; - priv->streams = g_ptr_array_new (); - priv->remove_requests = g_ptr_array_new (); -} - -static void stream_connection_state_changed_cb (GabbleMediaStream *stream, - GParamSpec *param, - GabbleMediaSession *session); -static void stream_got_local_codecs_changed_cb (GabbleMediaStream *stream, - GParamSpec *param, - GabbleMediaSession *session); - -static void -_emit_new_stream (GabbleMediaSession *session, - GabbleMediaStream *stream) -{ - gchar *object_path; - guint id, media_type; - - g_object_get (stream, - "object-path", &object_path, - "id", &id, - "media-type", &media_type, - NULL); - - /* all of the streams are bidirectional from farsight's point of view, it's - * just in the signalling they change */ - tp_svc_media_session_handler_emit_new_stream_handler (session, - object_path, id, media_type, TP_MEDIA_STREAM_DIRECTION_BIDIRECTIONAL); - - g_free (object_path); -} - - -static GabbleMediaStream * -_lookup_stream_by_name_and_initiator (GabbleMediaSession *session, - const gchar *stream_name, - JingleInitiator stream_initiator) -{ - GabbleMediaSessionPrivate *priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session); - guint i; - - for (i = 0; i < priv->streams->len; i++) - { - GabbleMediaStream *stream = g_ptr_array_index (priv->streams, i); - - if (tp_strdiff (stream->name, stream_name)) - continue; - - if (stream_initiator != INITIATOR_INVALID && - stream_initiator != stream->initiator) - continue; - - return stream; - } - - return NULL; -} - - -static GabbleMediaStream * -create_media_stream (GabbleMediaSession *session, - const gchar *name, - JingleInitiator initiator, - guint media_type) -{ - GabbleMediaSessionPrivate *priv; - gchar *object_path; - GabbleMediaStream *stream; - guint id; - - g_assert (GABBLE_IS_MEDIA_SESSION (session)); - g_assert (name != NULL); - - priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session); - - /* assert that if we're in google mode: - * - we only try to make one stream - * - it's an audio stream - * - it's called GTALK_STREAM_NAME */ - if (priv->mode == MODE_GOOGLE) - { - g_assert (priv->streams->len == 0); - g_assert (media_type == TP_MEDIA_STREAM_TYPE_AUDIO); - g_assert (!tp_strdiff (name, GTALK_STREAM_NAME)); - } - - g_assert (priv->streams->len < MAX_STREAMS); - g_assert (_lookup_stream_by_name_and_initiator (session, name, initiator) == - NULL); - - id = _gabble_media_channel_get_stream_id (priv->channel); - - GMS_DEBUG_INFO (session, - "creating new %s %s stream called \"%s\" with id %u", - priv->mode == MODE_GOOGLE ? "google" : "jingle", - media_type == TP_MEDIA_STREAM_TYPE_AUDIO ? "audio" : "video", - name, id); - - object_path = g_strdup_printf ("%s/MediaStream%u", priv->object_path, id); - - stream = g_object_new (GABBLE_TYPE_MEDIA_STREAM, - "connection", priv->conn, - "media-session", session, - "object-path", object_path, - "mode", priv->mode, - "name", name, - "id", id, - "initiator", initiator, - "media-type", media_type, - NULL); - - g_signal_connect (stream, "notify::connection-state", - (GCallback) stream_connection_state_changed_cb, - session); - g_signal_connect (stream, "notify::got-local-codecs", - (GCallback) stream_got_local_codecs_changed_cb, - session); - - g_ptr_array_add (priv->streams, stream); - - g_free (object_path); - - if (priv->ready) - _emit_new_stream (session, stream); - - g_signal_emit (session, signals[STREAM_ADDED], 0, stream); - - return stream; -} - -static void -destroy_media_stream (GabbleMediaSession *session, - GabbleMediaStream *stream) -{ - GabbleMediaSessionPrivate *priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session); - - _gabble_media_stream_close (stream); - g_ptr_array_remove_fast (priv->streams, stream); - g_object_unref (stream); -} - -static GObject * -gabble_media_session_constructor (GType type, guint n_props, - GObjectConstructParam *props) -{ - GObject *obj; - GabbleMediaSessionPrivate *priv; - DBusGConnection *bus; - - obj = G_OBJECT_CLASS (gabble_media_session_parent_class)-> - constructor (type, n_props, props); - priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (GABBLE_MEDIA_SESSION (obj)); - - bus = tp_get_bus (); - dbus_g_connection_register_g_object (bus, priv->object_path, obj); - - return obj; -} - -static void -gabble_media_session_get_property (GObject *object, - guint property_id, - GValue *value, - GParamSpec *pspec) -{ - GabbleMediaSession *session = GABBLE_MEDIA_SESSION (object); - GabbleMediaSessionPrivate *priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session); - - switch (property_id) { - case PROP_CONNECTION: - g_value_set_object (value, priv->conn); - break; - case PROP_MEDIA_CHANNEL: - g_value_set_object (value, priv->channel); - break; - case PROP_OBJECT_PATH: - g_value_set_string (value, priv->object_path); - break; - case PROP_SESSION_ID: - g_value_set_string (value, priv->id); - break; - case PROP_INITIATOR: - g_value_set_uint (value, session->initiator); - break; - case PROP_PEER: - g_value_set_uint (value, priv->peer); - break; - case PROP_PEER_RESOURCE: - g_value_set_string (value, priv->peer_resource); - break; - case PROP_STATE: - g_value_set_uint (value, priv->state); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } -} - -static void session_state_changed (GabbleMediaSession *session, - JingleSessionState prev_state, - JingleSessionState new_state); - -static void -gabble_media_session_set_property (GObject *object, - guint property_id, - const GValue *value, - GParamSpec *pspec) -{ - GabbleMediaSession *session = GABBLE_MEDIA_SESSION (object); - GabbleMediaSessionPrivate *priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session); - JingleSessionState prev_state; - - switch (property_id) { - case PROP_CONNECTION: - priv->conn = g_value_get_object (value); - break; - case PROP_MEDIA_CHANNEL: - priv->channel = g_value_get_object (value); - break; - case PROP_OBJECT_PATH: - g_free (priv->object_path); - priv->object_path = g_value_dup_string (value); - break; - case PROP_SESSION_ID: - g_free (priv->id); - priv->id = g_value_dup_string (value); - break; - case PROP_INITIATOR: - session->initiator = g_value_get_uint (value); - break; - case PROP_PEER: - priv->peer = g_value_get_uint (value); - break; - case PROP_PEER_RESOURCE: - g_free (priv->peer_resource); - priv->peer_resource = g_value_dup_string (value); - break; - case PROP_STATE: - prev_state = priv->state; - priv->state = g_value_get_uint (value); - - if (priv->state == JS_STATE_ENDED) - g_assert (priv->terminated); - - if (priv->state != prev_state) - session_state_changed (session, prev_state, priv->state); - - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } -} - -static void gabble_media_session_dispose (GObject *object); -static void gabble_media_session_finalize (GObject *object); - -static void -gabble_media_session_class_init (GabbleMediaSessionClass *gabble_media_session_class) -{ - GObjectClass *object_class = G_OBJECT_CLASS (gabble_media_session_class); - GParamSpec *param_spec; - - g_type_class_add_private (gabble_media_session_class, - sizeof (GabbleMediaSessionPrivate)); - - object_class->constructor = gabble_media_session_constructor; - - object_class->get_property = gabble_media_session_get_property; - object_class->set_property = gabble_media_session_set_property; - - object_class->dispose = gabble_media_session_dispose; - object_class->finalize = gabble_media_session_finalize; - - param_spec = g_param_spec_object ("connection", "GabbleConnection object", - "Gabble connection object that owns this " - "media session's channel.", - GABBLE_TYPE_CONNECTION, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | - G_PARAM_STATIC_NICK | - G_PARAM_STATIC_BLURB); - g_object_class_install_property (object_class, PROP_CONNECTION, param_spec); - - param_spec = g_param_spec_object ("media-channel", - "GabbleMediaChannel object", - "Gabble media channel object that owns this media session object.", - GABBLE_TYPE_MEDIA_CHANNEL, - G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_NICK | - G_PARAM_STATIC_BLURB); - g_object_class_install_property (object_class, PROP_MEDIA_CHANNEL, - param_spec); - - param_spec = g_param_spec_string ("object-path", "D-Bus object path", - "The D-Bus object path used for this " - "object on the bus.", - NULL, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | - G_PARAM_STATIC_NAME | - G_PARAM_STATIC_BLURB); - g_object_class_install_property (object_class, PROP_OBJECT_PATH, param_spec); - - param_spec = g_param_spec_string ("session-id", "Session ID", - "A unique session identifier used " - "throughout all communication.", - NULL, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | - G_PARAM_STATIC_NAME | - G_PARAM_STATIC_BLURB); - g_object_class_install_property (object_class, PROP_SESSION_ID, param_spec); - - param_spec = g_param_spec_uint ("initiator", "Session initiator", - "An enum signifying which end initiated " - "the session.", - INITIATOR_LOCAL, - INITIATOR_REMOTE, - INITIATOR_LOCAL, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | - G_PARAM_STATIC_NAME | - G_PARAM_STATIC_BLURB); - g_object_class_install_property (object_class, PROP_INITIATOR, param_spec); - - param_spec = g_param_spec_uint ("peer", "Session peer", - "The TpHandle representing the contact " - "with whom this session communicates.", - 0, G_MAXUINT32, 0, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | - G_PARAM_STATIC_NAME | - G_PARAM_STATIC_BLURB); - g_object_class_install_property (object_class, PROP_PEER, param_spec); - - param_spec = g_param_spec_string ("peer-resource", - "Session peer's resource", - "The resource of the contact " - "with whom this session communicates, " - "if applicable", - NULL, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_WRITABLE | - G_PARAM_STATIC_NAME | - G_PARAM_STATIC_BLURB); - g_object_class_install_property (object_class, PROP_PEER_RESOURCE, - param_spec); - - param_spec = g_param_spec_uint ("state", "Session state", - "The current state that the session is in.", - 0, G_MAXUINT32, 0, - G_PARAM_READWRITE | - G_PARAM_STATIC_NAME | - G_PARAM_STATIC_BLURB); - g_object_class_install_property (object_class, PROP_STATE, param_spec); - - signals[STREAM_ADDED] = - g_signal_new ("stream-added", - G_OBJECT_CLASS_TYPE (gabble_media_session_class), - G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, - 0, - NULL, NULL, - g_cclosure_marshal_VOID__OBJECT, - G_TYPE_NONE, 1, G_TYPE_OBJECT); - - signals[TERMINATED] = - g_signal_new ("terminated", - G_OBJECT_CLASS_TYPE (gabble_media_session_class), - G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, - 0, - NULL, NULL, - gabble_marshal_VOID__UINT_UINT, - G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT); -} - -static void -gabble_media_session_dispose (GObject *object) -{ - GabbleMediaSession *self = GABBLE_MEDIA_SESSION (object); - GabbleMediaSessionPrivate *priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (self); - guint i; - - DEBUG ("called"); - - if (priv->dispose_has_run) - return; - - priv->dispose_has_run = TRUE; - - _gabble_media_session_terminate (self, INITIATOR_LOCAL, - TP_CHANNEL_GROUP_CHANGE_REASON_NONE); - - if (priv->timer_id != 0) - g_source_remove (priv->timer_id); - - if (priv->streams != NULL) - { - for (i = 0; i < priv->streams->len; i++) - g_object_unref (g_ptr_array_index (priv->streams, i)); - g_ptr_array_free (priv->streams, TRUE); - priv->streams = NULL; - } - - for (i = 0; i < priv->remove_requests->len; i++) - g_ptr_array_free (g_ptr_array_index (priv->remove_requests, i), TRUE); - g_ptr_array_free (priv->remove_requests, TRUE); - priv->remove_requests = NULL; - - if (G_OBJECT_CLASS (gabble_media_session_parent_class)->dispose) - G_OBJECT_CLASS (gabble_media_session_parent_class)->dispose (object); -} - -static void -gabble_media_session_finalize (GObject *object) -{ - GabbleMediaSession *self = GABBLE_MEDIA_SESSION (object); - GabbleMediaSessionPrivate *priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (self); - - g_free (priv->id); - g_free (priv->object_path); - g_free (priv->peer_resource); - G_OBJECT_CLASS (gabble_media_session_parent_class)->finalize (object); -} - - -/** - * gabble_media_session_error - * - * Implements D-Bus method Error - * on interface org.freedesktop.Telepathy.Media.SessionHandler - */ -static void -gabble_media_session_error (TpSvcMediaSessionHandler *iface, - guint errno, - const gchar *message, - DBusGMethodInvocation *context) -{ - GabbleMediaSession *self = GABBLE_MEDIA_SESSION (iface); - GabbleMediaSessionPrivate *priv; - GPtrArray *tmp; - guint i; - - g_assert (GABBLE_IS_MEDIA_SESSION (self)); - - priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (self); - - GMS_DEBUG_INFO (self, "Media.SessionHandler::Error called, error %u (%s) -- " - "emitting error on each stream", errno, message); - - if (priv->state == JS_STATE_ENDED) - { - tp_svc_media_session_handler_return_from_error (context); - return; - } - else if (priv->state == JS_STATE_PENDING_CREATED) - { - /* shortcut to prevent sending remove actions if we haven't sent an - * initiate yet */ - g_object_set (self, "state", JS_STATE_ENDED, NULL); - tp_svc_media_session_handler_return_from_error (context); - return; - } - - g_assert (priv->streams != NULL); - - tmp = priv->streams; - priv->streams = NULL; - - for (i = 0; i < tmp->len; i++) - { - GabbleMediaStream *stream = g_ptr_array_index (priv->streams, i); - - gabble_media_stream_error (stream, errno, message, NULL); - } - - g_ptr_array_free (tmp, TRUE); - - tp_svc_media_session_handler_return_from_error (context); -} - - -/** - * gabble_media_session_ready - * - * Implements D-Bus method Ready - * on interface org.freedesktop.Telepathy.Media.SessionHandler - */ -static void -gabble_media_session_ready (TpSvcMediaSessionHandler *iface, - DBusGMethodInvocation *context) -{ - GabbleMediaSession *self = GABBLE_MEDIA_SESSION (iface); - GabbleMediaSessionPrivate *priv; - guint i; - - g_assert (GABBLE_IS_MEDIA_SESSION (self)); - - priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (self); - - priv->ready = TRUE; - - for (i = 0; i < priv->streams->len; i++) - _emit_new_stream (self, g_ptr_array_index (priv->streams, i)); - - tp_svc_media_session_handler_return_from_ready (context); -} - - -static gboolean -_handle_create (GabbleMediaSession *session, - LmMessage *message, - LmMessageNode *content_node, - const gchar *stream_name, - GabbleMediaStream *stream, - LmMessageNode *desc_node, - LmMessageNode *trans_node, - GError **error) -{ - GabbleMediaSessionPrivate *priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session); - GabbleMediaSessionMode session_mode; - TpMediaStreamType stream_type; - gboolean override_existing = FALSE; - - if ((priv->state == JS_STATE_PENDING_CREATED) && - (session->initiator == INITIATOR_LOCAL)) - { - DEBUG ("we're trying to call ourselves, rejecting with busy"); - _gabble_media_session_terminate (session, INITIATOR_REMOTE, - TP_CHANNEL_GROUP_CHANGE_REASON_BUSY); - return FALSE; - } - - - if (stream != NULL) - { - /* streams added by the session initiator may replace similarly-named - * streams which we are trying to add (but havn't had acknowledged) */ - if (stream->signalling_state < STREAM_SIG_STATE_ACKNOWLEDGED) - { - if (session->initiator == INITIATOR_REMOTE) - { - override_existing = TRUE; - } - else - { - g_set_error (error, GABBLE_XMPP_ERROR, XMPP_ERROR_CONFLICT, - "session initiator is creating a stream named \"%s\" " - "already", stream_name); - return FALSE; - } - } - else - { - g_set_error (error, GABBLE_XMPP_ERROR, XMPP_ERROR_CONFLICT, - "can't create new stream called \"%s\", it already exists, " - "rejecting", stream_name); - return FALSE; - } - } - - if (desc_node == NULL) - { - g_set_error (error, GABBLE_XMPP_ERROR, XMPP_ERROR_BAD_REQUEST, - "unable to create stream without a content description"); - return FALSE; - } - - if (lm_message_node_has_namespace (desc_node, - NS_GOOGLE_SESSION_PHONE, NULL)) - { - session_mode = MODE_GOOGLE; - stream_type = TP_MEDIA_STREAM_TYPE_AUDIO; - } - else if (lm_message_node_has_namespace (desc_node, - NS_JINGLE_DESCRIPTION_AUDIO, NULL)) - { - session_mode = MODE_JINGLE; - stream_type = TP_MEDIA_STREAM_TYPE_AUDIO; - } - else if (lm_message_node_has_namespace (desc_node, - NS_JINGLE_DESCRIPTION_VIDEO, NULL)) - { - session_mode = MODE_JINGLE; - stream_type = TP_MEDIA_STREAM_TYPE_VIDEO; - } - else - { - g_set_error (error, GABBLE_XMPP_ERROR, - XMPP_ERROR_JINGLE_UNSUPPORTED_CONTENT, - "refusing to create stream for unsupported content description"); - return FALSE; - } - - /* MODE_GOOGLE is allowed to have a null transport node */ - if (session_mode == MODE_JINGLE && trans_node == NULL) - { - g_set_error (error, GABBLE_XMPP_ERROR, - XMPP_ERROR_JINGLE_UNSUPPORTED_TRANSPORT, - "refusing to create stream for unsupported transport"); - return FALSE; - } - - if (session_mode != priv->mode) - { - if (priv->streams->len > 0) - { - g_set_error (error, GABBLE_XMPP_ERROR, XMPP_ERROR_UNEXPECTED_REQUEST, - "refusing to change mode because streams already exist"); - return FALSE; - } - else - { - GMS_DEBUG_INFO (session, "setting session mode to %s", - session_mode == MODE_GOOGLE ? "google" : "jingle"); - priv->mode = session_mode; - } - } - - if (override_existing) - { - GMS_DEBUG_INFO (session, "removing our unacknowledged stream \"%s\" " - "in favour of the session initiator's", stream_name); - - /* disappear this stream */ - destroy_media_stream (session, stream); - - stream = NULL; - } - - if (priv->streams->len == MAX_STREAMS) - { - g_set_error (error, GABBLE_XMPP_ERROR, XMPP_ERROR_RESOURCE_CONSTRAINT, - "refusing to create more than " G_STRINGIFY (MAX_STREAMS) - " streams"); - return FALSE; - } - - stream = create_media_stream (session, stream_name, INITIATOR_REMOTE, - stream_type); - - /* set the signalling state to ACKNOWLEDGED */ - g_object_set (stream, - "signalling-state", STREAM_SIG_STATE_ACKNOWLEDGED, - NULL); - - /* for jingle streams, set the direction to none, so that the - * direction handler adds the right flags */ - if (priv->mode == MODE_JINGLE) - g_object_set (stream, - "combined-direction", TP_MEDIA_STREAM_DIRECTION_NONE, - NULL); - - return TRUE; -} - - -static TpMediaStreamDirection -_senders_to_direction (GabbleMediaSession *session, - const gchar *senders) -{ - TpMediaStreamDirection ret = TP_MEDIA_STREAM_DIRECTION_NONE; - - if (!tp_strdiff (senders, "initiator")) - { - if (session->initiator == INITIATOR_LOCAL) - ret = TP_MEDIA_STREAM_DIRECTION_SEND; - else - ret = TP_MEDIA_STREAM_DIRECTION_RECEIVE; - } - else if (!tp_strdiff (senders, "responder")) - { - if (session->initiator == INITIATOR_REMOTE) - ret = TP_MEDIA_STREAM_DIRECTION_SEND; - else - ret = TP_MEDIA_STREAM_DIRECTION_RECEIVE; - } - else if (!tp_strdiff (senders, "both")) - { - ret = TP_MEDIA_STREAM_DIRECTION_BIDIRECTIONAL; - } - - return ret; -} - -static gboolean -_handle_direction (GabbleMediaSession *session, - LmMessage *message, - LmMessageNode *content_node, - const gchar *stream_name, - GabbleMediaStream *stream, - LmMessageNode *desc_node, - LmMessageNode *trans_node, - GError **error) -{ - GabbleMediaSessionPrivate *priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session); - const gchar *senders; - CombinedStreamDirection new_combined_dir; - TpMediaStreamDirection requested_dir, current_dir; - TpMediaStreamPendingSend pending_send; - - if (priv->mode == MODE_GOOGLE) - return TRUE; - - requested_dir = TP_MEDIA_STREAM_DIRECTION_BIDIRECTIONAL; - - senders = lm_message_node_get_attribute (content_node, "senders"); - if (senders != NULL) - requested_dir = _senders_to_direction (session, senders); - - if (requested_dir == TP_MEDIA_STREAM_DIRECTION_NONE) - { - g_set_error (error, GABBLE_XMPP_ERROR, XMPP_ERROR_BAD_REQUEST, - "received invalid content senders value \"%s\" on stream \"%s\"; " - "rejecting", senders, stream_name); - return FALSE; - } - - current_dir = COMBINED_DIRECTION_GET_DIRECTION (stream->combined_direction); - pending_send = COMBINED_DIRECTION_GET_PENDING_SEND - (stream->combined_direction); - - GMS_DEBUG_INFO (session, "received request for senders \"%s\" on stream " - "\"%s\"", senders, stream_name); - - /* if local sending has been added, remove it, - * and set the pending local send flag */ - if (((current_dir & TP_MEDIA_STREAM_DIRECTION_SEND) == 0) && - ((requested_dir & TP_MEDIA_STREAM_DIRECTION_SEND) != 0)) - { - GMS_DEBUG_INFO (session, "setting pending local send flag"); - requested_dir &= ~TP_MEDIA_STREAM_DIRECTION_SEND; - pending_send |= TP_MEDIA_STREAM_PENDING_LOCAL_SEND; - } - -#if 0 - /* clear any pending remote send */ - if ((pending_send & TP_MEDIA_STREAM_PENDING_REMOTE_SEND) != 0) - { - GMS_DEBUG_INFO (session, "setting pending local send flag"); - pending_send &= ~TP_MEDIA_STREAM_PENDING_REMOTE_SEND; - } -#endif - - /* make any necessary changes */ - new_combined_dir = MAKE_COMBINED_DIRECTION (requested_dir, pending_send); - if (new_combined_dir != stream->combined_direction) - { - g_object_set (stream, "combined-direction", new_combined_dir, NULL); - _gabble_media_stream_update_sending (stream, FALSE); - } - - return TRUE; -} - - -static gboolean -_handle_accept (GabbleMediaSession *session, - LmMessage *message, - LmMessageNode *content_node, - const gchar *stream_name, - GabbleMediaStream *stream, - LmMessageNode *desc_node, - LmMessageNode *trans_node, - GError **error) -{ - g_object_set (stream, "playing", TRUE, NULL); - - _gabble_media_stream_update_sending (stream, TRUE); - - return TRUE; -} - - -static gboolean -_handle_codecs (GabbleMediaSession *session, - LmMessage *message, - LmMessageNode *content_node, - const gchar *stream_name, - GabbleMediaStream *stream, - LmMessageNode *desc_node, - LmMessageNode *trans_node, - GError **error) -{ - if (desc_node == NULL) - { - g_set_error (error, GABBLE_XMPP_ERROR, XMPP_ERROR_BAD_REQUEST, - "unable to handle codecs without a content description node"); - return FALSE; - } - - if (!_gabble_media_stream_post_remote_codecs (stream, message, desc_node, - error)) - return FALSE; - - return TRUE; -} - - -static gboolean -_handle_candidates (GabbleMediaSession *session, - LmMessage *message, - LmMessageNode *content_node, - const gchar *stream_name, - GabbleMediaStream *stream, - LmMessageNode *desc_node, - LmMessageNode *trans_node, - GError **error) -{ - GabbleMediaSessionPrivate *priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session); - - if (trans_node == NULL) - { - if (priv->mode == MODE_GOOGLE) - { - trans_node = content_node; - } - else - { - g_set_error (error, GABBLE_XMPP_ERROR, XMPP_ERROR_BAD_REQUEST, - "unable to handle candidates without a transport node"); - return FALSE; - } - } - - if (!_gabble_media_stream_post_remote_candidates (stream, message, - trans_node, error)) - return FALSE; - - return TRUE; -} - -static guint -_count_non_removing_streams (GabbleMediaSession *session) -{ - GabbleMediaSessionPrivate *priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session); - guint i, ret = 0; - - for (i = 0; i < priv->streams->len; i++) - { - GabbleMediaStream *stream = g_ptr_array_index (priv->streams, i); - - if (stream->signalling_state < STREAM_SIG_STATE_REMOVING) - ret++; - } - - return ret; -} - -static gboolean -_handle_remove (GabbleMediaSession *session, - LmMessage *message, - LmMessageNode *content_node, - const gchar *stream_name, - GabbleMediaStream *stream, - LmMessageNode *desc_node, - LmMessageNode *trans_node, - GError **error) -{ - /* reducing a session to contain 0 streams is invalid; instead the peer - * should terminate the session. I guess we'll do it for them... */ - if (_count_non_removing_streams (session) == 1) - { - g_set_error (error, GABBLE_XMPP_ERROR, XMPP_ERROR_BAD_REQUEST, - "unable to remove the last stream in a Jingle call"); - return FALSE; - } - - /* close the stream */ - destroy_media_stream (session, stream); - - return TRUE; -} - - -static gboolean -_handle_terminate (GabbleMediaSession *session, - LmMessage *message, - LmMessageNode *content_node, - const gchar *stream_name, - GabbleMediaStream *stream, - LmMessageNode *desc_node, - LmMessageNode *trans_node, - GError **error) -{ - DEBUG ("called for %s", stream_name); - - _gabble_media_session_terminate (session, INITIATOR_REMOTE, - TP_CHANNEL_GROUP_CHANGE_REASON_NONE); - - return TRUE; -} - - -typedef gboolean (*StreamHandlerFunc)(GabbleMediaSession *session, - LmMessage *message, - LmMessageNode *content_node, - const gchar *stream_name, - GabbleMediaStream *stream, - LmMessageNode *desc_node, - LmMessageNode *trans_node, - GError **error); - -typedef struct _Handler Handler; - -struct _Handler { - const gchar *actions[3]; - JingleSessionState min_allowed_state; - JingleSessionState max_allowed_state; - StreamHandlerFunc stream_handlers[4]; - JingleSessionState new_state; -}; - -static Handler handlers[] = { - { - { "initiate", "session-initiate", NULL }, - JS_STATE_PENDING_CREATED, - JS_STATE_PENDING_CREATED, - { _handle_create, _handle_direction, _handle_codecs, NULL }, - JS_STATE_PENDING_INITIATED - }, - { - { "accept", "session-accept", NULL }, - JS_STATE_PENDING_INITIATED, - JS_STATE_PENDING_INITIATED, - { _handle_direction, _handle_codecs, _handle_accept, NULL }, - JS_STATE_ACTIVE - }, - { - { "reject", NULL }, - JS_STATE_PENDING_INITIATE_SENT, - JS_STATE_PENDING_INITIATED, - { _handle_terminate, NULL }, - JS_STATE_INVALID - }, - { - { "terminate", "session-terminate", NULL }, - JS_STATE_PENDING_INITIATED, - JS_STATE_ENDED, - { _handle_terminate, NULL }, - JS_STATE_INVALID - }, - { - { "candidates", "transport-info", NULL }, - JS_STATE_PENDING_INITIATED, - JS_STATE_ACTIVE, - { _handle_candidates, NULL }, - JS_STATE_INVALID - }, - { - { "content-add", NULL }, - JS_STATE_ACTIVE, - JS_STATE_ACTIVE, - { _handle_create, _handle_direction, _handle_codecs, NULL }, - JS_STATE_INVALID, - }, - { - { "content-modify", NULL }, - JS_STATE_PENDING_INITIATED, - JS_STATE_ACTIVE, - { _handle_direction, NULL }, - JS_STATE_INVALID - }, - { - { "content-accept", NULL }, - JS_STATE_PENDING_INITIATED, - JS_STATE_ACTIVE, - { _handle_direction, _handle_codecs, _handle_accept, NULL }, - JS_STATE_INVALID - }, - { - { "content-remove", "content-decline", NULL }, - JS_STATE_PENDING_INITIATED, - JS_STATE_ACTIVE, - { _handle_remove, NULL }, - JS_STATE_INVALID - }, - { - { NULL }, - JS_STATE_INVALID, - JS_STATE_INVALID, - { NULL }, - JS_STATE_INVALID - } -}; - - -static gboolean -_call_handlers_on_stream (GabbleMediaSession *session, - LmMessage *message, - LmMessageNode *content_node, - const gchar *stream_name, - JingleInitiator stream_creator, - StreamHandlerFunc *func, - GError **error) -{ - GabbleMediaStream *stream = NULL; - LmMessageNode *desc_node = NULL, *trans_node = NULL; - StreamHandlerFunc *tmp; - gboolean stream_created = FALSE; - - if (content_node != NULL) - { - desc_node = lm_message_node_get_child (content_node, "description"); - - trans_node = lm_message_node_get_child_with_namespace (content_node, - "transport", NS_GOOGLE_TRANSPORT_P2P); - } - - for (tmp = func; *tmp != NULL; tmp++) - { - /* handlers may create the stream */ - if (stream == NULL && stream_name != NULL) - stream = _lookup_stream_by_name_and_initiator (session, stream_name, - stream_creator); - - /* the create handler is able to check whether or not the stream - * exists, and act accordingly (sometimes it will replace an existing - * stream, sometimes it will reject). the termination handler - * also requires no stream to do it's job. */ - if (*tmp != _handle_create && *tmp != _handle_terminate) - { - /* all other handlers require the stream to exist */ - if (stream == NULL) - { - const gchar *created = ""; - - if (stream_creator == INITIATOR_LOCAL) - created = "locally-created "; - else if (stream_creator == INITIATOR_REMOTE) - created = "remotely-created "; - - g_set_error (error, GABBLE_XMPP_ERROR, XMPP_ERROR_ITEM_NOT_FOUND, - "unable to handle action for unknown %sstream \"%s\" ", - created, stream_name); - - return FALSE; - } - else - { - /* don't do anything with actions on streams which have not been - * acknowledged, or that we're trying to remove, to deal with - * adding/removing race conditions (actions sent by the other end - * before they're aware that we've added or removed a stream) */ - if (stream->signalling_state != STREAM_SIG_STATE_ACKNOWLEDGED) - { - GMS_DEBUG_WARNING (session, "ignoring action because stream " - "%s is in state %d, not ACKNOWLEDGED", stream->name, - stream->signalling_state); - return TRUE; - } - } - } - - if (!(*tmp) (session, message, content_node, stream_name, stream, - desc_node, trans_node, error)) - { - /* if we successfully created the stream but failed to do something - * with it later, remove it */ - if (stream_created) - destroy_media_stream (session, stream); - - return FALSE; - } - - if (*tmp == _handle_create) - { - stream_created = TRUE; - /* force a stream lookup after the create handler, even if we - * already had one (it has replacement semantics in certain - * situations) */ - stream = NULL; - } - } - - return TRUE; -} - - -static JingleInitiator -_creator_to_initiator (GabbleMediaSession *session, const gchar *creator) -{ - if (!tp_strdiff (creator, "initiator")) - { - if (session->initiator == INITIATOR_LOCAL) - return INITIATOR_LOCAL; - else - return INITIATOR_REMOTE; - } - else if (!tp_strdiff (creator, "responder")) - { - if (session->initiator == INITIATOR_LOCAL) - return INITIATOR_REMOTE; - else - return INITIATOR_LOCAL; - } - else - return INITIATOR_INVALID; -} - - -static gboolean -_call_handlers_on_streams (GabbleMediaSession *session, - LmMessage *message, - LmMessageNode *session_node, - StreamHandlerFunc *func, - GError **error) -{ - LmMessageNode *content_node; - - if (lm_message_node_has_namespace (session_node, NS_GOOGLE_SESSION, NULL)) - return _call_handlers_on_stream (session, message, session_node, - GTALK_STREAM_NAME, INITIATOR_INVALID, func, error); - - if (session_node->children == NULL) - return _call_handlers_on_stream (session, message, NULL, NULL, - INITIATOR_INVALID, func, error); - - for (content_node = session_node->children; - NULL != content_node; - content_node = content_node->next) - { - const gchar *stream_name, *stream_creator; - JingleInitiator stream_initiator; - - if (tp_strdiff (content_node->name, "content")) - continue; - - stream_name = lm_message_node_get_attribute (content_node, "name"); - - if (stream_name == NULL) - { - g_set_error (error, GABBLE_XMPP_ERROR, XMPP_ERROR_BAD_REQUEST, - "rejecting content node with no name"); - return FALSE; - } - - stream_creator = lm_message_node_get_attribute (content_node, "creator"); - stream_initiator = _creator_to_initiator (session, stream_creator); - - /* we allow NULL creator to mean INITIATOR_INVALID for backwards - * compatibility with clients that don't put a creator attribute in */ - if (stream_creator != NULL && stream_initiator == INITIATOR_INVALID) - { - g_set_error (error, GABBLE_XMPP_ERROR, XMPP_ERROR_BAD_REQUEST, - "rejecting content node with invalid creators value"); - return FALSE; - } - - if (!_call_handlers_on_stream (session, message, content_node, - stream_name, stream_initiator, func, error)) - return FALSE; - } - - return TRUE; -} - - -gboolean -_gabble_media_session_handle_action (GabbleMediaSession *session, - LmMessage *message, - LmMessageNode *session_node, - const gchar *action, - GError **error) -{ - GabbleMediaSessionPrivate *priv; - StreamHandlerFunc *funcs = NULL; - JingleSessionState new_state = JS_STATE_INVALID; - Handler *i; - const gchar **tmp; - - g_assert (GABBLE_IS_MEDIA_SESSION (session)); - - priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session); - - GMS_DEBUG_INFO (session, "got jingle session action \"%s\" from peer", - action); - - /* do the state machine dance */ - - /* search the table of handlers for the action */ - for (i = handlers; NULL != i->actions[0]; i++) - { - for (tmp = i->actions; NULL != *tmp; tmp++) - if (0 == strcmp (*tmp, action)) - break; - - if (NULL == *tmp) - continue; - - /* if we're outside the allowable states for this action, return an error - * immediately */ - if (priv->state < i->min_allowed_state || - priv->state > i->max_allowed_state) - { - g_set_error (error, GABBLE_XMPP_ERROR, - XMPP_ERROR_JINGLE_OUT_OF_ORDER, - "action \"%s\" not allowed in current state", action); - goto ERROR; - } - - funcs = i->stream_handlers; - new_state = i->new_state; - - break; - } - - /* pointer is not NULL if we found a matching action */ - if (NULL == funcs) - { - g_set_error (error, GABBLE_XMPP_ERROR, - XMPP_ERROR_FEATURE_NOT_IMPLEMENTED, "action \"%s\" not implemented", - action); - goto ERROR; - } - - /* call handlers if there are any (NULL-terminated array) */ - if (NULL != *funcs) - { - if (!_call_handlers_on_streams (session, message, session_node, funcs, - error)) - { - if (*error == NULL) - g_set_error (error, GABBLE_XMPP_ERROR, XMPP_ERROR_BAD_REQUEST, - "unknown error encountered with action \"%s\"", - action); - - goto ERROR; - } - } - - /* acknowledge the IQ before changing the state because the new state - * could perform some actions which the other end will only accept - * if this action has been acknowledged */ - _gabble_connection_acknowledge_set_iq (priv->conn, message); - - /* if the action specified a new state to go to, set it */ - if (JS_STATE_INVALID != new_state) - g_object_set (session, "state", new_state, NULL); - - return TRUE; - -ERROR: - g_assert (error != NULL); - GMS_DEBUG_ERROR (session, (*error)->message); - return FALSE; -} - -static gboolean -timeout_session (gpointer data) -{ - GabbleMediaSession *session = data; - - DEBUG ("session timed out"); - - _gabble_media_session_terminate (session, INITIATOR_LOCAL, - TP_CHANNEL_GROUP_CHANGE_REASON_ERROR); - - return FALSE; -} - -static void do_content_add (GabbleMediaSession *, GabbleMediaStream *); - -void -_add_ready_new_streams (GabbleMediaSession *session) -{ - GabbleMediaSessionPrivate *priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session); - guint i; - - for (i = 0; i < priv->streams->len; i++) - { - GabbleMediaStream *stream = g_ptr_array_index (priv->streams, i); - - GMS_DEBUG_DUMP (session, "pondering accept-time add for stream: %s, got " - "local codecs: %s, initiator: %s, signalling state: %d", - stream->name, stream->got_local_codecs ? "true" : "false", - stream->initiator == INITIATOR_LOCAL ? "local" : "remote", - stream->signalling_state); - - if (stream->got_local_codecs == FALSE) - continue; - - if (stream->initiator == INITIATOR_REMOTE) - continue; - - if (stream->signalling_state > STREAM_SIG_STATE_NEW) - continue; - - do_content_add (session, stream); - } -} - -static void -session_state_changed (GabbleMediaSession *session, - JingleSessionState prev_state, - JingleSessionState new_state) -{ - GabbleMediaSessionPrivate *priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session); - - GMS_DEBUG_EVENT (session, "state changed from %s to %s", - session_states[prev_state].name, - session_states[new_state].name); - - /* - * If the state goes from CREATED to INITIATED (which means the remote - * end initiated), set the timer. If, OTOH, we're the end which just sent an - * initiate, set the timer. - */ - if ((prev_state == JS_STATE_PENDING_CREATED && - new_state == JS_STATE_PENDING_INITIATED) || - (new_state == JS_STATE_PENDING_INITIATE_SENT)) - { - priv->timer_id = - g_timeout_add (DEFAULT_SESSION_TIMEOUT, timeout_session, session); - } - else if (new_state == JS_STATE_ACTIVE) - { - g_source_remove (priv->timer_id); - priv->timer_id = 0; - - /* signal any streams to the remote end which were added locally & - * became ready before the session was accepted, so haven't been - * mentioned yet */ - _add_ready_new_streams (session); - } -} - -static void -_mark_local_streams_sent (GabbleMediaSession *session) -{ - GabbleMediaSessionPrivate *priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session); - guint i; - - for (i = 0; i < priv->streams->len; i++) - { - GabbleMediaStream *stream = g_ptr_array_index (priv->streams, i); - - if (stream->initiator == INITIATOR_REMOTE) - continue; - - GMS_DEBUG_INFO (session, "marking local stream %s as signalled", - stream->name); - - g_object_set (stream, "signalling-state", STREAM_SIG_STATE_SENT, NULL); - } -} - -static void -_mark_local_streams_acked (GabbleMediaSession *session) -{ - GabbleMediaSessionPrivate *priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session); - guint i; - - for (i = 0; i < priv->streams->len; i++) - { - GabbleMediaStream *stream = g_ptr_array_index (priv->streams, i); - - if (stream->initiator == INITIATOR_REMOTE) - continue; - - if (stream->signalling_state != STREAM_SIG_STATE_SENT) - continue; - - GMS_DEBUG_INFO (session, "marking local stream %s as acknowledged", - stream->name); - - g_object_set (stream, - "signalling-state", STREAM_SIG_STATE_ACKNOWLEDGED, - NULL); - } -} - -static void -_set_remote_streams_playing (GabbleMediaSession *session) -{ - GabbleMediaSessionPrivate *priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session); - guint i; - - for (i = 0; i < priv->streams->len; i++) - { - GabbleMediaStream *stream = g_ptr_array_index (priv->streams, i); - - if (stream->initiator == INITIATOR_LOCAL) - continue; - - GMS_DEBUG_INFO (session, "setting remote stream %s as playing", - stream->name); - - g_object_set (stream, "playing", TRUE, NULL); - } -} - -static const gchar *_direction_to_senders (GabbleMediaSession *, - TpMediaStreamDirection); - -static void -_add_content_descriptions_one (GabbleMediaSession *session, - GabbleMediaStream *stream, - LmMessageNode *session_node) -{ - GabbleMediaSessionPrivate *priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session); - LmMessageNode *content_node; - - if (priv->mode == MODE_GOOGLE) - { - content_node = session_node; - } - else - { - TpMediaStreamDirection direction; - TpMediaStreamPendingSend pending_send; - - content_node = _gabble_media_stream_add_content_node (stream, - session_node); - - direction = COMBINED_DIRECTION_GET_DIRECTION (stream->combined_direction); - pending_send = COMBINED_DIRECTION_GET_PENDING_SEND - (stream->combined_direction); - - /* if we have a pending local send flag set, the signalled (ie understood - * by both ends) direction of the stream is assuming that we are actually - * sending, so we should OR that into the direction before deciding what - * to signal the stream with. we don't need to consider pending remote - * send because it doesn't happen in Jingle */ - - if ((pending_send & TP_MEDIA_STREAM_PENDING_LOCAL_SEND) != 0) - direction |= TP_MEDIA_STREAM_DIRECTION_SEND; - - if (direction != TP_MEDIA_STREAM_DIRECTION_BIDIRECTIONAL) - { - const gchar *senders; - senders = _direction_to_senders (session, direction); - lm_message_node_set_attribute (content_node, "senders", senders); - } - } - - _gabble_media_stream_content_node_add_description (stream, content_node); - - _gabble_media_stream_content_node_add_transport (stream, content_node); -} - -static void -_add_content_descriptions (GabbleMediaSession *session, - LmMessageNode *session_node, - JingleInitiator stream_initiator) -{ - GabbleMediaSessionPrivate *priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session); - guint i; - - for (i = 0; i < priv->streams->len; i++) - { - GabbleMediaStream *stream = g_ptr_array_index (priv->streams, i); - - if (stream->initiator != stream_initiator) - { - GMS_DEBUG_INFO (session, - "not adding content description for %s stream %s", - stream->initiator == INITIATOR_LOCAL ? "local" : "remote", - stream->name); - continue; - } - - _add_content_descriptions_one (session, stream, session_node); - } -} - -static LmHandlerResult -accept_msg_reply_cb (GabbleConnection *conn, - LmMessage *sent_msg, - LmMessage *reply_msg, - GObject *object, - gpointer user_data) -{ - GabbleMediaSession *session = GABBLE_MEDIA_SESSION (object); - GabbleMediaSessionPrivate *priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session); - guint i; - - MSG_REPLY_CB_END_SESSION_IF_NOT_SUCCESSFUL (session, "accept failed"); - - for (i = 0; i < priv->streams->len; i++) - { - GabbleMediaStream *stream = g_ptr_array_index (priv->streams, i); - - if (stream->initiator == INITIATOR_LOCAL) - continue; - - _gabble_media_stream_update_sending (stream, TRUE); - } - - g_object_set (session, "state", JS_STATE_ACTIVE, NULL); - - return LM_HANDLER_RESULT_REMOVE_MESSAGE; -} - -static gboolean -_stream_not_ready_for_accept (GabbleMediaSession *session, - GabbleMediaStream *stream) -{ - /* locally initiated streams shouldn't delay acceptance */ - if (stream->initiator == INITIATOR_LOCAL) - return FALSE; - - if (!stream->got_local_codecs) - { - GMS_DEBUG_INFO (session, "stream %s does not yet have local codecs", - stream->name); - - return TRUE; - } - - if (stream->connection_state != TP_MEDIA_STREAM_STATE_CONNECTED) - { - GMS_DEBUG_INFO (session, "stream %s is not yet connected", stream->name); - - return TRUE; - } - - return FALSE; -} - -static void -try_session_accept (GabbleMediaSession *session) -{ - GabbleMediaSessionPrivate *priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session); - LmMessage *msg; - LmMessageNode *session_node; - const gchar *action; - guint i; - - if (priv->state < JS_STATE_ACTIVE && !priv->locally_accepted) - { - GMS_DEBUG_INFO (session, "not sending accept yet, waiting for local " - "user to accept call"); - return; - } - - for (i = 0; i < priv->streams->len; i++) - { - GabbleMediaStream *stream = g_ptr_array_index (priv->streams, i); - - if (_stream_not_ready_for_accept (session, stream)) - { - GMS_DEBUG_INFO (session, "not sending accept yet, found a stream " - "which was not yet connected or was missing local codecs"); - return; - } - } - - if (priv->mode == MODE_GOOGLE) - action = "accept"; - else - action = "session-accept"; - - /* construct a session acceptance message */ - msg = _gabble_media_session_message_new (session, action, &session_node); - - /* only accept REMOTE streams; any LOCAL streams were added by the local - * user before accepting and should be signalled after the accept */ - _add_content_descriptions (session, session_node, INITIATOR_REMOTE); - - GMS_DEBUG_INFO (session, "sending jingle session action \"%s\" to peer", - action); - - /* send the final acceptance message */ - _gabble_connection_send_with_reply (priv->conn, msg, accept_msg_reply_cb, - G_OBJECT (session), NULL, NULL); - - lm_message_unref (msg); - - /* set remote streams playing */ - _set_remote_streams_playing (session); - - g_object_set (session, "state", JS_STATE_PENDING_ACCEPT_SENT, NULL); -} - -static LmHandlerResult -content_accept_msg_reply_cb (GabbleConnection *conn, - LmMessage *sent_msg, - LmMessage *reply_msg, - GObject *object, - gpointer user_data) -{ - GabbleMediaSession *session = GABBLE_MEDIA_SESSION (user_data); - GabbleMediaStream *stream = GABBLE_MEDIA_STREAM (object); - - if (lm_message_get_sub_type (reply_msg) != LM_MESSAGE_SUB_TYPE_RESULT) - { - GMS_DEBUG_ERROR (session, "content-accept failed; removing stream"); - NODE_DEBUG (sent_msg->node, "message sent"); - NODE_DEBUG (reply_msg->node, "message reply"); - - _gabble_media_session_remove_streams (session, &stream, 1); - - return LM_HANDLER_RESULT_REMOVE_MESSAGE; - } - - _gabble_media_stream_update_sending (stream, TRUE); - - return LM_HANDLER_RESULT_REMOVE_MESSAGE; -} - -static void -try_content_accept (GabbleMediaSession *session, - GabbleMediaStream *stream) -{ - GabbleMediaSessionPrivate *priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session); - LmMessage *msg; - LmMessageNode *session_node; - - g_assert (priv->state == JS_STATE_ACTIVE); - g_assert (priv->mode == MODE_JINGLE); - - if (_stream_not_ready_for_accept (session, stream)) - { - GMS_DEBUG_INFO (session, "not sending content-accept yet, stream %s " - "is disconnected or missing local codecs", stream->name); - return; - } - - /* send a content acceptance message */ - msg = _gabble_media_session_message_new (session, "content-accept", - &session_node); - - _add_content_descriptions_one (session, stream, session_node); - - GMS_DEBUG_INFO (session, "sending jingle session action \"content-accept\" " - "to peer for stream %s", stream->name); - - _gabble_connection_send_with_reply (priv->conn, msg, - content_accept_msg_reply_cb, G_OBJECT (stream), session, NULL); - - lm_message_unref (msg); - - /* set stream playing */ - g_object_set (stream, "playing", TRUE, NULL); -} - -static LmHandlerResult -initiate_msg_reply_cb (GabbleConnection *conn, - LmMessage *sent_msg, - LmMessage *reply_msg, - GObject *object, - gpointer user_data) -{ - GabbleMediaSession *session = GABBLE_MEDIA_SESSION (object); - - MSG_REPLY_CB_END_SESSION_IF_NOT_SUCCESSFUL (session, "initiate failed"); - - g_object_set (session, "state", JS_STATE_PENDING_INITIATED, NULL); - - /* mark all of the streams that we sent in the initiate as acknowledged */ - _mark_local_streams_acked (session); - - return LM_HANDLER_RESULT_REMOVE_MESSAGE; -} - -static gboolean -_stream_not_ready_for_initiate (GabbleMediaSession *session, - GabbleMediaStream *stream) -{ - if (!stream->got_local_codecs) - { - GMS_DEBUG_INFO (session, "stream %s does not yet have local codecs", - stream->name); - - return TRUE; - } - - return FALSE; -} - -static void -try_session_initiate (GabbleMediaSession *session) -{ - GabbleMediaSessionPrivate *priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session); - LmMessage *msg; - LmMessageNode *session_node; - const gchar *action; - guint i; - - for (i = 0; i < priv->streams->len; i++) - { - GabbleMediaStream *stream = g_ptr_array_index (priv->streams, i); - - if (_stream_not_ready_for_initiate (session, stream)) - { - GMS_DEBUG_INFO (session, "not sending initiate yet, found a stream " - "which was missing local codecs"); - return; - } - } - - if (priv->mode == MODE_GOOGLE) - action = "initiate"; - else - action = "session-initiate"; - - msg = _gabble_media_session_message_new (session, action, &session_node); - - _add_content_descriptions (session, session_node, INITIATOR_LOCAL); - - GMS_DEBUG_INFO (session, "sending jingle action \"%s\" to peer", action); - - _gabble_connection_send_with_reply (priv->conn, msg, initiate_msg_reply_cb, - G_OBJECT (session), NULL, NULL); - - lm_message_unref (msg); - - /* mark local streams as sent (so that eg candidates will be sent) */ - _mark_local_streams_sent (session); - - g_object_set (session, "state", JS_STATE_PENDING_INITIATE_SENT, NULL); -} - -static LmHandlerResult -content_add_msg_reply_cb (GabbleConnection *conn, - LmMessage *sent_msg, - LmMessage *reply_msg, - GObject *object, - gpointer user_data) -{ - GabbleMediaSession *session = GABBLE_MEDIA_SESSION (user_data); - GabbleMediaStream *stream = GABBLE_MEDIA_STREAM (object); - - if (lm_message_get_sub_type (reply_msg) != LM_MESSAGE_SUB_TYPE_RESULT) - { - if (session->initiator == INITIATOR_REMOTE && - stream->signalling_state == STREAM_SIG_STATE_ACKNOWLEDGED) - { - GMS_DEBUG_INFO (session, "ignoring content-add failure, stream has " - "been successfully created by the session initiator"); - } - else - { - GMS_DEBUG_ERROR (session, "content-add failed; removing stream"); - NODE_DEBUG (sent_msg->node, "message sent"); - NODE_DEBUG (reply_msg->node, "message reply"); - - _gabble_media_stream_close (stream); - } - } - else - { - if (stream->signalling_state == STREAM_SIG_STATE_SENT) - { - GMS_DEBUG_INFO (session, "content-add succeeded, marking stream as " - "ACKNOWLEDGED"); - - g_object_set (stream, - "signalling-state", STREAM_SIG_STATE_ACKNOWLEDGED, - NULL); - } - else - { - GMS_DEBUG_INFO (session, "content-add succeeded, but not marking" - "stream as ACKNOWLEDGED, it's in state %d", - stream->signalling_state); - } - } - - return LM_HANDLER_RESULT_REMOVE_MESSAGE; -} - -static void -do_content_add (GabbleMediaSession *session, - GabbleMediaStream *stream) -{ - GabbleMediaSessionPrivate *priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session); - LmMessage *msg; - LmMessageNode *session_node; - - g_assert (priv->state == JS_STATE_ACTIVE); - g_assert (priv->mode == MODE_JINGLE); - - if (_stream_not_ready_for_initiate (session, stream)) - { - GMS_DEBUG_ERROR (session, "trying to send content-add for stream %s " - "but we have no local codecs. what?!", stream->name); - g_assert_not_reached (); - return; - } - - msg = _gabble_media_session_message_new (session, "content-add", - &session_node); - - _add_content_descriptions_one (session, stream, session_node); - - GMS_DEBUG_INFO (session, "sending jingle action \"content-add\" to peer for " - "stream %s", stream->name); - - _gabble_connection_send_with_reply (priv->conn, msg, - content_add_msg_reply_cb, G_OBJECT (stream), session, NULL); - - lm_message_unref (msg); - - /* mark stream as sent */ - g_object_set (stream, "signalling-state", STREAM_SIG_STATE_SENT, NULL); -} - -static void -stream_connection_state_changed_cb (GabbleMediaStream *stream, - GParamSpec *param, - GabbleMediaSession *session) -{ - GabbleMediaSessionPrivate *priv; - - g_assert (GABBLE_IS_MEDIA_SESSION (session)); - - priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session); - - if (stream->connection_state != TP_MEDIA_STREAM_STATE_CONNECTED) - return; - - GMS_DEBUG_INFO (session, "stream %s has gone connected", stream->name); - - if (stream->playing) - { - GMS_DEBUG_INFO (session, "doing nothing, stream is already playing"); - return; - } - - /* after session is active, we do things per-stream with content-* actions */ - if (priv->state < JS_STATE_ACTIVE) - { - /* send a session accept if the session was initiated by the peer */ - if (session->initiator == INITIATOR_REMOTE) - { - try_session_accept (session); - } - else - { - GMS_DEBUG_INFO (session, "session initiated by us, so we're not " - "going to consider sending an accept"); - } - } - else - { - /* send a content accept if the stream was added by the peer */ - if (stream->initiator == INITIATOR_REMOTE) - { - try_content_accept (session, stream); - } - else - { - GMS_DEBUG_INFO (session, "stream added by us, so we're not going " - "to send an accept"); - } - } -} - -static void -stream_got_local_codecs_changed_cb (GabbleMediaStream *stream, - GParamSpec *param, - GabbleMediaSession *session) -{ - GabbleMediaSessionPrivate *priv; - - g_assert (GABBLE_IS_MEDIA_SESSION (session)); - - priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session); - - if (!stream->got_local_codecs) - return; - - GMS_DEBUG_INFO (session, "stream %s has got local codecs", stream->name); - - if (stream->playing) - { - GMS_DEBUG_ERROR (session, "stream was already playing and we got local " - "codecs. what?!"); - g_assert_not_reached (); - return; - } - - /* after session is active, we do things per-stream with content-* actions */ - if (priv->state < JS_STATE_ACTIVE) - { - if (session->initiator == INITIATOR_REMOTE) - { - if (priv->state < JS_STATE_PENDING_ACCEPT_SENT) - { - try_session_accept (session); - } - else - { - GMS_DEBUG_INFO (session, "stream added after sending accept; " - "not doing content-add until remote end acknowledges"); - } - } - else - { - if (priv->state < JS_STATE_PENDING_INITIATE_SENT) - { - try_session_initiate (session); - } - else - { - GMS_DEBUG_INFO (session, "stream added after sending initiate; " - "not doing content-add until remote end accepts"); - } - } - } - else - { - if (stream->initiator == INITIATOR_REMOTE) - { - try_content_accept (session, stream); - } - else - { - do_content_add (session, stream); - } - } -} - -static gchar * -get_jid_for_contact (GabbleMediaSession *session, - TpHandle handle) -{ - GabbleMediaSessionPrivate *priv; - TpBaseConnection *conn; - const gchar *base_jid; - TpHandle self; - TpHandleRepoIface *contact_handles; - - g_assert (GABBLE_IS_MEDIA_SESSION (session)); - - priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session); - conn = (TpBaseConnection *)priv->conn; - contact_handles = tp_base_connection_get_handles (conn, - TP_HANDLE_TYPE_CONTACT); - self = conn->self_handle; - - base_jid = tp_handle_inspect (contact_handles, handle); - g_assert (base_jid != NULL); - - if (handle == self) - { - gchar *resource, *ret; - g_object_get (priv->conn, "resource", &resource, NULL); - g_assert (resource != NULL); - ret = g_strdup_printf ("%s/%s", base_jid, resource); - g_free (resource); - return ret; - } - else - { - g_assert (priv->peer_resource != NULL); - return g_strdup_printf ("%s/%s", base_jid, priv->peer_resource); - } -} - -LmMessage * -_gabble_media_session_message_new (GabbleMediaSession *session, - const gchar *action, - LmMessageNode **session_node) -{ - GabbleMediaSessionPrivate *priv; - TpBaseConnection *conn; - LmMessage *msg; - LmMessageNode *iq_node, *node; - gchar *peer_jid, *initiator_jid; - TpHandle initiator_handle; - const gchar *element, *xmlns; - - g_assert (GABBLE_IS_MEDIA_SESSION (session)); - - priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session); - conn = (TpBaseConnection *)priv->conn; - - peer_jid = get_jid_for_contact (session, priv->peer); - - msg = lm_message_new_with_sub_type ( - peer_jid, - LM_MESSAGE_TYPE_IQ, - LM_MESSAGE_SUB_TYPE_SET); - - g_free (peer_jid); - - iq_node = lm_message_get_node (msg); - - if (priv->mode == MODE_GOOGLE) - element = "session"; - else - element = "jingle"; - - if (session->initiator == INITIATOR_LOCAL) - initiator_handle = conn->self_handle; - else - initiator_handle = priv->peer; - - node = lm_message_node_add_child (iq_node, element, NULL); - initiator_jid = get_jid_for_contact (session, initiator_handle); - - lm_message_node_set_attributes (node, - (priv->mode == MODE_GOOGLE) ? "id" : "sid", priv->id, - (priv->mode == MODE_GOOGLE) ? "type" : "action", action, - "initiator", initiator_jid, - NULL); - - if (priv->mode == MODE_GOOGLE) - xmlns = NS_GOOGLE_SESSION; - else - xmlns = NS_JINGLE; - - lm_message_node_set_attribute (node, "xmlns", xmlns); - g_free (initiator_jid); - - if (session_node) - *session_node = node; - - return msg; -} - -void -_gabble_media_session_accept (GabbleMediaSession *session) -{ - GabbleMediaSessionPrivate *priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session); - guint i; - - priv->locally_accepted = TRUE; - - /* accept any local pending sends */ - for (i = 0; i < priv->streams->len; i++) - { - GabbleMediaStream *stream = g_ptr_array_index (priv->streams, i); - CombinedStreamDirection combined_dir = stream->combined_direction; - TpMediaStreamDirection current_dir; - TpMediaStreamPendingSend pending_send; - - current_dir = COMBINED_DIRECTION_GET_DIRECTION (combined_dir); - pending_send = COMBINED_DIRECTION_GET_PENDING_SEND (combined_dir); - - if ((pending_send & TP_MEDIA_STREAM_PENDING_LOCAL_SEND) != 0) - { - GMS_DEBUG_INFO (session, "accepting pending local send on stream %s", - stream->name); - - current_dir |= TP_MEDIA_STREAM_DIRECTION_SEND; - pending_send &= ~TP_MEDIA_STREAM_PENDING_LOCAL_SEND; - combined_dir = MAKE_COMBINED_DIRECTION (current_dir, pending_send); - g_object_set (stream, "combined-direction", combined_dir, NULL); - _gabble_media_stream_update_sending (stream, FALSE); - } - } - - try_session_accept (session); -} - -static LmHandlerResult -content_remove_msg_reply_cb (GabbleConnection *conn, - LmMessage *sent_msg, - LmMessage *reply_msg, - GObject *object, - gpointer user_data) -{ - GabbleMediaSession *session = GABBLE_MEDIA_SESSION (object); - GabbleMediaSessionPrivate *priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session); - GPtrArray *removing = (GPtrArray *) user_data; - guint i; - - MSG_REPLY_CB_END_SESSION_IF_NOT_SUCCESSFUL (session, - "stream removal failed"); - - for (i = 0; i < removing->len; i++) - destroy_media_stream (session, - GABBLE_MEDIA_STREAM (g_ptr_array_index (removing, i))); - - g_ptr_array_remove_fast (priv->remove_requests, removing); - g_ptr_array_free (removing, TRUE); - - return LM_HANDLER_RESULT_REMOVE_MESSAGE; -} - -void -_gabble_media_session_remove_streams (GabbleMediaSession *session, - GabbleMediaStream **streams, - guint len) -{ - GabbleMediaSessionPrivate *priv; - LmMessage *msg = NULL; - LmMessageNode *session_node; - GPtrArray *removing = NULL; - guint i; - - g_assert (GABBLE_IS_MEDIA_SESSION (session)); - - priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session); - - /* end the session if there'd be no streams left after reducing it */ - if (_count_non_removing_streams (session) == len) - { - _gabble_media_session_terminate (session, INITIATOR_LOCAL, - TP_CHANNEL_GROUP_CHANGE_REASON_NONE); - return; - } - - /* construct a remove message if we're in a state greater than CREATED (ie - * something has been sent/received about this session) */ - if (priv->state > JS_STATE_PENDING_CREATED) - { - msg = _gabble_media_session_message_new (session, "content-remove", - &session_node); - removing = g_ptr_array_sized_new (len); - } - - /* right, remove them */ - for (i = 0; i < len; i++) - { - GabbleMediaStream *stream = streams[i]; - - switch (stream->signalling_state) - { - case STREAM_SIG_STATE_NEW: - destroy_media_stream (session, stream); - break; - case STREAM_SIG_STATE_SENT: - case STREAM_SIG_STATE_ACKNOWLEDGED: - { - LmMessageNode *content_node; - - g_assert (msg != NULL); - g_assert (removing != NULL); - - content_node = _gabble_media_stream_add_content_node (stream, - session_node); - - g_object_set (stream, - "playing", FALSE, - "signalling-state", STREAM_SIG_STATE_REMOVING, - NULL); - - /* close the stream now, but don't forget about it until the - * removal message is acknowledged, since we need to be able to - * detect content-remove cross-talk */ - _gabble_media_stream_close (stream); - g_ptr_array_add (removing, stream); - } - break; - case STREAM_SIG_STATE_REMOVING: - break; - } - } - - /* send the remove message if necessary */ - if (msg != NULL) - { - if (removing->len > 0) - { - GMS_DEBUG_INFO (session, "sending jingle session action " - "\"content-remove\" to peer"); - - _gabble_connection_send_with_reply (priv->conn, msg, - content_remove_msg_reply_cb, G_OBJECT (session), removing, NULL); - - g_ptr_array_add (priv->remove_requests, removing); - } - else - { - g_ptr_array_free (removing, TRUE); - } - - lm_message_unref (msg); - } - else - { - GMS_DEBUG_INFO (session, "not sending jingle session action " - "\"content-remove\" to peer, no initiates or adds sent for " - "these streams"); - } -} - -/* for when you want the reply to be removed from - * the handler chain, but don't care what it is */ -static LmHandlerResult -ignore_reply_cb (GabbleConnection *conn, - LmMessage *sent_msg, - LmMessage *reply_msg, - GObject *object, - gpointer user_data) -{ - return LM_HANDLER_RESULT_REMOVE_MESSAGE; -} - -static void -send_reject_message (GabbleMediaSession *session) -{ - GabbleMediaSessionPrivate *priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session); - LmMessage *msg; - LmMessageNode *session_node; - - /* this should only happen in google mode, and we should only arrive in that - * mode when we've ended up talking to a resource that doesn't support - * jingle */ - g_assert (priv->mode == MODE_GOOGLE); - g_assert (priv->peer_resource != NULL); - - /* construct a session terminate message */ - msg = _gabble_media_session_message_new (session, "reject", &session_node); - - GMS_DEBUG_INFO (session, "sending jingle session action \"reject\" to peer"); - - /* send it */ - _gabble_connection_send_with_reply (priv->conn, msg, ignore_reply_cb, - G_OBJECT (session), NULL, NULL); - - lm_message_unref (msg); -} - -static void -send_terminate_message (GabbleMediaSession *session) -{ - GabbleMediaSessionPrivate *priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session); - const gchar *action; - LmMessage *msg; - LmMessageNode *session_node; - - /* construct a session terminate message */ - if (priv->mode == MODE_GOOGLE) - action = "terminate"; - else - action = "session-terminate"; - - msg = _gabble_media_session_message_new (session, action, &session_node); - - GMS_DEBUG_INFO (session, "sending jingle session action \"%s\" to peer", - action); - - /* send it */ - _gabble_connection_send_with_reply (priv->conn, msg, ignore_reply_cb, - G_OBJECT (session), NULL, NULL); - - lm_message_unref (msg); -} - -void -_gabble_media_session_terminate (GabbleMediaSession *session, - JingleInitiator who, - TpChannelGroupChangeReason why) -{ - GabbleMediaSessionPrivate *priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session); - TpBaseConnection *conn = (TpBaseConnection *)priv->conn; - TpHandle actor; - - if (priv->state == JS_STATE_ENDED) - return; - - if (who == INITIATOR_REMOTE) - { - actor = priv->peer; - } - else - { - actor = conn->self_handle; - - /* Need to tell them that it's all over. */ - - /* Jingle doesn't have a "reject" action; a termination before an - * acceptance indicates that the call has been declined */ - - if (session->initiator == INITIATOR_REMOTE && - priv->state == JS_STATE_PENDING_INITIATED && - priv->mode == MODE_GOOGLE) - { - send_reject_message (session); - } - - /* if we're still in CREATED, then we've not sent or received any - * messages about this session yet, so no terminate is necessary */ - else if (priv->state > JS_STATE_PENDING_CREATED) - { - send_terminate_message (session); - } - - while (priv->streams->len > 0) - destroy_media_stream (session, g_ptr_array_index (priv->streams, 0)); - } - - priv->terminated = TRUE; - g_object_set (session, "state", JS_STATE_ENDED, NULL); - g_signal_emit (session, signals[TERMINATED], 0, actor, why); -} - -#if _GMS_DEBUG_LEVEL -void -_gabble_media_session_debug (GabbleMediaSession *session, - DebugMessageType type, - const gchar *format, ...) -{ - if (DEBUGGING) - { - va_list list; - gchar buf[512]; - GabbleMediaSessionPrivate *priv; - time_t curtime; - struct tm *loctime; - gchar stamp[10]; - const gchar *type_str; - - g_assert (GABBLE_IS_MEDIA_SESSION (session)); - - priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session); - - curtime = time (NULL); - loctime = localtime (&curtime); - - strftime (stamp, sizeof (stamp), "%T", loctime); - - va_start (list, format); - - vsnprintf (buf, sizeof (buf), format, list); - - va_end (list); - - switch (type) { - case DEBUG_MSG_INFO: - type_str = TP_ANSI_BOLD_ON TP_ANSI_FG_WHITE; - break; - case DEBUG_MSG_DUMP: - type_str = TP_ANSI_BOLD_ON TP_ANSI_FG_GREEN; - break; - case DEBUG_MSG_WARNING: - type_str = TP_ANSI_BOLD_ON TP_ANSI_FG_YELLOW; - break; - case DEBUG_MSG_ERROR: - type_str = TP_ANSI_BOLD_ON TP_ANSI_FG_WHITE TP_ANSI_BG_RED; - break; - case DEBUG_MSG_EVENT: - type_str = TP_ANSI_BOLD_ON TP_ANSI_FG_CYAN; - break; - default: - g_assert_not_reached (); - return; - } - - printf ("[%s%s%s] %s%-26s%s %s%s%s\n", - TP_ANSI_BOLD_ON TP_ANSI_FG_WHITE, - stamp, - TP_ANSI_RESET, - session_states[priv->state].attributes, - session_states[priv->state].name, - TP_ANSI_RESET, - type_str, - buf, - TP_ANSI_RESET); - - fflush (stdout); - } -} - -#endif /* _GMS_DEBUG_LEVEL */ - -static const gchar * -_name_stream (GabbleMediaSession *session, - TpMediaStreamType media_type) -{ - GabbleMediaSessionPrivate *priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session); - static gchar ret[MAX_STREAM_NAME_LEN] = GTALK_STREAM_NAME; - - if (priv->mode != MODE_GOOGLE) - { - guint i = 1; - - do { - g_snprintf (ret, MAX_STREAM_NAME_LEN, "%s%u", - media_type == TP_MEDIA_STREAM_TYPE_AUDIO ? "audio" : "video", - i++); - - /* even though we now have seperate namespaces for local and remote, - * actually check in both so that we can still support clients which - * have 1 namespace (such as our older selves :D) */ - if (_lookup_stream_by_name_and_initiator (session, ret, - INITIATOR_INVALID) != NULL) - { - ret[0] = '\0'; - } - } while (ret[0] == '\0'); - } - - return ret; -} - - -gboolean -_gabble_media_session_request_streams (GabbleMediaSession *session, - const GArray *media_types, - GPtrArray **ret, - GError **error) -{ - static GabblePresenceCapabilities google_audio_caps = - PRESENCE_CAP_GOOGLE_VOICE; - static GabblePresenceCapabilities jingle_audio_caps = - PRESENCE_CAP_JINGLE | PRESENCE_CAP_JINGLE_DESCRIPTION_AUDIO | - PRESENCE_CAP_GOOGLE_TRANSPORT_P2P; - static GabblePresenceCapabilities jingle_video_caps = - PRESENCE_CAP_JINGLE | PRESENCE_CAP_JINGLE_DESCRIPTION_VIDEO | - PRESENCE_CAP_GOOGLE_TRANSPORT_P2P; - - GabbleMediaSessionPrivate *priv; - GabblePresence *presence; - gboolean want_audio, want_video; - GabblePresenceCapabilities jingle_desired_caps; - guint idx; - gchar *dump; - - g_assert (GABBLE_IS_MEDIA_SESSION (session)); - - priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session); - - presence = gabble_presence_cache_get (priv->conn->presence_cache, - priv->peer); - - if (presence == NULL) - { - g_set_error (error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE, - "member has no audio/video capabilities"); - - return FALSE; - } - - dump = gabble_presence_dump (presence); - GMS_DEBUG_DUMP (session, "presence for peer %d:\n%s", priv->peer, dump); - g_free (dump); - - want_audio = want_video = FALSE; - - for (idx = 0; idx < media_types->len; idx++) - { - guint media_type = g_array_index (media_types, guint, idx); - - if (media_type == TP_MEDIA_STREAM_TYPE_AUDIO) - { - want_audio = TRUE; - } - else if (media_type == TP_MEDIA_STREAM_TYPE_VIDEO) - { - want_video = TRUE; - } - else - { - g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, - "given media type %u is invalid", media_type); - return FALSE; - } - } - - /* work out what we'd need to do these streams with jingle */ - jingle_desired_caps = 0; - - if (want_audio) - jingle_desired_caps |= jingle_audio_caps; - - if (want_video) - jingle_desired_caps |= jingle_video_caps; - - GMS_DEBUG_INFO (session, "want audio: %s; want video: %s", - want_audio ? "yes" : "no", want_video ? "yes" : "no"); - - /* existing call; the recipient and the mode has already been decided */ - if (priv->peer_resource) - { - /* is a google call... we have no other option */ - if (priv->mode == MODE_GOOGLE) - { - GMS_DEBUG_INFO (session, "already in Google mode; can't add new " - "stream"); - - g_assert (priv->streams->len == 1); - - g_set_error (error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE, - "Google Talk calls may only contain one stream"); - - return FALSE; - } - - if (!gabble_presence_resource_has_caps (presence, priv->peer_resource, - jingle_desired_caps)) - { - GMS_DEBUG_INFO (session, - "in Jingle mode but have insufficient caps for requested streams"); - - g_set_error (error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE, - "existing call member doesn't support all requested media" - " types"); - - return FALSE; - } - - GMS_DEBUG_INFO (session, - "in Jingle mode, and have necessary caps"); - } - - /* no existing call; we should choose a recipient and a mode */ - else - { - const gchar *resource; - - g_assert (priv->streams->len == 0); - - /* see if we have a fully-capable jingle resource; regardless of the - * desired media type it's best if we can add/remove the others later */ - resource = gabble_presence_pick_resource_by_caps (presence, - jingle_audio_caps | jingle_video_caps); - - if (resource == NULL) - { - GMS_DEBUG_INFO (session, "contact is not fully jingle-capable"); - - /* ok, no problem. see if we can do just what's wanted with jingle */ - resource = gabble_presence_pick_resource_by_caps (presence, - jingle_desired_caps); - - if (resource == NULL && want_audio && !want_video) - { - GMS_DEBUG_INFO (session, - "contact doesn't have desired Jingle capabilities"); - - /* last ditch... if we want only audio and not video, we can make - * do with google talk */ - resource = gabble_presence_pick_resource_by_caps (presence, - google_audio_caps); - - if (resource != NULL) - { - /* only one stream possible with google */ - if (media_types->len == 1) - { - GMS_DEBUG_INFO (session, - "contact has no Jingle capabilities; " - "falling back to Google audio call"); - priv->mode = MODE_GOOGLE; - } - else - { - g_set_error (error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE, - "Google Talk calls may only contain one stream"); - - return FALSE; - } - } - else - { - GMS_DEBUG_INFO (session, - "contact doesn't have desired Google capabilities"); - } - } - } - - if (resource == NULL) - { - GMS_DEBUG_INFO (session, - "contact doesn't have a resource with suitable capabilities"); - - g_set_error (error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE, - "member does not have the desired audio/video capabilities"); - - return FALSE; - } - - priv->peer_resource = g_strdup (resource); - } - - /* check it's not a ridiculous number of streams */ - if ((priv->streams->len + media_types->len) > MAX_STREAMS) - { - g_set_error (error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE, - "I think that's quite enough streams already"); - return FALSE; - } - - /* if we've got here, we're good to make the streams */ - - *ret = g_ptr_array_sized_new (media_types->len); - - for (idx = 0; idx < media_types->len; idx++) - { - guint media_type = g_array_index (media_types, guint, idx); - GabbleMediaStream *stream; - const gchar *stream_name; - - if (priv->mode == MODE_GOOGLE) - stream_name = GTALK_STREAM_NAME; - else - stream_name = _name_stream (session, media_type); - - stream = create_media_stream (session, stream_name, INITIATOR_LOCAL, - media_type); - - g_ptr_array_add (*ret, stream); - } - - return TRUE; -} - -static const gchar * -_direction_to_senders (GabbleMediaSession *session, - TpMediaStreamDirection dir) -{ - const gchar *ret = NULL; - - switch (dir) - { - case TP_MEDIA_STREAM_DIRECTION_NONE: - g_assert_not_reached (); - break; - case TP_MEDIA_STREAM_DIRECTION_SEND: - if (session->initiator == INITIATOR_LOCAL) - ret = "initiator"; - else - ret = "responder"; - break; - case TP_MEDIA_STREAM_DIRECTION_RECEIVE: - if (session->initiator == INITIATOR_REMOTE) - ret = "initiator"; - else - ret = "responder"; - break; - case TP_MEDIA_STREAM_DIRECTION_BIDIRECTIONAL: - ret = "both"; - break; - } - - g_assert (ret != NULL); - - return ret; -} - -static LmHandlerResult -direction_msg_reply_cb (GabbleConnection *conn, - LmMessage *sent_msg, - LmMessage *reply_msg, - GObject *object, - gpointer user_data) -{ - GabbleMediaSession *session = GABBLE_MEDIA_SESSION (user_data); - GabbleMediaStream *stream = GABBLE_MEDIA_STREAM (object); - - MSG_REPLY_CB_END_SESSION_IF_NOT_SUCCESSFUL (session, - "direction change failed"); - - if (stream->playing) - { - _gabble_media_stream_update_sending (stream, TRUE); - } - - return LM_HANDLER_RESULT_REMOVE_MESSAGE; -} - -static gboolean -send_direction_change (GabbleMediaSession *session, - GabbleMediaStream *stream, - TpMediaStreamDirection dir, - GError **error) -{ - GabbleMediaSessionPrivate *priv; - const gchar *senders; - LmMessage *msg; - LmMessageNode *session_node, *content_node; - gboolean ret; - - priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session); - senders = _direction_to_senders (session, dir); - - if (stream->signalling_state == STREAM_SIG_STATE_NEW || - stream->signalling_state == STREAM_SIG_STATE_REMOVING) - { - GMS_DEBUG_INFO (session, "not sending content-modify for %s stream %s", - stream->signalling_state == STREAM_SIG_STATE_NEW ? "new" : "removing", - stream->name); - return TRUE; - } - - GMS_DEBUG_INFO (session, "sending jingle session action \"content-modify\" " - "to peer for stream %s (senders=%s)", stream->name, senders); - - msg = _gabble_media_session_message_new (session, "content-modify", - &session_node); - content_node = _gabble_media_stream_add_content_node (stream, session_node); - - lm_message_node_set_attribute (content_node, "senders", senders); - - ret = _gabble_connection_send_with_reply (priv->conn, msg, - direction_msg_reply_cb, G_OBJECT (stream), session, error); - - lm_message_unref (msg); - - return ret; -} - -gboolean -_gabble_media_session_request_stream_direction (GabbleMediaSession *session, - GabbleMediaStream *stream, - TpMediaStreamDirection requested_dir, - GError **error) -{ - GabbleMediaSessionPrivate *priv; - CombinedStreamDirection new_combined_dir; - TpMediaStreamDirection current_dir; //, new_dir; - TpMediaStreamPendingSend pending_send; - - priv = GABBLE_MEDIA_SESSION_GET_PRIVATE (session); - - current_dir = COMBINED_DIRECTION_GET_DIRECTION (stream->combined_direction); - pending_send = COMBINED_DIRECTION_GET_PENDING_SEND - (stream->combined_direction); - - if (priv->mode == MODE_GOOGLE) - { - g_assert (current_dir == TP_MEDIA_STREAM_DIRECTION_BIDIRECTIONAL); - - if (requested_dir == TP_MEDIA_STREAM_DIRECTION_BIDIRECTIONAL) - return TRUE; - - g_set_error (error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE, - "Google Talk calls can only be bi-directional"); - return FALSE; - } - - if (requested_dir == TP_MEDIA_STREAM_DIRECTION_NONE) - { - GMS_DEBUG_INFO (session, "request for NONE direction; removing stream"); - - _gabble_media_session_remove_streams (session, &stream, 1); - - return TRUE; - } - - /* if we're awaiting a local decision on sending... */ - if ((pending_send & TP_MEDIA_STREAM_PENDING_LOCAL_SEND) != 0) - { - /* clear the flag */ - pending_send &= ~TP_MEDIA_STREAM_PENDING_LOCAL_SEND; - - /* make our current_dir match what other end thinks (he thinks we're - * bidirectional) so that we send the correct transitions */ - current_dir ^= TP_MEDIA_STREAM_DIRECTION_SEND; - } - -#if 0 - /* if we're asking the remote end to start sending, set the pending flag and - * don't change our directionality just yet */ - new_dir = requested_dir; - if (((current_dir & TP_MEDIA_STREAM_DIRECTION_RECEIVE) == 0) && - ((new_dir & TP_MEDIA_STREAM_DIRECTION_RECEIVE) != 0)) - { - pending_send ^= TP_MEDIA_STREAM_PENDING_REMOTE_SEND; - new_dir &= ~TP_MEDIA_STREAM_DIRECTION_RECEIVE; - } -#endif - - /* make any necessary changes */ - new_combined_dir = MAKE_COMBINED_DIRECTION (requested_dir, pending_send); - if (new_combined_dir != stream->combined_direction) - { - g_object_set (stream, "combined-direction", new_combined_dir, NULL); - _gabble_media_stream_update_sending (stream, FALSE); - } - - /* short-circuit sending a request if we're not asking for anything new */ - if (current_dir == requested_dir) - return TRUE; - - /* send request */ - return send_direction_change (session, stream, requested_dir, error); -} - -static void -session_handler_iface_init (gpointer g_iface, gpointer iface_data) -{ - TpSvcMediaSessionHandlerClass *klass = - (TpSvcMediaSessionHandlerClass *)g_iface; - -#define IMPLEMENT(x) tp_svc_media_session_handler_implement_##x (\ - klass, gabble_media_session_##x) - IMPLEMENT(error); - IMPLEMENT(ready); -#undef IMPLEMENT -} diff --git a/src/gabble-media-session.h b/src/gabble-media-session.h deleted file mode 100644 index ee80b0a96..000000000 --- a/src/gabble-media-session.h +++ /dev/null @@ -1,170 +0,0 @@ -/* - * gabble-media-session.h - Header for GabbleMediaSession - * Copyright (C) 2006 Collabora Ltd. - * Copyright (C) 2006 Nokia Corporation - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef __GABBLE_MEDIA_SESSION_H__ -#define __GABBLE_MEDIA_SESSION_H__ - -#include <glib-object.h> -#include <loudmouth/loudmouth.h> - -#include "gabble-types.h" -#include "gabble-media-stream.h" -#include <telepathy-glib/enums.h> - -G_BEGIN_DECLS - -typedef enum -{ - MODE_GOOGLE, - MODE_JINGLE -} GabbleMediaSessionMode; - -typedef enum { - JS_STATE_INVALID = -1, - JS_STATE_PENDING_CREATED = 0, - JS_STATE_PENDING_INITIATE_SENT, - JS_STATE_PENDING_INITIATED, - JS_STATE_PENDING_ACCEPT_SENT, - JS_STATE_ACTIVE, - JS_STATE_ENDED -} JingleSessionState; - -typedef enum { - DEBUG_MSG_INFO = 0, - DEBUG_MSG_DUMP, - DEBUG_MSG_WARNING, - DEBUG_MSG_ERROR, - DEBUG_MSG_EVENT -} DebugMessageType; - -typedef struct _GabbleMediaSession GabbleMediaSession; -typedef struct _GabbleMediaSessionClass GabbleMediaSessionClass; - -struct _GabbleMediaSessionClass { - GObjectClass parent_class; -}; - -struct _GabbleMediaSession { - GObject parent; - - JingleInitiator initiator; - - gpointer priv; -}; - -GType gabble_media_session_get_type (void); - -/* TYPE MACROS */ -#define GABBLE_TYPE_MEDIA_SESSION \ - (gabble_media_session_get_type ()) -#define GABBLE_MEDIA_SESSION(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_MEDIA_SESSION, \ - GabbleMediaSession)) -#define GABBLE_MEDIA_SESSION_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_MEDIA_SESSION, \ - GabbleMediaSessionClass)) -#define GABBLE_IS_MEDIA_SESSION(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_MEDIA_SESSION)) -#define GABBLE_IS_MEDIA_SESSION_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_MEDIA_SESSION)) -#define GABBLE_MEDIA_SESSION_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_MEDIA_SESSION, \ - GabbleMediaSessionClass)) - -/* CONVENIENCE MACROS */ -#define MSG_REPLY_CB_END_SESSION_IF_NOT_SUCCESSFUL(s, m) \ - G_STMT_START { \ - if (lm_message_get_sub_type (reply_msg) != LM_MESSAGE_SUB_TYPE_RESULT) \ - { \ - GMS_DEBUG_ERROR (s, m); \ - NODE_DEBUG (sent_msg->node, "message sent"); \ - NODE_DEBUG (reply_msg->node, "message reply"); \ - _gabble_media_session_terminate (s, INITIATOR_LOCAL, \ - TP_CHANNEL_GROUP_CHANGE_REASON_ERROR); \ - return LM_HANDLER_RESULT_REMOVE_MESSAGE; \ - } \ - } G_STMT_END - -gboolean -_gabble_media_session_handle_action (GabbleMediaSession *session, - LmMessage *message, - LmMessageNode *session_node, - const gchar *action, - GError **error); - -LmMessage *_gabble_media_session_message_new (GabbleMediaSession *session, - const gchar *action, - LmMessageNode **session_node); - -void _gabble_media_session_accept (GabbleMediaSession *session); -void _gabble_media_session_remove_streams (GabbleMediaSession *session, - GabbleMediaStream **streams, guint len); -void _gabble_media_session_terminate (GabbleMediaSession *session, - JingleInitiator who, TpChannelGroupChangeReason why); - -gboolean _gabble_media_session_request_streams (GabbleMediaSession *session, - const GArray *types, - GPtrArray **ret, - GError **error); - -gboolean _gabble_media_session_request_stream_direction (GabbleMediaSession *, - GabbleMediaStream *, TpMediaStreamDirection, GError **); - -#ifndef _GMS_DEBUG_LEVEL -#define _GMS_DEBUG_LEVEL 2 -#endif - -#if _GMS_DEBUG_LEVEL -#ifdef ENABLE_DEBUG - -#define GMS_DEBUG_INFO(s, ...) \ - _gabble_media_session_debug (s, DEBUG_MSG_INFO, __VA_ARGS__) -#if _GMS_DEBUG_LEVEL > 1 -#define GMS_DEBUG_DUMP(s, ...) \ - _gabble_media_session_debug (s, DEBUG_MSG_DUMP, __VA_ARGS__) -#else -#define GMS_DEBUG_DUMP(s, ...) -#endif -#define GMS_DEBUG_WARNING(s, ...) \ - _gabble_media_session_debug (s, DEBUG_MSG_WARNING, __VA_ARGS__) -#define GMS_DEBUG_ERROR(s, ...) \ - _gabble_media_session_debug (s, DEBUG_MSG_ERROR, __VA_ARGS__) -#define GMS_DEBUG_EVENT(s, ...) \ - _gabble_media_session_debug (s, DEBUG_MSG_EVENT, __VA_ARGS__) - -void _gabble_media_session_debug (GabbleMediaSession *session, - DebugMessageType type, - const gchar *format, ...) - G_GNUC_PRINTF (3, 4); - -#else - -#define GMS_DEBUG_INFO(s, ...) -#define GMS_DEBUG_DUMP(s, ...) -#define GMS_DEBUG_WARNING(s, ...) -#define GMS_DEBUG_ERROR(s, ...) -#define GMS_DEBUG_EVENT(s, ...) - -#endif /* ENABLE_DEBUG */ -#endif /* _GMS_DEBUG_LEVEL */ - -G_END_DECLS - -#endif /* #ifndef __GABBLE_MEDIA_SESSION_H__*/ diff --git a/src/gabble-media-stream.c b/src/gabble-media-stream.c deleted file mode 100644 index 4be1c8b9c..000000000 --- a/src/gabble-media-stream.c +++ /dev/null @@ -1,1719 +0,0 @@ -/* - * gabble-media-stream.c - Source for GabbleMediaStream - * Copyright (C) 2006 Collabora Ltd. - * Copyright (C) 2006 Nokia Corporation - * @author Ole Andre Vadla Ravnaas <ole.andre.ravnaas@collabora.co.uk> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "gabble-media-stream.h" - -#include <dbus/dbus-glib.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#define DEBUG_FLAG GABBLE_DEBUG_MEDIA - -#include <telepathy-glib/debug-ansi.h> -#include "debug.h" -#include "namespaces.h" - -#include "gabble-connection.h" -#include "gabble-media-channel.h" -#include "gabble-media-session.h" -#include "gabble-media-session-enumtypes.h" - -#include <telepathy-glib/dbus.h> -#include <telepathy-glib/enums.h> -#include <telepathy-glib/errors.h> -#include <telepathy-glib/svc-media-interfaces.h> - -#include "gabble-signals-marshal.h" - -static void stream_handler_iface_init (gpointer, gpointer); - -G_DEFINE_TYPE_WITH_CODE(GabbleMediaStream, - gabble_media_stream, - G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_MEDIA_STREAM_HANDLER, - stream_handler_iface_init) - ) - -/* signal enum */ -enum -{ - DESTROY, - - NEW_ACTIVE_CANDIDATE_PAIR, - NEW_NATIVE_CANDIDATE, - SUPPORTED_CODECS, - ERROR, - - LAST_SIGNAL -}; - -static guint signals[LAST_SIGNAL] = {0}; - -/* properties */ -enum -{ - PROP_CONNECTION = 1, - PROP_MEDIA_SESSION, - PROP_OBJECT_PATH, - PROP_MODE, - PROP_NAME, - PROP_ID, - PROP_INITIATOR, - PROP_MEDIA_TYPE, - PROP_CONNECTION_STATE, - PROP_READY, - PROP_GOT_LOCAL_CODECS, - PROP_SIGNALLING_STATE, - PROP_PLAYING, - PROP_COMBINED_DIRECTION, - LAST_PROPERTY -}; - -/* private structure */ -typedef struct _GabbleMediaStreamPrivate GabbleMediaStreamPrivate; - -struct _GabbleMediaStreamPrivate -{ - GabbleConnection *conn; - GabbleMediaSession *session; - GabbleMediaSessionMode mode; - gchar *object_path; - guint id; - guint media_type; - - gboolean ready; - gboolean sending; - - GValue native_codecs; /* intersected codec list */ - GValue native_candidates; - - GValue remote_codecs; - GValue remote_candidates; - - guint remote_candidate_count; - - gboolean closed; - gboolean dispose_has_run; -}; - -#define GABBLE_MEDIA_STREAM_GET_PRIVATE(obj) \ - ((GabbleMediaStreamPrivate *)obj->priv) - -#ifdef ENABLE_DEBUG -#if _GMS_DEBUG_LEVEL > 1 -static const char *tp_protocols[] = { - "TP_MEDIA_STREAM_BASE_PROTO_UDP (0)", - "TP_MEDIA_STREAM_BASE_PROTO_TCP (1)" -}; - -static const char *tp_transports[] = { - "TP_MEDIA_STREAM_TRANSPORT_TYPE_LOCAL (0)", - "TP_MEDIA_STREAM_TRANSPORT_TYPE_DERIVED (1)", - "TP_MEDIA_STREAM_TRANSPORT_TYPE_RELAY (2)" -}; -#endif -#endif - -static void push_native_candidates (GabbleMediaStream *stream); -static void push_remote_codecs (GabbleMediaStream *stream); -static void push_remote_candidates (GabbleMediaStream *stream); -static void push_playing (GabbleMediaStream *stream); -static void push_sending (GabbleMediaStream *stream); - -static void -gabble_media_stream_init (GabbleMediaStream *self) -{ - GabbleMediaStreamPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self, - GABBLE_TYPE_MEDIA_STREAM, GabbleMediaStreamPrivate); - - self->priv = priv; - - g_value_init (&priv->native_codecs, TP_TYPE_CODEC_LIST); - g_value_take_boxed (&priv->native_codecs, - dbus_g_type_specialized_construct (TP_TYPE_CODEC_LIST)); - - g_value_init (&priv->native_candidates, TP_TYPE_CANDIDATE_LIST); - g_value_take_boxed (&priv->native_candidates, - dbus_g_type_specialized_construct (TP_TYPE_CANDIDATE_LIST)); - - g_value_init (&priv->remote_codecs, TP_TYPE_CODEC_LIST); - g_value_take_boxed (&priv->remote_codecs, - dbus_g_type_specialized_construct (TP_TYPE_CODEC_LIST)); - - g_value_init (&priv->remote_candidates, TP_TYPE_CANDIDATE_LIST); - g_value_take_boxed (&priv->remote_candidates, - dbus_g_type_specialized_construct (TP_TYPE_CANDIDATE_LIST)); -} - -static GObject * -gabble_media_stream_constructor (GType type, guint n_props, - GObjectConstructParam *props) -{ - GObject *obj; - GabbleMediaStreamPrivate *priv; - DBusGConnection *bus; - - /* call base class constructor */ - obj = G_OBJECT_CLASS (gabble_media_stream_parent_class)-> - constructor (type, n_props, props); - priv = GABBLE_MEDIA_STREAM_GET_PRIVATE (GABBLE_MEDIA_STREAM (obj)); - - /* go for the bus */ - bus = tp_get_bus (); - dbus_g_connection_register_g_object (bus, priv->object_path, obj); - - return obj; -} - -static void -gabble_media_stream_get_property (GObject *object, - guint property_id, - GValue *value, - GParamSpec *pspec) -{ - GabbleMediaStream *stream = GABBLE_MEDIA_STREAM (object); - GabbleMediaStreamPrivate *priv = GABBLE_MEDIA_STREAM_GET_PRIVATE (stream); - - switch (property_id) { - case PROP_CONNECTION: - g_value_set_object (value, priv->conn); - break; - case PROP_MEDIA_SESSION: - g_value_set_object (value, priv->session); - break; - case PROP_OBJECT_PATH: - g_value_set_string (value, priv->object_path); - break; - case PROP_MODE: - g_value_set_enum (value, priv->mode); - break; - case PROP_NAME: - g_value_set_string (value, stream->name); - break; - case PROP_ID: - g_value_set_uint (value, priv->id); - break; - case PROP_INITIATOR: - g_value_set_uint (value, stream->initiator); - break; - case PROP_MEDIA_TYPE: - g_value_set_uint (value, priv->media_type); - break; - case PROP_CONNECTION_STATE: - g_value_set_uint (value, stream->connection_state); - break; - case PROP_READY: - g_value_set_boolean (value, priv->ready); - break; - case PROP_GOT_LOCAL_CODECS: - g_value_set_boolean (value, stream->got_local_codecs); - break; - case PROP_SIGNALLING_STATE: - g_value_set_uint (value, stream->signalling_state); - break; - case PROP_PLAYING: - g_value_set_boolean (value, stream->playing); - break; - case PROP_COMBINED_DIRECTION: - g_value_set_uint (value, stream->combined_direction); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } -} - -static void -gabble_media_stream_set_property (GObject *object, - guint property_id, - const GValue *value, - GParamSpec *pspec) -{ - GabbleMediaStream *stream = GABBLE_MEDIA_STREAM (object); - GabbleMediaStreamPrivate *priv = GABBLE_MEDIA_STREAM_GET_PRIVATE (stream); - - switch (property_id) { - case PROP_CONNECTION: - priv->conn = g_value_get_object (value); - break; - case PROP_MEDIA_SESSION: - priv->session = g_value_get_object (value); - break; - case PROP_OBJECT_PATH: - g_free (priv->object_path); - priv->object_path = g_value_dup_string (value); - break; - case PROP_MODE: - priv->mode = g_value_get_enum (value); - break; - case PROP_NAME: - g_free (stream->name); - stream->name = g_value_dup_string (value); - break; - case PROP_ID: - priv->id = g_value_get_uint (value); - break; - case PROP_INITIATOR: - stream->initiator = g_value_get_uint (value); - break; - case PROP_MEDIA_TYPE: - priv->media_type = g_value_get_uint (value); - break; - case PROP_CONNECTION_STATE: - GMS_DEBUG_INFO (priv->session, "stream %s connection state %d", - stream->name, stream->connection_state); - stream->connection_state = g_value_get_uint (value); - break; - case PROP_READY: - priv->ready = g_value_get_boolean (value); - break; - case PROP_GOT_LOCAL_CODECS: - stream->got_local_codecs = g_value_get_boolean (value); - break; - case PROP_SIGNALLING_STATE: - { - StreamSignallingState old = stream->signalling_state; - stream->signalling_state = g_value_get_uint (value); - GMS_DEBUG_INFO (priv->session, "stream %s sig_state %d->%d", - stream->name, old, stream->signalling_state); - if (stream->signalling_state != old) - push_native_candidates (stream); - } - break; - case PROP_PLAYING: - { - gboolean old = stream->playing; - stream->playing = g_value_get_boolean (value); - if (stream->playing != old) - push_playing (stream); - } - break; - case PROP_COMBINED_DIRECTION: - stream->combined_direction = g_value_get_uint (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } -} - -static void gabble_media_stream_dispose (GObject *object); -static void gabble_media_stream_finalize (GObject *object); - -static void -gabble_media_stream_class_init (GabbleMediaStreamClass *gabble_media_stream_class) -{ - GObjectClass *object_class = G_OBJECT_CLASS (gabble_media_stream_class); - GParamSpec *param_spec; - - g_type_class_add_private (gabble_media_stream_class, - sizeof (GabbleMediaStreamPrivate)); - - object_class->constructor = gabble_media_stream_constructor; - - object_class->get_property = gabble_media_stream_get_property; - object_class->set_property = gabble_media_stream_set_property; - - object_class->dispose = gabble_media_stream_dispose; - object_class->finalize = gabble_media_stream_finalize; - - param_spec = g_param_spec_object ("connection", "GabbleConnection object", - "Gabble connection object that owns this " - "media stream's channel.", - GABBLE_TYPE_CONNECTION, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | - G_PARAM_STATIC_NICK | - G_PARAM_STATIC_BLURB); - g_object_class_install_property (object_class, PROP_CONNECTION, param_spec); - - param_spec = g_param_spec_object ("media-session", - "GabbleMediaSession object", - "Gabble media session object that owns this media stream object.", - GABBLE_TYPE_MEDIA_SESSION, - G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_NICK | - G_PARAM_STATIC_BLURB); - g_object_class_install_property (object_class, PROP_MEDIA_SESSION, - param_spec); - - param_spec = g_param_spec_string ("object-path", "D-Bus object path", - "The D-Bus object path used for this " - "object on the bus.", - NULL, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | - G_PARAM_STATIC_NAME | - G_PARAM_STATIC_BLURB); - g_object_class_install_property (object_class, PROP_OBJECT_PATH, param_spec); - - param_spec = g_param_spec_enum ("mode", "Signalling mode", - "Which signalling mode used to control the " - "stream.", - gabble_media_session_mode_get_type (), - MODE_JINGLE, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | - G_PARAM_STATIC_NAME | - G_PARAM_STATIC_BLURB); - g_object_class_install_property (object_class, PROP_MODE, param_spec); - - param_spec = g_param_spec_string ("name", "Stream name", - "An opaque name for the stream used in the signalling.", NULL, - G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_NAME | - G_PARAM_STATIC_BLURB); - g_object_class_install_property (object_class, PROP_NAME, param_spec); - - param_spec = g_param_spec_uint ("id", "Stream ID", - "A stream number for the stream used in the " - "D-Bus API.", - 0, G_MAXUINT, 0, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | - G_PARAM_STATIC_NAME | - G_PARAM_STATIC_BLURB); - g_object_class_install_property (object_class, PROP_ID, param_spec); - - param_spec = g_param_spec_uint ("initiator", "Stream initiator", - "An enum signifying which end initiated " - "the stream.", - INITIATOR_LOCAL, - INITIATOR_REMOTE, - INITIATOR_LOCAL, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | - G_PARAM_STATIC_NAME | - G_PARAM_STATIC_BLURB); - g_object_class_install_property (object_class, PROP_INITIATOR, param_spec); - - param_spec = g_param_spec_uint ("media-type", "Stream media type", - "A constant indicating which media type the " - "stream carries.", - TP_MEDIA_STREAM_TYPE_AUDIO, - TP_MEDIA_STREAM_TYPE_VIDEO, - TP_MEDIA_STREAM_TYPE_AUDIO, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | - G_PARAM_STATIC_NAME | - G_PARAM_STATIC_BLURB); - g_object_class_install_property (object_class, PROP_MEDIA_TYPE, param_spec); - - param_spec = g_param_spec_uint ("connection-state", - "Stream connection state", - "An integer indicating the state of the stream's connection.", - TP_MEDIA_STREAM_STATE_DISCONNECTED, - TP_MEDIA_STREAM_STATE_CONNECTED, - TP_MEDIA_STREAM_STATE_DISCONNECTED, - G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_NAME | - G_PARAM_STATIC_BLURB); - g_object_class_install_property (object_class, PROP_CONNECTION_STATE, - param_spec); - - param_spec = g_param_spec_boolean ("ready", "Ready?", - "A boolean signifying whether the user " - "is ready to handle signals from this " - "object.", - FALSE, - G_PARAM_CONSTRUCT | - G_PARAM_READWRITE | - G_PARAM_STATIC_NAME | - G_PARAM_STATIC_BLURB); - g_object_class_install_property (object_class, PROP_READY, param_spec); - - param_spec = g_param_spec_boolean ("got-local-codecs", "Got local codecs?", - "A boolean signifying whether we've got the locally supported codecs " - "from the user.", FALSE, - G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_NAME | - G_PARAM_STATIC_BLURB); - g_object_class_install_property (object_class, PROP_GOT_LOCAL_CODECS, - param_spec); - - param_spec = g_param_spec_uint ("signalling-state", "Signalling state", - "Whether the stream is newly created, " - "sent to the peer, or acknowledged.", - STREAM_SIG_STATE_NEW, - STREAM_SIG_STATE_REMOVING, - STREAM_SIG_STATE_NEW, - G_PARAM_CONSTRUCT | - G_PARAM_READWRITE | - G_PARAM_STATIC_NAME | - G_PARAM_STATIC_BLURB); - g_object_class_install_property (object_class, PROP_SIGNALLING_STATE, - param_spec); - - param_spec = g_param_spec_boolean ("playing", "Set playing", - "A boolean signifying whether the stream " - "has been set playing yet.", - FALSE, - G_PARAM_CONSTRUCT | - G_PARAM_READWRITE | - G_PARAM_STATIC_NAME | - G_PARAM_STATIC_BLURB); - g_object_class_install_property (object_class, PROP_PLAYING, param_spec); - - param_spec = g_param_spec_uint ("combined-direction", - "Combined direction", - "An integer indicating the directions the stream currently sends in, " - "and the peers who have been asked to send.", - TP_MEDIA_STREAM_DIRECTION_NONE, - MAKE_COMBINED_DIRECTION (TP_MEDIA_STREAM_DIRECTION_BIDIRECTIONAL, - TP_MEDIA_STREAM_PENDING_LOCAL_SEND | - TP_MEDIA_STREAM_PENDING_REMOTE_SEND), - TP_MEDIA_STREAM_DIRECTION_BIDIRECTIONAL, - G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_NAME | - G_PARAM_STATIC_BLURB); - g_object_class_install_property (object_class, PROP_COMBINED_DIRECTION, - param_spec); - - /* signals not exported by D-Bus interface */ - signals[DESTROY] = - g_signal_new ("destroy", - G_OBJECT_CLASS_TYPE (gabble_media_stream_class), - G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, - 0, - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); - - signals[NEW_ACTIVE_CANDIDATE_PAIR] = - g_signal_new ("new-active-candidate-pair", - G_OBJECT_CLASS_TYPE (gabble_media_stream_class), - G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, - 0, - NULL, NULL, - gabble_marshal_VOID__STRING_STRING, - G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING); - - signals[NEW_NATIVE_CANDIDATE] = - g_signal_new ("new-native-candidate", - G_OBJECT_CLASS_TYPE (gabble_media_stream_class), - G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, - 0, - NULL, NULL, - gabble_marshal_VOID__STRING_BOXED, - G_TYPE_NONE, 2, G_TYPE_STRING, TP_TYPE_TRANSPORT_LIST); - - signals[SUPPORTED_CODECS] = - g_signal_new ("supported-codecs", - G_OBJECT_CLASS_TYPE (gabble_media_stream_class), - G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, - 0, - NULL, NULL, - g_cclosure_marshal_VOID__BOXED, - G_TYPE_NONE, 1, TP_TYPE_CODEC_LIST); - - signals[ERROR] = - g_signal_new ("error", - G_OBJECT_CLASS_TYPE (gabble_media_stream_class), - G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, - 0, - NULL, NULL, - gabble_marshal_VOID__UINT_STRING, - G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_STRING); -} - -void -gabble_media_stream_dispose (GObject *object) -{ - GabbleMediaStream *self = GABBLE_MEDIA_STREAM (object); - GabbleMediaStreamPrivate *priv = GABBLE_MEDIA_STREAM_GET_PRIVATE (self); - - if (priv->dispose_has_run) - return; - - _gabble_media_stream_close (self); - - g_signal_emit (self, signals[DESTROY], 0); - - priv->dispose_has_run = TRUE; - - if (G_OBJECT_CLASS (gabble_media_stream_parent_class)->dispose) - G_OBJECT_CLASS (gabble_media_stream_parent_class)->dispose (object); -} - -void -gabble_media_stream_finalize (GObject *object) -{ - GabbleMediaStream *self = GABBLE_MEDIA_STREAM (object); - GabbleMediaStreamPrivate *priv = GABBLE_MEDIA_STREAM_GET_PRIVATE (self); - - g_free (priv->object_path); - - g_value_unset (&priv->native_codecs); - g_value_unset (&priv->native_candidates); - - g_value_unset (&priv->remote_codecs); - g_value_unset (&priv->remote_candidates); - - G_OBJECT_CLASS (gabble_media_stream_parent_class)->finalize (object); -} - -/** - * gabble_media_stream_codec_choice - * - * Implements D-Bus method CodecChoice - * on interface org.freedesktop.Telepathy.Media.StreamHandler - */ -static void -gabble_media_stream_codec_choice (TpSvcMediaStreamHandler *iface, - guint codec_id, - DBusGMethodInvocation *context) -{ - GabbleMediaStream *self = GABBLE_MEDIA_STREAM (iface); - GabbleMediaStreamPrivate *priv; - - g_assert (GABBLE_IS_MEDIA_STREAM (self)); - - priv = GABBLE_MEDIA_STREAM_GET_PRIVATE (self); - - tp_svc_media_stream_handler_return_from_codec_choice (context); -} - - -gboolean -gabble_media_stream_error (GabbleMediaStream *self, - guint errno, - const gchar *message, - GError **error) -{ - GabbleMediaStreamPrivate *priv; - - g_assert (GABBLE_IS_MEDIA_STREAM (self)); - - priv = GABBLE_MEDIA_STREAM_GET_PRIVATE (self); - - GMS_DEBUG_WARNING (priv->session, - "Media.StreamHandler::Error called, error %u (%s) -- emitting signal", - errno, message); - - g_signal_emit (self, signals[ERROR], 0, errno, message); - - return TRUE; -} - - -/** - * gabble_media_stream_error - * - * Implements D-Bus method Error - * on interface org.freedesktop.Telepathy.Media.StreamHandler - */ -static void -gabble_media_stream_error_async (TpSvcMediaStreamHandler *iface, - guint errno, - const gchar *message, - DBusGMethodInvocation *context) -{ - GabbleMediaStream *self = GABBLE_MEDIA_STREAM (iface); - GError *error = NULL; - - if (gabble_media_stream_error (self, errno, message, &error)) - { - tp_svc_media_stream_handler_return_from_error (context); - } - else - { - dbus_g_method_return_error (context, error); - g_error_free (error); - } -} - - -/** - * gabble_media_stream_native_candidates_prepared - * - * Implements D-Bus method NativeCandidatesPrepared - * on interface org.freedesktop.Telepathy.Media.StreamHandler - */ -static void -gabble_media_stream_native_candidates_prepared (TpSvcMediaStreamHandler *iface, - DBusGMethodInvocation *context) -{ - GabbleMediaStream *self = GABBLE_MEDIA_STREAM (iface); - GabbleMediaStreamPrivate *priv; - - g_assert (GABBLE_IS_MEDIA_STREAM (self)); - - priv = GABBLE_MEDIA_STREAM_GET_PRIVATE (self); - - tp_svc_media_stream_handler_return_from_native_candidates_prepared (context); -} - - -/** - * gabble_media_stream_new_active_candidate_pair - * - * Implements D-Bus method NewActiveCandidatePair - * on interface org.freedesktop.Telepathy.Media.StreamHandler - */ -static void -gabble_media_stream_new_active_candidate_pair (TpSvcMediaStreamHandler *iface, - const gchar *native_candidate_id, - const gchar *remote_candidate_id, - DBusGMethodInvocation *context) -{ - GabbleMediaStream *self = GABBLE_MEDIA_STREAM (iface); - GabbleMediaStreamPrivate *priv; - - g_assert (GABBLE_IS_MEDIA_STREAM (self)); - - priv = GABBLE_MEDIA_STREAM_GET_PRIVATE (self); - - g_signal_emit (self, signals[NEW_ACTIVE_CANDIDATE_PAIR], 0, - native_candidate_id, remote_candidate_id); - - tp_svc_media_stream_handler_return_from_new_active_candidate_pair (context); -} - - -/** - * gabble_media_stream_new_native_candidate - * - * Implements D-Bus method NewNativeCandidate - * on interface org.freedesktop.Telepathy.Media.StreamHandler - */ -static void -gabble_media_stream_new_native_candidate (TpSvcMediaStreamHandler *iface, - const gchar *candidate_id, - const GPtrArray *transports, - DBusGMethodInvocation *context) -{ - GabbleMediaStream *self = GABBLE_MEDIA_STREAM (iface); - GabbleMediaStreamPrivate *priv; - JingleSessionState state; - GPtrArray *candidates; - GValue candidate = { 0, }; - GValueArray *transport; - const gchar *addr; - - g_assert (GABBLE_IS_MEDIA_STREAM (self)); - - priv = GABBLE_MEDIA_STREAM_GET_PRIVATE (self); - - g_object_get (priv->session, "state", &state, NULL); - - /* FIXME: maybe this should be an assertion in case the channel - * isn't closed early enough right now? */ - if (state > JS_STATE_ACTIVE) - { - DEBUG ("state > JS_STATE_ACTIVE, doing nothing"); - tp_svc_media_stream_handler_return_from_new_native_candidate (context); - return; - } - - candidates = g_value_get_boxed (&priv->native_candidates); - - g_value_init (&candidate, TP_TYPE_CANDIDATE_STRUCT); - g_value_take_boxed (&candidate, - dbus_g_type_specialized_construct (TP_TYPE_CANDIDATE_STRUCT)); - - dbus_g_type_struct_set (&candidate, - 0, candidate_id, - 1, transports, - G_MAXUINT); - - if (transports->len != 1) - { - GError only_one = { TP_ERRORS, TP_ERROR_NOT_IMPLEMENTED, "google p2p " - "connections only support the concept of one transport per " - "candidate" }; - GMS_DEBUG_WARNING (priv->session, "%s: number of transports was not 1; " - "rejecting", G_STRFUNC); - dbus_g_method_return_error (context, &only_one); - return; - } - - transport = g_ptr_array_index (transports, 0); - addr = g_value_get_string (g_value_array_get_nth (transport, 1)); - if (!strcmp (addr, "127.0.0.1")) - { - GMS_DEBUG_WARNING (priv->session, - "%s: ignoring native localhost candidate", G_STRFUNC); - tp_svc_media_stream_handler_return_from_new_native_candidate (context); - return; - } - - g_ptr_array_add (candidates, g_value_get_boxed (&candidate)); - - GMS_DEBUG_INFO (priv->session, - "put 1 native candidate from stream-engine into cache"); - - push_native_candidates (self); - - g_signal_emit (self, signals[NEW_NATIVE_CANDIDATE], 0, - candidate_id, transports); - - tp_svc_media_stream_handler_return_from_new_native_candidate (context); -} - -static void gabble_media_stream_set_local_codecs (TpSvcMediaStreamHandler *, - const GPtrArray *codecs, DBusGMethodInvocation *); - -/** - * gabble_media_stream_ready - * - * Implements D-Bus method Ready - * on interface org.freedesktop.Telepathy.Media.StreamHandler - */ -static void -gabble_media_stream_ready (TpSvcMediaStreamHandler *iface, - const GPtrArray *codecs, - DBusGMethodInvocation *context) -{ - GabbleMediaStream *self = GABBLE_MEDIA_STREAM (iface); - GabbleMediaStreamPrivate *priv; - - g_assert (GABBLE_IS_MEDIA_STREAM (self)); - - priv = GABBLE_MEDIA_STREAM_GET_PRIVATE (self); - - GMS_DEBUG_INFO (priv->session, "ready called"); - - g_object_set (self, "ready", TRUE, NULL); - - push_remote_codecs (self); - push_remote_candidates (self); - push_playing (self); - push_sending (self); - - /* set_local_codecs and ready return the same thing, so we can do... */ - gabble_media_stream_set_local_codecs (iface, codecs, context); -} - - -/** - * gabble_media_stream_set_local_codecs - * - * Implements D-Bus method SetLocalCodecs - * on interface org.freedesktop.Telepathy.Media.StreamHandler - */ -static void -gabble_media_stream_set_local_codecs (TpSvcMediaStreamHandler *iface, - const GPtrArray *codecs, - DBusGMethodInvocation *context) -{ - GabbleMediaStream *self = GABBLE_MEDIA_STREAM (iface); - GabbleMediaStreamPrivate *priv; - - g_assert (GABBLE_IS_MEDIA_STREAM (self)); - - priv = GABBLE_MEDIA_STREAM_GET_PRIVATE (self); - - GMS_DEBUG_INFO (priv->session, "putting list of all %d locally supported " - "codecs from stream-engine into cache", codecs->len); - - g_value_set_boxed (&priv->native_codecs, codecs); - - g_object_set (self, "got-local-codecs", TRUE, NULL); - - tp_svc_media_stream_handler_return_from_set_local_codecs (context); -} - - -/** - * gabble_media_stream_stream_state - * - * Implements D-Bus method StreamState - * on interface org.freedesktop.Telepathy.Media.StreamHandler - */ -static void -gabble_media_stream_stream_state (TpSvcMediaStreamHandler *iface, - guint connection_state, - DBusGMethodInvocation *context) -{ - GabbleMediaStream *self = GABBLE_MEDIA_STREAM (iface); - g_assert (GABBLE_IS_MEDIA_STREAM (self)); - - g_object_set (self, "connection-state", connection_state, NULL); - - tp_svc_media_stream_handler_return_from_stream_state (context); -} - - -/** - * gabble_media_stream_supported_codecs - * - * Implements D-Bus method SupportedCodecs - * on interface org.freedesktop.Telepathy.Media.StreamHandler - */ -static void -gabble_media_stream_supported_codecs (TpSvcMediaStreamHandler *iface, - const GPtrArray *codecs, - DBusGMethodInvocation *context) -{ - GabbleMediaStream *self = GABBLE_MEDIA_STREAM (iface); - GabbleMediaStreamPrivate *priv; - - g_assert (GABBLE_IS_MEDIA_STREAM (self)); - - priv = GABBLE_MEDIA_STREAM_GET_PRIVATE (self); - - GMS_DEBUG_INFO (priv->session, "got codec intersection containing %d " - "codecs from stream-engine", codecs->len); - - /* store the intersection for later on */ - g_value_set_boxed (&priv->native_codecs, codecs); - - g_signal_emit (self, signals[SUPPORTED_CODECS], 0, codecs); - - tp_svc_media_stream_handler_return_from_supported_codecs (context); -} - -static LmHandlerResult -candidates_msg_reply_cb (GabbleConnection *conn, - LmMessage *sent_msg, - LmMessage *reply_msg, - GObject *object, - gpointer user_data) -{ - GabbleMediaStream *stream = GABBLE_MEDIA_STREAM (object); - GabbleMediaStreamPrivate *priv; - - g_assert (GABBLE_IS_MEDIA_STREAM (stream)); - - priv = GABBLE_MEDIA_STREAM_GET_PRIVATE (stream); - - MSG_REPLY_CB_END_SESSION_IF_NOT_SUCCESSFUL (priv->session, - "candidates failed"); - - return LM_HANDLER_RESULT_REMOVE_MESSAGE; -} - -static void -_add_rtp_candidate_node (GabbleMediaSession *session, LmMessageNode *parent, - GValueArray *candidate) -{ - gchar *addr; - gchar *user; - gchar *pass; - gchar *port_str; - gchar *pref_str; - gchar *xml; - const gchar *type_str; - const gchar *candidate_id; - guint port; - gdouble pref; - TpMediaStreamBaseProto proto; - TpMediaStreamTransportType type; - const GPtrArray *transports; - GValue transport = { 0, }; - LmMessageNode *cand_node; - - candidate_id = g_value_get_string (g_value_array_get_nth (candidate, 0)); - transports = g_value_get_boxed (g_value_array_get_nth (candidate, 1)); - - /* jingle audio only supports the concept of one transport per candidate */ - g_assert (transports->len == 1); - - g_value_init (&transport, TP_TYPE_TRANSPORT_STRUCT); - g_value_set_static_boxed (&transport, g_ptr_array_index (transports, 0)); - - dbus_g_type_struct_get (&transport, - 1, &addr, - 2, &port, - 3, &proto, - 6, &pref, - 7, &type, - 8, &user, - 9, &pass, - G_MAXUINT); - - port_str = g_strdup_printf ("%d", port); - pref_str = g_strdup_printf ("%f", pref); - - switch (type) { - case TP_MEDIA_STREAM_TRANSPORT_TYPE_LOCAL: - type_str = "local"; - break; - case TP_MEDIA_STREAM_TRANSPORT_TYPE_DERIVED: - type_str = "stun"; - break; - case TP_MEDIA_STREAM_TRANSPORT_TYPE_RELAY: - type_str = "relay"; - break; - default: - g_error ("%s: TpMediaStreamTransportType has an invalid value", - G_STRFUNC); - return; - } - - cand_node = lm_message_node_add_child (parent, "candidate", NULL); - lm_message_node_set_attributes (cand_node, - "name", "rtp", - "address", addr, - "port", port_str, - "username", user, - "password", pass, - "preference", pref_str, - "protocol", (proto == TP_MEDIA_STREAM_BASE_PROTO_UDP) ? "udp" : "tcp", - "type", type_str, - "network", "0", - "generation", "0", - NULL); - - xml = lm_message_node_to_string (cand_node); - GMS_DEBUG_DUMP (session, - " from Telepathy D-Bus struct: [%s\"%s\", %s[%s1, \"%s\", %d, %s, " - "\"%s\", \"%s\", %f, %s, \"%s\", \"%s\"%s]]", - TP_ANSI_BOLD_OFF, candidate_id, TP_ANSI_BOLD_ON, TP_ANSI_BOLD_OFF, - addr, port, tp_protocols[proto], "RTP", "AVP", pref, tp_transports[type], - user, pass, TP_ANSI_BOLD_ON); - GMS_DEBUG_DUMP (session, - " to Jingle XML: [%s%s%s]", TP_ANSI_BOLD_OFF, xml, TP_ANSI_BOLD_ON); - g_free (xml); - - g_free (addr); - g_free (user); - g_free (pass); - g_free (port_str); - g_free (pref_str); -} - -static LmMessage * -_gabble_media_stream_message_new (GabbleMediaStream *stream, - const gchar *action, - LmMessageNode **content_node) -{ - GabbleMediaStreamPrivate *priv = GABBLE_MEDIA_STREAM_GET_PRIVATE (stream); - LmMessage *msg; - LmMessageNode *session_node = NULL; - - /* construct a session message */ - msg = _gabble_media_session_message_new (priv->session, action, - &session_node); - - /* add our content node to it if necessary */ - *content_node = _gabble_media_stream_add_content_node (stream, session_node); - - return msg; -} - - -static void -push_candidate (GabbleMediaStream *stream, GValueArray *candidate) -{ - GabbleMediaStreamPrivate *priv = GABBLE_MEDIA_STREAM_GET_PRIVATE (stream); - LmMessage *msg; - LmMessageNode *content_node, *transport_node; - const gchar *action; - - if (priv->mode == MODE_GOOGLE) - action = "candidates"; - else - action = "transport-info"; - - /* construct a base message */ - msg = _gabble_media_stream_message_new (stream, action, &content_node); - - /* for jingle, add a transport */ - transport_node = _gabble_media_stream_content_node_add_transport (stream, - content_node); - - /* add transport info to it */ - _add_rtp_candidate_node (priv->session, transport_node, candidate); - - GMS_DEBUG_INFO (priv->session, "sending jingle session action \"%s\" to " - "peer", action); - - /* send it */ - _gabble_connection_send_with_reply (priv->conn, msg, candidates_msg_reply_cb, - G_OBJECT (stream), NULL, NULL); - - /* clean up */ - lm_message_unref (msg); -} - -static void -push_native_candidates (GabbleMediaStream *stream) -{ - GabbleMediaStreamPrivate *priv; - GPtrArray *candidates; - guint i; - - g_assert (GABBLE_IS_MEDIA_STREAM (stream)); - - priv = GABBLE_MEDIA_STREAM_GET_PRIVATE (stream); - - if (stream->signalling_state == STREAM_SIG_STATE_NEW || - stream->signalling_state == STREAM_SIG_STATE_REMOVING) - return; - - candidates = g_value_get_boxed (&priv->native_candidates); - - for (i = 0; i < candidates->len; i++) - push_candidate (stream, g_ptr_array_index (candidates, i)); - - g_value_take_boxed (&priv->native_candidates, - dbus_g_type_specialized_construct (TP_TYPE_CANDIDATE_LIST)); -} - -void -_gabble_media_stream_close (GabbleMediaStream *stream) -{ - GabbleMediaStreamPrivate *priv; - - g_assert (GABBLE_IS_MEDIA_STREAM (stream)); - - priv = GABBLE_MEDIA_STREAM_GET_PRIVATE (stream); - - if (!priv->closed) - { - priv->closed = TRUE; - tp_svc_media_stream_handler_emit_close (stream); - } -} - -gboolean -_gabble_media_stream_post_remote_codecs (GabbleMediaStream *stream, - LmMessage *message, - LmMessageNode *desc_node, - GError **error) -{ - GabbleMediaStreamPrivate *priv; - LmMessageNode *node; - GPtrArray *codecs; - - g_assert (GABBLE_IS_MEDIA_STREAM (stream)); - - priv = GABBLE_MEDIA_STREAM_GET_PRIVATE (stream); - - codecs = g_value_get_boxed (&priv->remote_codecs); - - g_assert (codecs->len == 0); - - for (node = desc_node->children; node; node = node->next) - { - guchar id; - const gchar *name, *str; - guint clockrate, channels; - GHashTable *params; - GValue codec = { 0, }; - - if (tp_strdiff (node->name, "payload-type")) - continue; - - /* id of codec */ - str = lm_message_node_get_attribute (node, "id"); - if (str == NULL) - { - g_set_error (error, GABBLE_XMPP_ERROR, XMPP_ERROR_BAD_REQUEST, - "description has no ID"); - return FALSE; - } - - id = atoi (str); - - /* codec name */ - name = lm_message_node_get_attribute (node, "name"); - if (name == NULL) - { - name = ""; - } - - /* clock rate: jingle and newer GTalk */ - str = lm_message_node_get_attribute (node, "clockrate"); /* google */ - if (str == NULL) - str = lm_message_node_get_attribute (node, "rate"); /* jingle */ - - if (str != NULL) - { - clockrate = atoi (str); - } - else - { - clockrate = 0; - } - - /* number of channels: jingle only */ - str = lm_message_node_get_attribute (node, "channels"); - if (str != NULL) - { - channels = atoi (str); - } - else - { - channels = 1; - } - - params = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_free); - - /* bitrate: newer GTalk only */ - str = lm_message_node_get_attribute (node, "bitrate"); - if (str != NULL) - { - g_hash_table_insert (params, "bitrate", g_strdup (str)); - } - - g_value_init (&codec, TP_TYPE_CODEC_STRUCT); - g_value_take_boxed (&codec, - dbus_g_type_specialized_construct (TP_TYPE_CODEC_STRUCT)); - - dbus_g_type_struct_set (&codec, - 0, id, - 1, name, - 2, TP_MEDIA_STREAM_TYPE_AUDIO, - 3, clockrate, - 4, channels, - 5, params, - G_MAXUINT); - - g_ptr_array_add (codecs, g_value_get_boxed (&codec)); - } - - GMS_DEBUG_INFO (priv->session, "put %d remote codecs from peer into cache", - codecs->len); - - push_remote_codecs (stream); - - return TRUE; -} - -static void -push_remote_codecs (GabbleMediaStream *stream) -{ - GabbleMediaStreamPrivate *priv; - GPtrArray *codecs; - - g_assert (GABBLE_IS_MEDIA_STREAM (stream)); - - priv = GABBLE_MEDIA_STREAM_GET_PRIVATE (stream); - - if (!priv->ready) - return; - - codecs = g_value_get_boxed (&priv->remote_codecs); - if (codecs->len == 0) - return; - - GMS_DEBUG_EVENT (priv->session, "passing %d remote codecs to stream-engine", - codecs->len); - - tp_svc_media_stream_handler_emit_set_remote_codecs (stream, codecs); - - g_value_take_boxed (&priv->remote_codecs, - dbus_g_type_specialized_construct (TP_TYPE_CODEC_LIST)); -} - -gboolean -_gabble_media_stream_post_remote_candidates (GabbleMediaStream *stream, - LmMessage *message, - LmMessageNode *transport_node, - GError **error) -{ - GabbleMediaStreamPrivate *priv; - LmMessageNode *node; - const gchar *str; - GPtrArray *candidates; - - g_assert (GABBLE_IS_MEDIA_STREAM (stream)); - - priv = GABBLE_MEDIA_STREAM_GET_PRIVATE (stream); - - candidates = g_value_get_boxed (&priv->remote_candidates); - - for (node = transport_node->children; node; node = node->next) - { - gchar *candidate_id; - const gchar *name, *addr; - guint16 port; - TpMediaStreamBaseProto proto; - gdouble pref; - TpMediaStreamTransportType type; - const gchar *user, *pass; - guchar net, gen; - GValue candidate = { 0, }; - GPtrArray *transports; - GValue transport = { 0, }; - gchar *xml; - - if (tp_strdiff (node->name, "candidate")) - continue; - - /* - * Candidate - */ - - /* stream name */ - name = lm_message_node_get_attribute (node, "name"); - if (name == NULL || strcmp (name, "rtp") != 0) - goto FAILURE; - - - /* - * Transport - */ - - /* ip address */ - addr = lm_message_node_get_attribute (node, "address"); - if (addr == NULL) - goto FAILURE; - - /* port */ - str = lm_message_node_get_attribute (node, "port"); - if (str == NULL) - goto FAILURE; - port = atoi (str); - - /* protocol */ - str = lm_message_node_get_attribute (node, "protocol"); - if (str == NULL) - goto FAILURE; - - if (strcmp (str, "udp") == 0) - { - proto = TP_MEDIA_STREAM_BASE_PROTO_UDP; - } - else if (strcmp (str, "tcp") == 0) - { - proto = TP_MEDIA_STREAM_BASE_PROTO_TCP; - } - else if (strcmp (str, "ssltcp") == 0) - { - GMS_DEBUG_WARNING (priv->session, "%s: ssltcp candidates " - "not yet supported", G_STRFUNC); - continue; - } - else - goto FAILURE; - - /* protocol profile: hardcoded to "AVP" for now */ - - /* preference */ - str = lm_message_node_get_attribute (node, "preference"); - if (str == NULL) - goto FAILURE; - pref = g_ascii_strtod (str, NULL); - - /* type */ - str = lm_message_node_get_attribute (node, "type"); - if (str == NULL) - goto FAILURE; - - if (strcmp (str, "local") == 0) - { - type = TP_MEDIA_STREAM_TRANSPORT_TYPE_LOCAL; - } - else if (strcmp (str, "stun") == 0) - { - type = TP_MEDIA_STREAM_TRANSPORT_TYPE_DERIVED; - } - else if (strcmp (str, "relay") == 0) - { - type = TP_MEDIA_STREAM_TRANSPORT_TYPE_RELAY; - } - else - goto FAILURE; - - /* username */ - user = lm_message_node_get_attribute (node, "username"); - if (user == NULL) - goto FAILURE; - - /* password */ - pass = lm_message_node_get_attribute (node, "password"); - if (pass == NULL) - goto FAILURE; - - /* unknown */ - str = lm_message_node_get_attribute (node, "network"); - if (str == NULL) - goto FAILURE; - net = atoi (str); - - /* unknown */ - str = lm_message_node_get_attribute (node, "generation"); - if (str == NULL) - goto FAILURE; - gen = atoi (str); - - - g_value_init (&transport, TP_TYPE_TRANSPORT_STRUCT); - g_value_take_boxed (&transport, - dbus_g_type_specialized_construct (TP_TYPE_TRANSPORT_STRUCT)); - - dbus_g_type_struct_set (&transport, - 0, 1, /* component number */ - 1, addr, - 2, port, - 3, proto, - 4, "RTP", - 5, "AVP", - 6, pref, - 7, type, - 8, user, - 9, pass, - G_MAXUINT); - - transports = g_ptr_array_sized_new (1); - g_ptr_array_add (transports, g_value_get_boxed (&transport)); - - - g_value_init (&candidate, TP_TYPE_CANDIDATE_STRUCT); - g_value_take_boxed (&candidate, - dbus_g_type_specialized_construct (TP_TYPE_CANDIDATE_STRUCT)); - - /* FIXME: is this naming scheme sensible? */ - candidate_id = g_strdup_printf ("R%d", ++priv->remote_candidate_count); - - dbus_g_type_struct_set (&candidate, - 0, candidate_id, - 1, transports, - G_MAXUINT); - - g_ptr_array_add (candidates, g_value_get_boxed (&candidate)); - - xml = lm_message_node_to_string (node); - GMS_DEBUG_INFO (priv->session, - "put 1 remote candidate from peer into cache"); - GMS_DEBUG_DUMP (priv->session, " from Jingle XML: [%s%s%s]", - TP_ANSI_BOLD_OFF, xml, TP_ANSI_BOLD_ON); - GMS_DEBUG_DUMP (priv->session, - " to Telepathy D-Bus struct: [%s\"%s\", %s[%s1, \"%s\", %d, %s, " - "\"%s\", \"%s\", %f, %s, \"%s\", \"%s\"%s]]", - TP_ANSI_BOLD_OFF, candidate_id, TP_ANSI_BOLD_ON, - TP_ANSI_BOLD_OFF, addr, port, tp_protocols[proto], - "RTP", "AVP", pref, tp_transports[type], - user, pass, TP_ANSI_BOLD_ON); - g_free (xml); - - g_free (candidate_id); - } - -/*SUCCESS:*/ - push_remote_candidates (stream); - - return TRUE; - -FAILURE: - g_set_error (error, GABBLE_XMPP_ERROR, XMPP_ERROR_BAD_REQUEST, - "unable to parse candidate"); - - return FALSE; -} - -static void -push_remote_candidates (GabbleMediaStream *stream) -{ - GabbleMediaStreamPrivate *priv; - GPtrArray *candidates; - guint i; - - g_assert (GABBLE_IS_MEDIA_STREAM (stream)); - - priv = GABBLE_MEDIA_STREAM_GET_PRIVATE (stream); - - candidates = g_value_get_boxed (&priv->remote_candidates); - - if (candidates->len == 0) - return; - - if (!priv->ready) - return; - - for (i = 0; i < candidates->len; i++) - { - GValueArray *candidate = g_ptr_array_index (candidates, i); - const gchar *candidate_id; - const GPtrArray *transports; - - candidate_id = g_value_get_string (g_value_array_get_nth (candidate, 0)); - transports = g_value_get_boxed (g_value_array_get_nth (candidate, 1)); - - GMS_DEBUG_EVENT (priv->session, "passing 1 remote candidate " - "to stream-engine"); - - tp_svc_media_stream_handler_emit_add_remote_candidate ( - stream, candidate_id, transports); - } - - g_value_take_boxed (&priv->remote_candidates, - dbus_g_type_specialized_construct (TP_TYPE_CANDIDATE_LIST)); -} - -static void -push_playing (GabbleMediaStream *stream) -{ - GabbleMediaStreamPrivate *priv; - - g_assert (GABBLE_IS_MEDIA_STREAM (stream)); - - priv = GABBLE_MEDIA_STREAM_GET_PRIVATE (stream); - - if (!priv->ready) - return; - - GMS_DEBUG_INFO (priv->session, "stream %s emitting SetStreamPlaying(%s)", - stream->name, stream->playing ? "true" : "false"); - - tp_svc_media_stream_handler_emit_set_stream_playing ( - stream, stream->playing); -} - -static void -push_sending (GabbleMediaStream *stream) -{ - GabbleMediaStreamPrivate *priv; - - g_assert (GABBLE_IS_MEDIA_STREAM (stream)); - - priv = GABBLE_MEDIA_STREAM_GET_PRIVATE (stream); - - if (!priv->ready) - return; - - GMS_DEBUG_INFO (priv->session, "stream %s emitting SetStreamSending(%s)", - stream->name, priv->sending ? "true" : "false"); - - tp_svc_media_stream_handler_emit_set_stream_sending ( - stream, priv->sending); -} - -/* - * oh sweet g_hash_table_foreach how beautiful thou be'st - * - * _\ / ^/ - * \/ \// 7_ __ - * ( 7 ) (__) (__) - * ^\\ |/__/___/ - * \\/_/ | <-- TP-cable kindly provided by Mika N. - * \ / O - * || /|\ - * || / \ - * || - * ____||_____________ - */ - -typedef struct { - GabbleMediaStreamPrivate *priv; - LmMessageNode *pt_node; -} CodecParamsFromTpContext; - -static const gchar *video_codec_params[] = { - "x", "y", "width", "height", "layer", "transparent", -}; - -static void -codec_params_from_tp_foreach (gpointer key, gpointer value, gpointer user_data) -{ - CodecParamsFromTpContext *ctx = user_data; - GabbleMediaStreamPrivate *priv = ctx->priv; - const gchar *pname = key, *pvalue = value; - - if (priv->media_type == TP_MEDIA_STREAM_TYPE_AUDIO) - { - if (priv->mode == MODE_GOOGLE && strcmp (pname, "bitrate") == 0) - { - lm_message_node_set_attribute (ctx->pt_node, pname, pvalue); - return; - } - } - else if (priv->mode == MODE_JINGLE) - { - gint i; - - for (i = 0; video_codec_params[i] != NULL; i++) - { - if (strcmp (pname, video_codec_params[i]) == 0) - { - lm_message_node_set_attribute (ctx->pt_node, pname, pvalue); - return; - } - } - } - - DEBUG ("ignoring %s=%s for %s %s stream", pname, pvalue, - (priv->mode == MODE_JINGLE) ? "jingle" : "google", - (priv->media_type == TP_MEDIA_STREAM_TYPE_AUDIO) ? "audio" : "video"); -} - -LmMessageNode * -_gabble_media_stream_add_content_node (GabbleMediaStream *stream, - LmMessageNode *session_node) -{ - GabbleMediaStreamPrivate *priv = GABBLE_MEDIA_STREAM_GET_PRIVATE (stream); - LmMessageNode *node = session_node; - - /* add our content node to it if in jingle mode */ - if (priv->mode == MODE_JINGLE) - { - node = lm_message_node_add_child (session_node, "content", NULL); - lm_message_node_set_attribute (node, "name", stream->name); - - if (priv->session->initiator == stream->initiator) - lm_message_node_set_attribute (node, "creator", "initiator"); - else - lm_message_node_set_attribute (node, "creator", "responder"); - } - - return node; -} - -void -_gabble_media_stream_content_node_add_description (GabbleMediaStream *stream, - LmMessageNode *content_node) -{ - GabbleMediaStreamPrivate *priv; - const GPtrArray *codecs; - LmMessageNode *desc_node; - guint i; - const gchar *xmlns; - - g_assert (GABBLE_IS_MEDIA_STREAM (stream)); - - priv = GABBLE_MEDIA_STREAM_GET_PRIVATE (stream); - - codecs = g_value_get_boxed (&priv->native_codecs); - - desc_node = lm_message_node_add_child (content_node, "description", NULL); - - if (priv->mode == MODE_GOOGLE) - xmlns = NS_GOOGLE_SESSION_PHONE; - else if (priv->media_type == TP_MEDIA_STREAM_TYPE_VIDEO) - xmlns = NS_JINGLE_DESCRIPTION_VIDEO; - else - xmlns = NS_JINGLE_DESCRIPTION_AUDIO; - - lm_message_node_set_attribute (desc_node, "xmlns", xmlns); - - for (i = 0; i < codecs->len; i++) - { - GValue codec = { 0, }; - guint id, clock_rate, channels; - gchar *name, buf[16]; - GHashTable *params; - LmMessageNode *pt_node; - CodecParamsFromTpContext ctx; - - g_value_init (&codec, TP_TYPE_CODEC_STRUCT); - g_value_set_static_boxed (&codec, g_ptr_array_index (codecs, i)); - - dbus_g_type_struct_get (&codec, - 0, &id, - 1, &name, - 3, &clock_rate, - 4, &channels, - 5, ¶ms, - G_MAXUINT); - - /* create a sub-node called "payload-type" and fill it */ - pt_node = lm_message_node_add_child (desc_node, "payload-type", NULL); - - /* id: required */ - sprintf (buf, "%u", id); - lm_message_node_set_attribute (pt_node, "id", buf); - - /* name: optional */ - if (*name != '\0') - { - lm_message_node_set_attribute (pt_node, "name", name); - } - - /* clock rate: optional */ - if (clock_rate != 0) - { - sprintf (buf, "%u", clock_rate); - lm_message_node_set_attribute (pt_node, - (priv->mode == MODE_GOOGLE) ? "clockrate" : "rate", buf); - } - - /* number of channels: optional, jingle only */ - /* FIXME: is it? */ - if (channels != 0 && priv->mode == MODE_JINGLE) - { - sprintf (buf, "%u", channels); - lm_message_node_set_attribute (pt_node, "channels", buf); - } - - /* parse the optional params */ - ctx.priv = priv; - ctx.pt_node = pt_node; - g_hash_table_foreach (params, codec_params_from_tp_foreach, &ctx); - - /* clean up */ - g_free (name); - g_hash_table_destroy (params); - } -} - -LmMessageNode * -_gabble_media_stream_content_node_add_transport (GabbleMediaStream *stream, - LmMessageNode *content_node) -{ - GabbleMediaStreamPrivate *priv; - LmMessageNode *node; - - g_assert (GABBLE_IS_MEDIA_STREAM (stream)); - - priv = GABBLE_MEDIA_STREAM_GET_PRIVATE (stream); - - if (priv->mode != MODE_JINGLE) - return content_node; - - node = lm_message_node_add_child (content_node, "transport", NULL); - - lm_message_node_set_attribute (node, "xmlns", NS_GOOGLE_TRANSPORT_P2P); - - return node; -} - -void -_gabble_media_stream_update_sending (GabbleMediaStream *stream, - gboolean start_sending) -{ - GabbleMediaStreamPrivate *priv; - gboolean new_sending; - - g_assert (GABBLE_IS_MEDIA_STREAM (stream)); - - priv = GABBLE_MEDIA_STREAM_GET_PRIVATE (stream); - - new_sending = - ((stream->combined_direction & TP_MEDIA_STREAM_DIRECTION_SEND) != 0); - - if (priv->sending == new_sending) - return; - - if (new_sending && !start_sending) - return; - - priv->sending = new_sending; - push_sending (stream); -} - -static void -stream_handler_iface_init (gpointer g_iface, gpointer iface_data) -{ - TpSvcMediaStreamHandlerClass *klass = - (TpSvcMediaStreamHandlerClass *)g_iface; - -#define IMPLEMENT(x,suffix) tp_svc_media_stream_handler_implement_##x (\ - klass, gabble_media_stream_##x##suffix) - IMPLEMENT(codec_choice,); - IMPLEMENT(error,_async); - IMPLEMENT(native_candidates_prepared,); - IMPLEMENT(new_active_candidate_pair,); - IMPLEMENT(new_native_candidate,); - IMPLEMENT(ready,); - IMPLEMENT(set_local_codecs,); - IMPLEMENT(stream_state,); - IMPLEMENT(supported_codecs,); -#undef IMPLEMENT -} diff --git a/src/gabble-media-stream.h b/src/gabble-media-stream.h deleted file mode 100644 index fc96b8b2b..000000000 --- a/src/gabble-media-stream.h +++ /dev/null @@ -1,143 +0,0 @@ -/* - * gabble-media-stream.h - Header for GabbleMediaStream - * Copyright (C) 2006 Collabora Ltd. - * Copyright (C) 2006 Nokia Corporation - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef __GABBLE_MEDIA_STREAM_H__ -#define __GABBLE_MEDIA_STREAM_H__ - -#include <glib-object.h> -#include <loudmouth/loudmouth.h> - -#include "gabble-types.h" -#include <telepathy-glib/enums.h> - -G_BEGIN_DECLS - -typedef enum -{ - STREAM_SIG_STATE_NEW, - STREAM_SIG_STATE_SENT, - STREAM_SIG_STATE_ACKNOWLEDGED, - STREAM_SIG_STATE_REMOVING -} StreamSignallingState; - -typedef guint32 CombinedStreamDirection; - -typedef struct _GabbleMediaStream GabbleMediaStream; -typedef struct _GabbleMediaStreamClass GabbleMediaStreamClass; - -struct _GabbleMediaStreamClass { - GObjectClass parent_class; -}; - -struct _GabbleMediaStream { - GObject parent; - - gchar *name; - - JingleInitiator initiator; - TpMediaStreamState connection_state; - StreamSignallingState signalling_state; - - CombinedStreamDirection combined_direction; - gboolean got_local_codecs; - gboolean playing; - - gpointer priv; -}; - -GType gabble_media_stream_get_type (void); - -/* TYPE MACROS */ -#define GABBLE_TYPE_MEDIA_STREAM \ - (gabble_media_stream_get_type ()) -#define GABBLE_MEDIA_STREAM(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_MEDIA_STREAM, \ - GabbleMediaStream)) -#define GABBLE_MEDIA_STREAM_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_MEDIA_STREAM, \ - GabbleMediaStreamClass)) -#define GABBLE_IS_MEDIA_STREAM(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_MEDIA_STREAM)) -#define GABBLE_IS_MEDIA_STREAM_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_MEDIA_STREAM)) -#define GABBLE_MEDIA_STREAM_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_MEDIA_STREAM, \ - GabbleMediaStreamClass)) - -#define TP_TYPE_TRANSPORT_STRUCT (dbus_g_type_get_struct ("GValueArray", \ - G_TYPE_UINT, \ - G_TYPE_STRING, \ - G_TYPE_UINT, \ - G_TYPE_UINT, \ - G_TYPE_STRING, \ - G_TYPE_STRING, \ - G_TYPE_DOUBLE, \ - G_TYPE_UINT, \ - G_TYPE_STRING, \ - G_TYPE_STRING, \ - G_TYPE_INVALID)) -#define TP_TYPE_TRANSPORT_LIST (dbus_g_type_get_collection ("GPtrArray", \ - TP_TYPE_TRANSPORT_STRUCT)) -#define TP_TYPE_CANDIDATE_STRUCT (dbus_g_type_get_struct ("GValueArray", \ - G_TYPE_STRING, \ - TP_TYPE_TRANSPORT_LIST, \ - G_TYPE_INVALID)) -#define TP_TYPE_CANDIDATE_LIST (dbus_g_type_get_collection ("GPtrArray", \ - TP_TYPE_CANDIDATE_STRUCT)) - -#define TP_TYPE_CODEC_STRUCT (dbus_g_type_get_struct ("GValueArray", \ - G_TYPE_UINT, \ - G_TYPE_STRING, \ - G_TYPE_UINT, \ - G_TYPE_UINT, \ - G_TYPE_UINT, \ - DBUS_TYPE_G_STRING_STRING_HASHTABLE, \ - G_TYPE_INVALID)) -#define TP_TYPE_CODEC_LIST (dbus_g_type_get_collection ("GPtrArray", \ - TP_TYPE_CODEC_STRUCT)) - -#define COMBINED_DIRECTION_GET_DIRECTION(d) \ - ((TpMediaStreamDirection) ((d) & TP_MEDIA_STREAM_DIRECTION_BIDIRECTIONAL)) -#define COMBINED_DIRECTION_GET_PENDING_SEND(d) \ - ((TpMediaStreamPendingSend) ((d) >> 2)) -#define MAKE_COMBINED_DIRECTION(d, p) \ - ((CombinedStreamDirection) ((d) | ((p) << 2))) - -gboolean gabble_media_stream_error (GabbleMediaStream *self, guint errno, - const gchar *message, GError **error); - -void _gabble_media_stream_close (GabbleMediaStream *close); -gboolean _gabble_media_stream_post_remote_codecs (GabbleMediaStream *stream, - LmMessage *message, LmMessageNode *desc_node, GError **error); -gboolean _gabble_media_stream_post_remote_candidates ( - GabbleMediaStream *stream, LmMessage *message, - LmMessageNode *transport_node, GError **error); -LmMessageNode *_gabble_media_stream_add_content_node ( - GabbleMediaStream *stream, LmMessageNode *session_node); -void _gabble_media_stream_content_node_add_description ( - GabbleMediaStream *stream, LmMessageNode *content_node); -LmMessageNode *_gabble_media_stream_content_node_add_transport ( - GabbleMediaStream *stream, LmMessageNode *content_node); -void _gabble_media_stream_update_sending (GabbleMediaStream *stream, - gboolean start_sending); - -G_END_DECLS - -#endif /* #ifndef __GABBLE_MEDIA_STREAM_H__*/ diff --git a/src/gabble-muc-channel.c b/src/gabble-muc-channel.c deleted file mode 100644 index 7916de5b0..000000000 --- a/src/gabble-muc-channel.c +++ /dev/null @@ -1,2807 +0,0 @@ -/* - * gabble-muc-channel.c - Source for GabbleMucChannel - * Copyright (C) 2006 Collabora Ltd. - * Copyright (C) 2006 Nokia Corporation - * @author Ole Andre Vadla Ravnaas <ole.andre.ravnaas@collabora.co.uk> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "gabble-muc-channel.h" - -#include <dbus/dbus-glib.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> - -#define DEBUG_FLAG GABBLE_DEBUG_MUC - -#include <telepathy-glib/debug-ansi.h> -#include "debug.h" -#include "disco.h" -#include "gabble-connection.h" -#include "gabble-error.h" -#include "namespaces.h" -#include "util.h" - -#include <telepathy-glib/dbus.h> -#include <telepathy-glib/errors.h> -#include <telepathy-glib/interfaces.h> -#include <telepathy-glib/channel-iface.h> - -#define DEFAULT_JOIN_TIMEOUT (180 * 1000) -#define MAX_NICK_RETRIES 3 - -#define PROPS_POLL_INTERVAL_LOW (60 * 1000 * 5) -#define PROPS_POLL_INTERVAL_HIGH (60 * 1000) - -static void channel_iface_init (gpointer, gpointer); -static void password_iface_init (gpointer, gpointer); -static void text_iface_init (gpointer, gpointer); -static void chat_state_iface_init (gpointer, gpointer); - -G_DEFINE_TYPE_WITH_CODE (GabbleMucChannel, gabble_muc_channel, - G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL, - channel_iface_init); - G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_PROPERTIES_INTERFACE, - tp_properties_mixin_iface_init); - G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_GROUP, - tp_group_mixin_iface_init); - G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_PASSWORD, - password_iface_init); - G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_TYPE_TEXT, - text_iface_init); - G_IMPLEMENT_INTERFACE (TP_TYPE_CHANNEL_IFACE, NULL); - G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_CHAT_STATE, - chat_state_iface_init) - ) - -/* signal enum */ -enum -{ - READY, - JOIN_ERROR, - LAST_SIGNAL -}; - -static guint signals[LAST_SIGNAL] = {0}; - -/* properties */ -enum -{ - PROP_OBJECT_PATH = 1, - PROP_CHANNEL_TYPE, - PROP_HANDLE_TYPE, - PROP_HANDLE, - PROP_CONNECTION, - PROP_STATE, - PROP_INVITE_SELF, - LAST_PROPERTY -}; - -typedef enum { - MUC_STATE_CREATED = 0, - MUC_STATE_INITIATED, - MUC_STATE_AUTH, - MUC_STATE_JOINED, - MUC_STATE_ENDED, -} GabbleMucState; - -#ifdef ENABLE_DEBUG -static const gchar *muc_states[] = -{ - "MUC_STATE_CREATED", - "MUC_STATE_INITIATED", - "MUC_STATE_AUTH", - "MUC_STATE_JOINED", - "MUC_STATE_ENDED", -}; -#endif - -/* role and affiliation enums */ -typedef enum { - ROLE_NONE = 0, - ROLE_VISITOR, - ROLE_PARTICIPANT, - ROLE_MODERATOR, - - NUM_ROLES, - - INVALID_ROLE, -} GabbleMucRole; - -typedef enum { - AFFILIATION_NONE = 0, - AFFILIATION_MEMBER, - AFFILIATION_ADMIN, - AFFILIATION_OWNER, - - NUM_AFFILIATIONS, - - INVALID_AFFILIATION, -} GabbleMucAffiliation; - -static const gchar *muc_roles[NUM_ROLES] = -{ - "none", - "visitor", - "participant", - "moderator", -}; - -static const gchar *muc_affiliations[NUM_AFFILIATIONS] = -{ - "none", - "member", - "admin", - "owner", -}; - -/* room properties */ -enum -{ - ROOM_PROP_ANONYMOUS = 0, - ROOM_PROP_INVITE_ONLY, - ROOM_PROP_MODERATED, - ROOM_PROP_NAME, - ROOM_PROP_DESCRIPTION, - ROOM_PROP_PASSWORD, - ROOM_PROP_PASSWORD_REQUIRED, - ROOM_PROP_PERSISTENT, - ROOM_PROP_PRIVATE, - ROOM_PROP_SUBJECT, - ROOM_PROP_SUBJECT_CONTACT, - ROOM_PROP_SUBJECT_TIMESTAMP, - - NUM_ROOM_PROPS, - - INVALID_ROOM_PROP, -}; - -const TpPropertySignature room_property_signatures[NUM_ROOM_PROPS] = { - { "anonymous", G_TYPE_BOOLEAN }, /* impl: READ, WRITE */ - { "invite-only", G_TYPE_BOOLEAN }, /* impl: READ, WRITE */ - { "moderated", G_TYPE_BOOLEAN }, /* impl: READ, WRITE */ - { "name", G_TYPE_STRING }, /* impl: READ, WRITE */ - { "description", G_TYPE_STRING }, /* impl: READ, WRITE */ - { "password", G_TYPE_STRING }, /* impl: WRITE */ - { "password-required", G_TYPE_BOOLEAN }, /* impl: READ, WRITE */ - { "persistent", G_TYPE_BOOLEAN }, /* impl: READ, WRITE */ - { "private", G_TYPE_BOOLEAN }, /* impl: READ, WRITE */ - { "subject", G_TYPE_STRING }, /* impl: READ, WRITE */ - { "subject-contact", G_TYPE_UINT }, /* impl: READ */ - { "subject-timestamp", G_TYPE_UINT }, /* impl: READ */ -}; - -/* private structures */ - -typedef struct _GabbleMucChannelPrivate GabbleMucChannelPrivate; - -struct _GabbleMucChannelPrivate -{ - GabbleConnection *conn; - gchar *object_path; - - GabbleMucState state; - - guint join_timer_id; - guint poll_timer_id; - - TpChannelPasswordFlags password_flags; - DBusGMethodInvocation *password_ctx; - gchar *password; - - TpHandle handle; - const gchar *jid; - - guint nick_retry_count; - GString *self_jid; - GabbleMucRole self_role; - GabbleMucAffiliation self_affil; - - guint recv_id; - - TpPropertiesContext *properties_ctx; - - gboolean ready_emitted; - - gboolean closed; - gboolean dispose_has_run; - - gboolean invite_self; -}; - -#define GABBLE_MUC_CHANNEL_GET_PRIVATE(o) \ - (G_TYPE_INSTANCE_GET_PRIVATE ((o), GABBLE_TYPE_MUC_CHANNEL, \ - GabbleMucChannelPrivate)) - -static void -gabble_muc_channel_init (GabbleMucChannel *obj) -{ - /* do nothing? */ -} - -static void contact_handle_to_room_identity (GabbleMucChannel *, TpHandle, - TpHandle *, GString **); - -static GObject * -gabble_muc_channel_constructor (GType type, guint n_props, - GObjectConstructParam *props) -{ - GObject *obj; - GabbleMucChannelPrivate *priv; - TpBaseConnection *conn; - DBusGConnection *bus; - TpHandleRepoIface *room_handles, *contact_handles; - TpHandle self_handle; - - obj = G_OBJECT_CLASS (gabble_muc_channel_parent_class)-> - constructor (type, n_props, props); - - priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (obj); - conn = (TpBaseConnection *)priv->conn; - - room_handles = tp_base_connection_get_handles (conn, TP_HANDLE_TYPE_ROOM); - contact_handles = tp_base_connection_get_handles (conn, - TP_HANDLE_TYPE_CONTACT); - - /* ref our room handle */ - tp_handle_ref (room_handles, priv->handle); - - /* get the room's jid */ - priv->jid = tp_handle_inspect (room_handles, priv->handle); - - /* get our own identity in the room */ - contact_handle_to_room_identity (GABBLE_MUC_CHANNEL (obj), - conn->self_handle, &self_handle, &priv->self_jid); - - tp_handle_ref (contact_handles, self_handle); - - /* initialize our own role and affiliation */ - priv->self_role = ROLE_NONE; - priv->self_affil = AFFILIATION_NONE; - - /* register object on the bus */ - bus = tp_get_bus (); - dbus_g_connection_register_g_object (bus, priv->object_path, obj); - - /* initialize group mixin */ - tp_group_mixin_init (obj, - G_STRUCT_OFFSET (GabbleMucChannel, group), - contact_handles, self_handle); - - /* set initial group flags */ - tp_group_mixin_change_flags (obj, - TP_CHANNEL_GROUP_FLAG_CHANNEL_SPECIFIC_HANDLES | - TP_CHANNEL_GROUP_FLAG_CAN_ADD, - 0); - - /* initialize properties mixin */ - tp_properties_mixin_init (obj, G_STRUCT_OFFSET ( - GabbleMucChannel, properties)); - - /* initialize text mixin */ - gabble_text_mixin_init (obj, G_STRUCT_OFFSET (GabbleMucChannel, text), - contact_handles, FALSE); - - tp_text_mixin_set_message_types (obj, - TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL, - TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION, - TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE, - G_MAXUINT); - - /* add ourselves to group mixin if needed */ - if (priv->invite_self) - { - GError *error = NULL; - GArray *members = g_array_sized_new (FALSE, FALSE, sizeof (TpHandle), 1); - g_array_append_val (members, self_handle); - tp_group_mixin_add_members (obj, members, - "", &error); - g_assert (error == NULL); - g_array_free (members, TRUE); - } - return obj; -} - -static void -properties_disco_cb (GabbleDisco *disco, - GabbleDiscoRequest *request, - const gchar *jid, - const gchar *node, - LmMessageNode *query_result, - GError *error, - gpointer user_data) -{ - GabbleMucChannel *chan = user_data; - GabbleMucChannelPrivate *priv; - TpIntSet *changed_props_val, *changed_props_flags; - LmMessageNode *lm_node; - const gchar *str; - GValue val = { 0, }; - - g_assert (GABBLE_IS_MUC_CHANNEL (chan)); - - priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (chan); - - if (error) - { - DEBUG ("got error %s", error->message); - return; - } - - changed_props_val = tp_intset_sized_new (NUM_ROOM_PROPS); - changed_props_flags = tp_intset_sized_new (NUM_ROOM_PROPS); - - /* - * Update room definition. - */ - - /* ROOM_PROP_NAME */ - lm_node = lm_message_node_get_child (query_result, "identity"); - if (lm_node) - { - const gchar *type, *category, *name; - - type = lm_message_node_get_attribute (lm_node, "type"); - category = lm_message_node_get_attribute (lm_node, "category"); - name = lm_message_node_get_attribute (lm_node, "name"); - - if (NULL != type && 0 == strcmp (type, "text") && - NULL != category && 0 == strcmp (category, "conference") && - NULL != name) - { - g_value_init (&val, G_TYPE_STRING); - g_value_set_string (&val, name); - - tp_properties_mixin_change_value (G_OBJECT (chan), ROOM_PROP_NAME, - &val, changed_props_val); - - tp_properties_mixin_change_flags (G_OBJECT (chan), ROOM_PROP_NAME, - TP_PROPERTY_FLAG_READ, - 0, changed_props_flags); - - g_value_unset (&val); - } - } - - for (lm_node = query_result->children; lm_node; lm_node = lm_node->next) - { - guint prop_id = INVALID_ROOM_PROP; - - if (strcmp (lm_node->name, "feature") == 0) - { - str = lm_message_node_get_attribute (lm_node, "var"); - if (str == NULL) - continue; - - /* ROOM_PROP_ANONYMOUS */ - if (strcmp (str, "muc_nonanonymous") == 0) - { - prop_id = ROOM_PROP_ANONYMOUS; - g_value_init (&val, G_TYPE_BOOLEAN); - g_value_set_boolean (&val, FALSE); - } - else if (strcmp (str, "muc_semianonymous") == 0 || - strcmp (str, "muc_anonymous") == 0) - { - prop_id = ROOM_PROP_ANONYMOUS; - g_value_init (&val, G_TYPE_BOOLEAN); - g_value_set_boolean (&val, TRUE); - } - - /* ROOM_PROP_INVITE_ONLY */ - else if (strcmp (str, "muc_open") == 0) - { - prop_id = ROOM_PROP_INVITE_ONLY; - g_value_init (&val, G_TYPE_BOOLEAN); - g_value_set_boolean (&val, FALSE); - } - else if (strcmp (str, "muc_membersonly") == 0) - { - prop_id = ROOM_PROP_INVITE_ONLY; - g_value_init (&val, G_TYPE_BOOLEAN); - g_value_set_boolean (&val, TRUE); - } - - /* ROOM_PROP_MODERATED */ - else if (strcmp (str, "muc_unmoderated") == 0) - { - prop_id = ROOM_PROP_MODERATED; - g_value_init (&val, G_TYPE_BOOLEAN); - g_value_set_boolean (&val, FALSE); - } - else if (strcmp (str, "muc_moderated") == 0) - { - prop_id = ROOM_PROP_MODERATED; - g_value_init (&val, G_TYPE_BOOLEAN); - g_value_set_boolean (&val, TRUE); - } - - /* ROOM_PROP_PASSWORD_REQUIRED */ - else if (strcmp (str, "muc_unsecure") == 0 || - strcmp (str, "muc_unsecured") == 0) - { - prop_id = ROOM_PROP_PASSWORD_REQUIRED; - g_value_init (&val, G_TYPE_BOOLEAN); - g_value_set_boolean (&val, FALSE); - } - else if (strcmp (str, "muc_passwordprotected") == 0) - { - prop_id = ROOM_PROP_PASSWORD_REQUIRED; - g_value_init (&val, G_TYPE_BOOLEAN); - g_value_set_boolean (&val, TRUE); - } - - /* ROOM_PROP_PERSISTENT */ - else if (strcmp (str, "muc_temporary") == 0) - { - prop_id = ROOM_PROP_PERSISTENT; - g_value_init (&val, G_TYPE_BOOLEAN); - g_value_set_boolean (&val, FALSE); - } - else if (strcmp (str, "muc_persistent") == 0) - { - prop_id = ROOM_PROP_PERSISTENT; - g_value_init (&val, G_TYPE_BOOLEAN); - g_value_set_boolean (&val, TRUE); - } - - /* ROOM_PROP_PRIVATE */ - else if (strcmp (str, "muc_public") == 0) - { - prop_id = ROOM_PROP_PRIVATE; - g_value_init (&val, G_TYPE_BOOLEAN); - g_value_set_boolean (&val, FALSE); - } - else if (strcmp (str, "muc_hidden") == 0) - { - prop_id = ROOM_PROP_PRIVATE; - g_value_init (&val, G_TYPE_BOOLEAN); - g_value_set_boolean (&val, TRUE); - } - - /* Ignored */ - else if (strcmp (str, NS_MUC) == 0) - { - } - - /* Unhandled */ - else - { - g_warning ("%s: unhandled feature '%s'", G_STRFUNC, str); - } - } - else if (strcmp (lm_node->name, "x") == 0) - { - if (lm_message_node_has_namespace (lm_node, NS_X_DATA, NULL)) - { - LmMessageNode *field, *value_node; - - for (field = lm_node->children; field; field = field->next) - { - if (strcmp (field->name, "field") != 0) - continue; - - str = lm_message_node_get_attribute (field, "var"); - if (str == NULL) - continue; - - if (strcmp (str, "muc#roominfo_description") != 0) - continue; - - value_node = lm_message_node_get_child (field, "value"); - if (value_node == NULL) - continue; - - str = lm_message_node_get_value (value_node); - if (str == NULL) - { - str = ""; - } - - prop_id = ROOM_PROP_DESCRIPTION; - g_value_init (&val, G_TYPE_STRING); - g_value_set_string (&val, str); - } - } - } - - if (prop_id != INVALID_ROOM_PROP) - { - tp_properties_mixin_change_value (G_OBJECT (chan), prop_id, &val, - changed_props_val); - - tp_properties_mixin_change_flags (G_OBJECT (chan), prop_id, - TP_PROPERTY_FLAG_READ, - 0, changed_props_flags); - - g_value_unset (&val); - } - } - - /* - * Emit signals. - */ - tp_properties_mixin_emit_changed (G_OBJECT (chan), changed_props_val); - tp_properties_mixin_emit_flags (G_OBJECT (chan), changed_props_flags); - tp_intset_destroy (changed_props_val); - tp_intset_destroy (changed_props_flags); -} - -static void -room_properties_update (GabbleMucChannel *chan) -{ - GabbleMucChannelPrivate *priv; - GError *error = NULL; - - g_assert (GABBLE_IS_MUC_CHANNEL (chan)); - - priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (chan); - - if (gabble_disco_request (priv->conn->disco, GABBLE_DISCO_TYPE_INFO, - priv->jid, NULL, properties_disco_cb, chan, G_OBJECT (chan), - &error) == NULL) - { - g_warning ("%s: disco query failed: '%s'", G_STRFUNC, error->message); - g_error_free (error); - } -} - -static void -contact_handle_to_room_identity (GabbleMucChannel *chan, TpHandle main_handle, - TpHandle *room_handle, GString **room_jid) -{ - GabbleMucChannelPrivate *priv; - TpBaseConnection *conn; - TpHandleRepoIface *contact_repo; - const gchar *main_jid; - gchar *username, *jid; - - priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (chan); - conn = (TpBaseConnection *)priv->conn; - contact_repo = tp_base_connection_get_handles (conn, TP_HANDLE_TYPE_CONTACT); - - main_jid = tp_handle_inspect (contact_repo, main_handle); - - gabble_decode_jid (main_jid, &username, NULL, NULL); - - jid = g_strdup_printf ("%s/%s", priv->jid, username); - - g_free (username); - - if (room_handle) - { - *room_handle = tp_handle_ensure (contact_repo, jid, - GUINT_TO_POINTER (GABBLE_JID_ROOM_MEMBER), NULL); - } - - if (room_jid) - { - *room_jid = g_string_new (jid); - } - - g_free (jid); -} - -static gboolean -send_join_request (GabbleMucChannel *channel, - const gchar *password, - GError **error) -{ - GabbleMucChannelPrivate *priv; - LmMessage *msg; - LmMessageNode *x_node; - gboolean ret; - - priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (channel); - - /* build the message */ - msg = lm_message_new (priv->self_jid->str, LM_MESSAGE_TYPE_PRESENCE); - - x_node = lm_message_node_add_child (msg->node, "x", NULL); - lm_message_node_set_attribute (x_node, "xmlns", NS_MUC); - - g_free (priv->password); - - if (password != NULL) - { - priv->password = g_strdup (password); - lm_message_node_add_child (x_node, "password", password); - } - - /* send it */ - ret = _gabble_connection_send (priv->conn, msg, error); - if (!ret) - { - g_warning ("%s: _gabble_connection_send_with_reply failed", G_STRFUNC); - } - else - { - DEBUG ("join request sent"); - } - - lm_message_unref (msg); - - return ret; -} - -static gboolean -send_leave_message (GabbleMucChannel *channel, - const gchar *reason) -{ - GabbleMucChannelPrivate *priv; - LmMessage *msg; - GError *error = NULL; - gboolean ret; - - priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (channel); - - /* build the message */ - msg = lm_message_new_with_sub_type (priv->self_jid->str, - LM_MESSAGE_TYPE_PRESENCE, - LM_MESSAGE_SUB_TYPE_UNAVAILABLE); - - if (reason != NULL) - { - lm_message_node_add_child (msg->node, "status", reason); - } - - /* send it */ - ret = _gabble_connection_send (priv->conn, msg, &error); - if (!ret) - { - g_warning ("%s: _gabble_connection_send_with_reply failed", G_STRFUNC); - g_error_free (error); - } - else - { - DEBUG ("leave message sent"); - } - - lm_message_unref (msg); - - return ret; -} - -static void -gabble_muc_channel_get_property (GObject *object, - guint property_id, - GValue *value, - GParamSpec *pspec) -{ - GabbleMucChannel *chan = GABBLE_MUC_CHANNEL (object); - GabbleMucChannelPrivate *priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (chan); - - switch (property_id) { - case PROP_OBJECT_PATH: - g_value_set_string (value, priv->object_path); - break; - case PROP_CHANNEL_TYPE: - g_value_set_static_string (value, TP_IFACE_CHANNEL_TYPE_TEXT); - break; - case PROP_HANDLE_TYPE: - g_value_set_uint (value, TP_HANDLE_TYPE_ROOM); - break; - case PROP_HANDLE: - g_value_set_uint (value, priv->handle); - break; - case PROP_CONNECTION: - g_value_set_object (value, priv->conn); - break; - case PROP_STATE: - g_value_set_uint (value, priv->state); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } -} - -static void channel_state_changed (GabbleMucChannel *chan, - GabbleMucState prev_state, - GabbleMucState new_state); - -static void -gabble_muc_channel_set_property (GObject *object, - guint property_id, - const GValue *value, - GParamSpec *pspec) -{ - GabbleMucChannel *chan = GABBLE_MUC_CHANNEL (object); - GabbleMucChannelPrivate *priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (chan); - GabbleMucState prev_state; - - switch (property_id) { - case PROP_OBJECT_PATH: - g_free (priv->object_path); - priv->object_path = g_value_dup_string (value); - break; - case PROP_HANDLE: - priv->handle = g_value_get_uint (value); - break; - case PROP_HANDLE_TYPE: - /* this property is writable in the interface, but not actually - * meaningfully changable on this channel, so we do nothing */ - break; - case PROP_CONNECTION: - priv->conn = g_value_get_object (value); - break; - case PROP_STATE: - prev_state = priv->state; - priv->state = g_value_get_uint (value); - - if (priv->state != prev_state) - channel_state_changed (chan, prev_state, priv->state); - - break; - case PROP_INVITE_SELF: - priv->invite_self = g_value_get_boolean (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } -} - -static void gabble_muc_channel_dispose (GObject *object); -static void gabble_muc_channel_finalize (GObject *object); -static gboolean gabble_muc_channel_add_member (GObject *obj, TpHandle handle, - const gchar *message, GError **error); -static gboolean gabble_muc_channel_remove_member (GObject *obj, - TpHandle handle, const gchar *message, GError **error); -static gboolean gabble_muc_channel_do_set_properties (GObject *obj, - TpPropertiesContext *ctx, GError **error); - -static void -gabble_muc_channel_class_init (GabbleMucChannelClass *gabble_muc_channel_class) -{ - GObjectClass *object_class = G_OBJECT_CLASS (gabble_muc_channel_class); - GParamSpec *param_spec; - - g_type_class_add_private (gabble_muc_channel_class, - sizeof (GabbleMucChannelPrivate)); - - object_class->constructor = gabble_muc_channel_constructor; - - object_class->get_property = gabble_muc_channel_get_property; - object_class->set_property = gabble_muc_channel_set_property; - - object_class->dispose = gabble_muc_channel_dispose; - object_class->finalize = gabble_muc_channel_finalize; - - g_object_class_override_property (object_class, PROP_OBJECT_PATH, - "object-path"); - g_object_class_override_property (object_class, PROP_CHANNEL_TYPE, - "channel-type"); - g_object_class_override_property (object_class, PROP_HANDLE_TYPE, - "handle-type"); - g_object_class_override_property (object_class, PROP_HANDLE, "handle"); - - param_spec = g_param_spec_object ("connection", "GabbleConnection object", - "Gabble connection object that owns this " - "MUC channel object.", - GABBLE_TYPE_CONNECTION, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | - G_PARAM_STATIC_NICK | - G_PARAM_STATIC_BLURB); - g_object_class_install_property (object_class, PROP_CONNECTION, param_spec); - - param_spec = g_param_spec_uint ("state", "Channel state", - "The current state that the channel is in.", - 0, G_MAXUINT32, 0, - G_PARAM_READWRITE | - G_PARAM_STATIC_NAME | - G_PARAM_STATIC_BLURB); - g_object_class_install_property (object_class, PROP_STATE, param_spec); - - param_spec = g_param_spec_boolean ("invite-self", "Invite self", - "Whether the user should be added to members list.", FALSE, - G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE | G_PARAM_STATIC_NAME | - G_PARAM_STATIC_BLURB); - g_object_class_install_property (object_class, PROP_INVITE_SELF, param_spec); - - signals[READY] = - g_signal_new ("ready", - G_OBJECT_CLASS_TYPE (gabble_muc_channel_class), - G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, - 0, - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); - - signals[JOIN_ERROR] = - g_signal_new ("join-error", - G_OBJECT_CLASS_TYPE (gabble_muc_channel_class), - G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, - 0, - NULL, NULL, - g_cclosure_marshal_VOID__POINTER, - G_TYPE_NONE, 1, G_TYPE_POINTER); - - tp_group_mixin_class_init (object_class, - G_STRUCT_OFFSET (GabbleMucChannelClass, - group_class), - gabble_muc_channel_add_member, - gabble_muc_channel_remove_member); - - tp_properties_mixin_class_init (object_class, - G_STRUCT_OFFSET (GabbleMucChannelClass, - properties_class), - room_property_signatures, NUM_ROOM_PROPS, - gabble_muc_channel_do_set_properties); - - tp_text_mixin_class_init (object_class, - G_STRUCT_OFFSET (GabbleMucChannelClass, text_class)); -} - -static void clear_join_timer (GabbleMucChannel *chan); -static void clear_poll_timer (GabbleMucChannel *chan); - -void -gabble_muc_channel_dispose (GObject *object) -{ - GabbleMucChannel *self = GABBLE_MUC_CHANNEL (object); - GabbleMucChannelPrivate *priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (self); - - if (priv->dispose_has_run) - return; - - DEBUG ("called"); - - priv->dispose_has_run = TRUE; - - clear_join_timer (self); - clear_poll_timer (self); - - if (G_OBJECT_CLASS (gabble_muc_channel_parent_class)->dispose) - G_OBJECT_CLASS (gabble_muc_channel_parent_class)->dispose (object); -} - -void -gabble_muc_channel_finalize (GObject *object) -{ - GabbleMucChannel *self = GABBLE_MUC_CHANNEL (object); - GabbleMucChannelPrivate *priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (self); - TpHandleRepoIface *room_handles = tp_base_connection_get_handles ( - (TpBaseConnection *)priv->conn, TP_HANDLE_TYPE_ROOM); - - DEBUG ("called"); - - /* free any data held directly by the object here */ - tp_handle_unref (room_handles, priv->handle); - - g_free (priv->object_path); - - if (priv->self_jid) - { - g_string_free (priv->self_jid, TRUE); - } - - g_free (priv->password); - - tp_properties_mixin_finalize (object); - - tp_group_mixin_finalize (object); - - tp_text_mixin_finalize (object); - - G_OBJECT_CLASS (gabble_muc_channel_parent_class)->finalize (object); -} - -static void clear_join_timer (GabbleMucChannel *chan) -{ - GabbleMucChannelPrivate *priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (chan); - - if (priv->join_timer_id != 0) - { - g_source_remove (priv->join_timer_id); - priv->join_timer_id = 0; - } -} - -static void clear_poll_timer (GabbleMucChannel *chan) -{ - GabbleMucChannelPrivate *priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (chan); - - if (priv->poll_timer_id != 0) - { - g_source_remove (priv->poll_timer_id); - priv->poll_timer_id = 0; - } -} - -static void -change_password_flags (GabbleMucChannel *chan, - TpChannelPasswordFlags add, - TpChannelPasswordFlags remove) -{ - GabbleMucChannelPrivate *priv; - TpChannelPasswordFlags added, removed; - - g_assert (GABBLE_IS_MUC_CHANNEL (chan)); - - priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (chan); - - added = add & ~priv->password_flags; - priv->password_flags |= added; - - removed = remove & priv->password_flags; - priv->password_flags &= ~removed; - - if (add != 0 || remove != 0) - { - DEBUG ("emitting password flags changed, added 0x%X, removed 0x%X", - added, removed); - - tp_svc_channel_interface_password_emit_password_flags_changed ( - chan, added, removed); - } -} - -static void -provide_password_return_if_pending (GabbleMucChannel *chan, gboolean success) -{ - GabbleMucChannelPrivate *priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (chan); - - if (priv->password_ctx) - { - dbus_g_method_return (priv->password_ctx, success); - priv->password_ctx = NULL; - } - - if (success) - { - change_password_flags (chan, 0, TP_CHANNEL_PASSWORD_FLAG_PROVIDE); - } -} - -static void close_channel (GabbleMucChannel *chan, const gchar *reason, - gboolean inform_muc, TpHandle actor, guint reason_code); - -static gboolean -timeout_join (gpointer data) -{ - GabbleMucChannel *chan = data; - - DEBUG ("join timed out, closing channel"); - - provide_password_return_if_pending (chan, FALSE); - - close_channel (chan, NULL, FALSE, 0, 0); - - return FALSE; -} - -static gboolean -timeout_poll (gpointer data) -{ - GabbleMucChannel *chan = data; - - DEBUG ("polling for room properties"); - - room_properties_update (chan); - - return TRUE; -} - -static void -channel_state_changed (GabbleMucChannel *chan, - GabbleMucState prev_state, - GabbleMucState new_state) -{ - GabbleMucChannelPrivate *priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (chan); - - DEBUG ("state changed from %s to %s", muc_states[prev_state], - muc_states[new_state]); - - if (new_state == MUC_STATE_INITIATED) - { - priv->join_timer_id = - g_timeout_add (DEFAULT_JOIN_TIMEOUT, timeout_join, chan); - } - else if (new_state == MUC_STATE_JOINED) - { - gboolean low_bandwidth; - gint interval; - - provide_password_return_if_pending (chan, TRUE); - - clear_join_timer (chan); - - g_object_get (priv->conn, "low-bandwidth", &low_bandwidth, NULL); - - if (low_bandwidth) - interval = PROPS_POLL_INTERVAL_LOW; - else - interval = PROPS_POLL_INTERVAL_HIGH; - - priv->poll_timer_id = g_timeout_add (interval, timeout_poll, chan); - - /* no need to keep this around any longer, if it's set */ - g_free (priv->password); - priv->password = NULL; - } - else if (new_state == MUC_STATE_ENDED) - { - clear_poll_timer (chan); - } - - if (new_state == MUC_STATE_JOINED || new_state == MUC_STATE_AUTH) - { - if (!priv->ready_emitted) - { - g_signal_emit (chan, signals[READY], 0); - - priv->ready_emitted = TRUE; - } - } -} - - -static void -close_channel (GabbleMucChannel *chan, const gchar *reason, - gboolean inform_muc, TpHandle actor, guint reason_code) -{ - GabbleMucChannelPrivate *priv; - TpIntSet *set; - - g_assert (GABBLE_IS_MUC_CHANNEL (chan)); - - priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (chan); - - if (priv->closed) - return; - - priv->closed = TRUE; - - /* Remove us from member list */ - set = tp_intset_new (); - tp_intset_add (set, TP_GROUP_MIXIN (chan)->self_handle); - - tp_group_mixin_change_members ((GObject *)chan, - (reason != NULL) ? reason : "", - NULL, set, NULL, NULL, actor, - reason_code); - - tp_intset_destroy (set); - - /* Inform the MUC if requested */ - if (inform_muc && priv->state >= MUC_STATE_INITIATED) - { - send_leave_message (chan, reason); - } - - /* Update state and emit Closed signal */ - g_object_set (chan, "state", MUC_STATE_ENDED, NULL); - - tp_svc_channel_emit_closed (chan); -} - -gboolean -_gabble_muc_channel_is_ready (GabbleMucChannel *chan) -{ - GabbleMucChannelPrivate *priv; - - g_assert (GABBLE_IS_MUC_CHANNEL (chan)); - - priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (chan); - - return priv->ready_emitted; -} - -/** - * _gabble_muc_channel_presence_error - */ -void -_gabble_muc_channel_presence_error (GabbleMucChannel *chan, - const gchar *jid, - LmMessageNode *pres_node) -{ - GabbleMucChannelPrivate *priv; - LmMessageNode *error_node; - GabbleXmppError error; - TpHandle actor = 0; - guint reason_code = TP_CHANNEL_GROUP_CHANGE_REASON_NONE; - - g_assert (GABBLE_IS_MUC_CHANNEL (chan)); - - priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (chan); - - if (strcmp (jid, priv->self_jid->str) != 0) - { - g_warning ("%s: presence error from other jids than self not handled", - G_STRFUNC); - return; - } - - error_node = lm_message_node_get_child (pres_node, "error"); - if (error_node == NULL) - { - g_warning ("%s: missing required node 'error'", G_STRFUNC); - return; - } - - error = gabble_xmpp_error_from_node (error_node); - - if (priv->state >= MUC_STATE_JOINED) - { - g_warning ("%s: presence error while already member of the channel " - "-- NYI", G_STRFUNC); - return; - } - - /* We're not a member, find out why the join request failed - * and act accordingly. */ - if (error == XMPP_ERROR_NOT_AUTHORIZED) - { - /* channel can sit requiring a password indefinitely */ - clear_join_timer (chan); - - /* Password already provided and incorrect? */ - if (priv->state == MUC_STATE_AUTH) - { - provide_password_return_if_pending (chan, FALSE); - - return; - } - - DEBUG ("password required to join, changing password flags"); - - change_password_flags (chan, TP_CHANNEL_PASSWORD_FLAG_PROVIDE, 0); - - g_object_set (chan, "state", MUC_STATE_AUTH, NULL); - } - else - { - GError *tp_error /* doesn't need initializing */; - - switch (error) { - case XMPP_ERROR_FORBIDDEN: - tp_error = g_error_new (TP_ERRORS, TP_ERROR_CHANNEL_BANNED, - "banned from room"); - reason_code = TP_CHANNEL_GROUP_CHANGE_REASON_BANNED; - break; - case XMPP_ERROR_SERVICE_UNAVAILABLE: - tp_error = g_error_new (TP_ERRORS, TP_ERROR_CHANNEL_FULL, - "room is full"); - break; - case XMPP_ERROR_REGISTRATION_REQUIRED: - tp_error = g_error_new (TP_ERRORS, TP_ERROR_CHANNEL_INVITE_ONLY, - "room is invite only"); - break; - case XMPP_ERROR_CONFLICT: - if (priv->nick_retry_count < MAX_NICK_RETRIES) - { - g_string_append_c (priv->self_jid, '_'); - - if (send_join_request (chan, priv->password, &tp_error)) - { - priv->nick_retry_count++; - return; - } - } - else - { - tp_error = g_error_new (TP_ERRORS, TP_ERROR_NOT_AVAILABLE, - "nickname already in use and retry count exceeded"); - } - break; - default: - if (error != INVALID_XMPP_ERROR) - { - tp_error = g_error_new (TP_ERRORS, TP_ERROR_NOT_AVAILABLE, - gabble_xmpp_error_description (error)); - } - else - { - tp_error = g_error_new (TP_ERRORS, TP_ERROR_NOT_AVAILABLE, - "unknown error"); - } - break; - } - - g_signal_emit (chan, signals[JOIN_ERROR], 0, tp_error); - - close_channel (chan, tp_error->message, FALSE, actor, reason_code); - - g_error_free (tp_error); - } -} - -static GabbleMucRole -get_role_from_string (const gchar *role) -{ - guint i; - - if (role == NULL) - { - return ROLE_VISITOR; - } - - for (i = 0; i < NUM_ROLES; i++) - { - if (strcmp (role, muc_roles[i]) == 0) - { - return i; - } - } - - g_warning ("%s: unknown role '%s' -- defaulting to ROLE_VISITOR", - G_STRFUNC, role); - - return ROLE_VISITOR; -} - -static GabbleMucAffiliation -get_affiliation_from_string (const gchar *affil) -{ - guint i; - - if (affil == NULL) - { - return AFFILIATION_NONE; - } - - for (i = 0; i < NUM_AFFILIATIONS; i++) - { - if (strcmp (affil, muc_affiliations[i]) == 0) - { - return i; - } - } - - g_warning ("%s: unknown affiliation '%s' -- defaulting to " - "AFFILIATION_NONE", G_STRFUNC, affil); - - return AFFILIATION_NONE; -} - -static LmHandlerResult -room_created_submit_reply_cb (GabbleConnection *conn, LmMessage *sent_msg, - LmMessage *reply_msg, GObject *object, - gpointer user_data) -{ - if (lm_message_get_sub_type (reply_msg) != LM_MESSAGE_SUB_TYPE_RESULT) - { - g_warning ("%s: failed to submit room config", G_STRFUNC); - } - - return LM_HANDLER_RESULT_REMOVE_MESSAGE; -} - -static LmMessageNode * -config_form_get_form_node (LmMessage *msg) -{ - LmMessageNode *node; - - /* find the query node */ - node = lm_message_node_get_child (msg->node, "query"); - if (node == NULL) - return NULL; - - /* then the form node */ - for (node = node->children; node; node = node->next) - { - if (tp_strdiff (node->name, "x")) - { - continue; - } - - if (!lm_message_node_has_namespace (node, NS_X_DATA, NULL)) - { - continue; - } - - if (tp_strdiff (lm_message_node_get_attribute (node, "type"), "form")) - { - continue; - } - - return node; - } - - return NULL; -} - -static LmHandlerResult -perms_config_form_reply_cb (GabbleConnection *conn, LmMessage *sent_msg, - LmMessage *reply_msg, GObject *object, - gpointer user_data) -{ - GabbleMucChannel *chan = GABBLE_MUC_CHANNEL (object); - GabbleMucChannelPrivate *priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (chan); - LmMessageNode *form_node, *node; - - if (lm_message_get_sub_type (reply_msg) != LM_MESSAGE_SUB_TYPE_RESULT) - { - g_warning ("%s: request for config form denied, property permissions " - "will be inaccurate", G_STRFUNC); - goto OUT; - } - - /* just in case our affiliation has changed in the meantime */ - if (priv->self_affil != AFFILIATION_OWNER) - goto OUT; - - form_node = config_form_get_form_node (reply_msg); - if (form_node == NULL) - { - g_warning ("%s: form node node found, property permissions will be " - "inaccurate", G_STRFUNC); - goto OUT; - } - - for (node = form_node->children; node; node = node->next) - { - const gchar *var; - - if (strcmp (node->name, "field") != 0) - continue; - - var = lm_message_node_get_attribute (node, "var"); - if (var == NULL) - continue; - - if (strcmp (var, "muc#roomconfig_roomdesc") == 0 || - strcmp (var, "muc#owner_roomdesc") == 0) - { - if (tp_properties_mixin_is_readable (G_OBJECT (chan), - ROOM_PROP_DESCRIPTION)) - { - tp_properties_mixin_change_flags (G_OBJECT (chan), - ROOM_PROP_DESCRIPTION, TP_PROPERTY_FLAG_WRITE, 0, - NULL); - - goto OUT; - } - } - } - -OUT: - return LM_HANDLER_RESULT_REMOVE_MESSAGE; -} - -static void -update_permissions (GabbleMucChannel *chan) -{ - GabbleMucChannelPrivate *priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (chan); - TpChannelGroupFlags grp_flags_add, grp_flags_rem; - TpPropertyFlags prop_flags_add, prop_flags_rem; - TpIntSet *changed_props_val, *changed_props_flags; - - /* - * Update group flags. - */ - grp_flags_add = TP_CHANNEL_GROUP_FLAG_CAN_ADD | - TP_CHANNEL_GROUP_FLAG_MESSAGE_ADD; - grp_flags_rem = 0; - - if (priv->self_role == ROLE_MODERATOR) - { - grp_flags_add |= TP_CHANNEL_GROUP_FLAG_CAN_REMOVE | - TP_CHANNEL_GROUP_FLAG_MESSAGE_REMOVE; - } - else - { - grp_flags_rem |= TP_CHANNEL_GROUP_FLAG_CAN_REMOVE | - TP_CHANNEL_GROUP_FLAG_MESSAGE_REMOVE; - } - - tp_group_mixin_change_flags ((GObject *) chan, grp_flags_add, grp_flags_rem); - - - /* - * Update write capabilities based on room configuration - * and own role and affiliation. - */ - - changed_props_val = tp_intset_sized_new (NUM_ROOM_PROPS); - changed_props_flags = tp_intset_sized_new (NUM_ROOM_PROPS); - - /* - * Subject - * - * FIXME: this might be allowed for participants/moderators only, - * so for now just rely on the server making that call. - */ - - if (priv->self_role >= ROLE_VISITOR) - { - prop_flags_add = TP_PROPERTY_FLAG_WRITE; - prop_flags_rem = 0; - } - else - { - prop_flags_add = 0; - prop_flags_rem = TP_PROPERTY_FLAG_WRITE; - } - - tp_properties_mixin_change_flags (G_OBJECT (chan), - ROOM_PROP_SUBJECT, prop_flags_add, prop_flags_rem, - changed_props_flags); - - /* Room definition */ - if (priv->self_affil == AFFILIATION_OWNER) - { - prop_flags_add = TP_PROPERTY_FLAG_WRITE; - prop_flags_rem = 0; - } - else - { - prop_flags_add = 0; - prop_flags_rem = TP_PROPERTY_FLAG_WRITE; - } - - tp_properties_mixin_change_flags (G_OBJECT (chan), - ROOM_PROP_ANONYMOUS, prop_flags_add, prop_flags_rem, - changed_props_flags); - - tp_properties_mixin_change_flags (G_OBJECT (chan), - ROOM_PROP_INVITE_ONLY, prop_flags_add, prop_flags_rem, - changed_props_flags); - - tp_properties_mixin_change_flags (G_OBJECT (chan), - ROOM_PROP_MODERATED, prop_flags_add, prop_flags_rem, - changed_props_flags); - - tp_properties_mixin_change_flags (G_OBJECT (chan), - ROOM_PROP_NAME, prop_flags_add, prop_flags_rem, - changed_props_flags); - - tp_properties_mixin_change_flags (G_OBJECT (chan), - ROOM_PROP_PASSWORD, prop_flags_add, prop_flags_rem, - changed_props_flags); - - tp_properties_mixin_change_flags (G_OBJECT (chan), - ROOM_PROP_PASSWORD_REQUIRED, prop_flags_add, prop_flags_rem, - changed_props_flags); - - tp_properties_mixin_change_flags (G_OBJECT (chan), - ROOM_PROP_PERSISTENT, prop_flags_add, prop_flags_rem, - changed_props_flags); - - tp_properties_mixin_change_flags (G_OBJECT (chan), - ROOM_PROP_PRIVATE, prop_flags_add, prop_flags_rem, - changed_props_flags); - - tp_properties_mixin_change_flags (G_OBJECT (chan), - ROOM_PROP_SUBJECT, prop_flags_add, prop_flags_rem, - changed_props_flags); - - if (priv->self_affil == AFFILIATION_OWNER) - { - /* request the configuration form purely to see if the description - * is writable by us in this room. sigh. GO MUC!!! */ - LmMessage *msg; - LmMessageNode *node; - GError *error = NULL; - gboolean success; - - msg = lm_message_new_with_sub_type (priv->jid, - LM_MESSAGE_TYPE_IQ, LM_MESSAGE_SUB_TYPE_GET); - node = lm_message_node_add_child (msg->node, "query", NULL); - lm_message_node_set_attribute (node, "xmlns", NS_MUC_OWNER); - - success = _gabble_connection_send_with_reply (priv->conn, msg, - perms_config_form_reply_cb, G_OBJECT (chan), NULL, - &error); - - lm_message_unref (msg); - - if (!success) - { - g_warning ("%s: failed to request config form: %s", - G_STRFUNC, error->message); - g_error_free (error); - } - } - else - { - /* mark description unwritable if we're no longer an owner */ - tp_properties_mixin_change_flags (G_OBJECT (chan), - ROOM_PROP_DESCRIPTION, 0, TP_PROPERTY_FLAG_WRITE, - changed_props_flags); - } - - /* - * Emit signals. - */ - tp_properties_mixin_emit_changed (G_OBJECT (chan), changed_props_val); - tp_properties_mixin_emit_flags (G_OBJECT (chan), changed_props_flags); - tp_intset_destroy (changed_props_val); - tp_intset_destroy (changed_props_flags); -} - -/** - * _gabble_muc_channel_member_presence_updated - */ -void -_gabble_muc_channel_member_presence_updated (GabbleMucChannel *chan, - TpHandle handle, - LmMessage *message, - LmMessageNode *x_node) -{ - GabbleMucChannelPrivate *priv; - TpBaseConnection *conn; - TpIntSet *set; - TpGroupMixin *mixin; - LmMessageNode *item_node, *node; - const gchar *affil, *role, *owner_jid, *status_code; - TpHandle actor = 0; - guint reason_code = TP_CHANNEL_GROUP_CHANGE_REASON_NONE; - TpHandleRepoIface *contact_handles; - - DEBUG ("called"); - - g_assert (GABBLE_IS_MUC_CHANNEL (chan)); - - priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (chan); - conn = (TpBaseConnection *)priv->conn; - contact_handles = tp_base_connection_get_handles (conn, - TP_HANDLE_TYPE_CONTACT); - - mixin = TP_GROUP_MIXIN (chan); - - item_node = lm_message_node_get_child (x_node, "item"); - if (item_node == NULL) - { - g_warning ("%s: node missing 'item' child, ignoring", G_STRFUNC); - return; - } - - node = lm_message_node_get_child (x_node, "status"); - if (node) - { - status_code = lm_message_node_get_attribute (node, "code"); - } - else - { - status_code = NULL; - } - - role = lm_message_node_get_attribute (item_node, "role"); - affil = lm_message_node_get_attribute (item_node, "affiliation"); - owner_jid = lm_message_node_get_attribute (item_node, "jid"); - - /* update channel members according to presence */ - set = tp_intset_new (); - tp_intset_add (set, handle); - - if (lm_message_get_sub_type (message) != LM_MESSAGE_SUB_TYPE_UNAVAILABLE) - { - if (!tp_handle_set_is_member (mixin->members, handle)) - { - tp_group_mixin_change_members ((GObject *)chan, "", set, NULL, - NULL, NULL, 0, 0); - - if (owner_jid != NULL) - { - TpHandle owner_handle; - - owner_handle = tp_handle_ensure (contact_handles, owner_jid, - GUINT_TO_POINTER (GABBLE_JID_GLOBAL), NULL); - if (owner_handle == 0) - { - DEBUG ("ignoring invalid owner JID %s in MUC presence", - owner_jid); - } - else - { - tp_group_mixin_add_handle_owner ((GObject *)chan, handle, - owner_handle); - tp_handle_unref (contact_handles, owner_handle); - } - } - - if (handle == mixin->self_handle) - { - g_object_set (chan, "state", MUC_STATE_JOINED, NULL); - } - } - - if (handle == mixin->self_handle) - { - GabbleMucRole new_role; - GabbleMucAffiliation new_affil; - - /* accept newly-created room settings before we send anything - * below which queryies them. */ - if (status_code && strcmp (status_code, "201") == 0) - { - LmMessage *msg; - GError *error = NULL; - - msg = lm_message_new_with_sub_type (priv->jid, - LM_MESSAGE_TYPE_IQ, LM_MESSAGE_SUB_TYPE_SET); - - node = lm_message_node_add_child (msg->node, "query", NULL); - lm_message_node_set_attribute (node, "xmlns", NS_MUC_OWNER); - - node = lm_message_node_add_child (node, "x", NULL); - lm_message_node_set_attributes (node, - "xmlns", NS_X_DATA, - "type", "submit", - NULL); - - if (!_gabble_connection_send_with_reply (priv->conn, msg, - room_created_submit_reply_cb, G_OBJECT (chan), NULL, - &error)) - { - g_warning ("%s: failed to send submit message: %s", - G_STRFUNC, error->message); - g_error_free (error); - - lm_message_unref (msg); - close_channel (chan, NULL, TRUE, actor, reason_code); - - goto OUT; - } - - lm_message_unref (msg); - } - - /* Update room properties */ - room_properties_update (chan); - - /* update permissions after requesting new properties so that if we - * become an owner, we get our configuration form reply after the - * discovery reply, so we know whether there is a description - * property before we try and decide whether we can write to it. */ - new_role = get_role_from_string (role); - new_affil = get_affiliation_from_string (affil); - - if (new_role != priv->self_role || new_affil != priv->self_affil) - { - priv->self_role = new_role; - priv->self_affil = new_affil; - - update_permissions (chan); - } - } - } - else - { - LmMessageNode *reason_node, *actor_node; - const gchar *reason = "", *actor_jid = ""; - - actor_node = lm_message_node_get_child (item_node, "actor"); - if (actor_node != NULL) - { - actor_jid = lm_message_node_get_attribute (actor_node, "jid"); - if (actor_jid != NULL) - { - actor = tp_handle_ensure (contact_handles, actor_jid, NULL, - NULL); - if (actor == 0) - { - DEBUG ("ignoring invalid actor JID %s", actor_jid); - } - } - } - - /* Possible reasons we could have been removed from the room: - * 301 banned - * 307 kicked - * 321 "because of an affiliation change" - no reason_code - * 322 room has become members-only and we're not a member - no - * reason_code - * 332 system (server) is being shut down - no reason code - */ - if (status_code) - { - if (strcmp (status_code, "301") == 0) - { - reason_code = TP_CHANNEL_GROUP_CHANGE_REASON_BANNED; - } - else if (strcmp (status_code, "307") == 0) - { - reason_code = TP_CHANNEL_GROUP_CHANGE_REASON_KICKED; - } - } - - reason_node = lm_message_node_get_child (item_node, "reason"); - if (reason_node != NULL) - { - reason = lm_message_node_get_value (reason_node); - } - - if (handle != mixin->self_handle) - { - tp_group_mixin_change_members ((GObject *)chan, reason, - NULL, set, NULL, NULL, - actor, reason_code); - } - else - { - close_channel (chan, reason, FALSE, actor, reason_code); - } - } - -OUT: - tp_intset_destroy (set); - if (actor) - tp_handle_unref (contact_handles, actor); -} - - -/** - * _gabble_muc_channel_receive - */ -void -_gabble_muc_channel_receive (GabbleMucChannel *chan, - TpChannelTextMessageType msg_type, - TpHandleType handle_type, - TpHandle sender, - time_t timestamp, - const gchar *text, - LmMessage *msg) -{ - gboolean error; - GabbleMucChannelPrivate *priv; - LmMessageNode *subj_node, *node; - GValue val = { 0, }; - - g_assert (GABBLE_IS_MUC_CHANNEL (chan)); - - priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (chan); - - error = lm_message_get_sub_type (msg) == LM_MESSAGE_SUB_TYPE_ERROR; - - subj_node = lm_message_node_get_child (msg->node, "subject"); - - if (subj_node) - { - TpIntSet *changed_values, *changed_flags; - - if (priv->properties_ctx) - { - tp_properties_context_remove (priv->properties_ctx, - ROOM_PROP_SUBJECT); - } - - if (error) - { - GabbleXmppError xmpp_error = INVALID_XMPP_ERROR; - const gchar *err_desc = NULL; - - node = lm_message_node_get_child (msg->node, "error"); - if (node) - { - xmpp_error = gabble_xmpp_error_from_node (node); - } - - if (xmpp_error != INVALID_XMPP_ERROR) - { - err_desc = gabble_xmpp_error_description (xmpp_error); - } - - if (priv->properties_ctx) - { - GError *error = NULL; - - error = g_error_new (TP_ERRORS, TP_ERROR_PERMISSION_DENIED, - (err_desc) ? err_desc : "failed to change subject"); - - tp_properties_context_return (priv->properties_ctx, error); - priv->properties_ctx = NULL; - - /* Get the properties into a consistent state. */ - room_properties_update (chan); - } - - return; - } - - changed_values = tp_intset_sized_new (NUM_ROOM_PROPS); - changed_flags = tp_intset_sized_new (NUM_ROOM_PROPS); - - /* ROOM_PROP_SUBJECT */ - g_value_init (&val, G_TYPE_STRING); - g_value_set_string (&val, lm_message_node_get_value (subj_node)); - - tp_properties_mixin_change_value (G_OBJECT (chan), - ROOM_PROP_SUBJECT, &val, changed_values); - - tp_properties_mixin_change_flags (G_OBJECT (chan), - ROOM_PROP_SUBJECT, TP_PROPERTY_FLAG_READ, 0, - changed_flags); - - g_value_unset (&val); - - if (handle_type == TP_HANDLE_TYPE_CONTACT) - { - /* ROOM_PROP_SUBJECT_CONTACT */ - g_value_init (&val, G_TYPE_UINT); - g_value_set_uint (&val, sender); - - tp_properties_mixin_change_value (G_OBJECT (chan), - ROOM_PROP_SUBJECT_CONTACT, &val, changed_values); - - tp_properties_mixin_change_flags (G_OBJECT (chan), - ROOM_PROP_SUBJECT_CONTACT, TP_PROPERTY_FLAG_READ, 0, - changed_flags); - - g_value_unset (&val); - } - - /* ROOM_PROP_SUBJECT_TIMESTAMP */ - g_value_init (&val, G_TYPE_UINT); - g_value_set_uint (&val, timestamp); - - tp_properties_mixin_change_value (G_OBJECT (chan), - ROOM_PROP_SUBJECT_TIMESTAMP, &val, changed_values); - - tp_properties_mixin_change_flags (G_OBJECT (chan), - ROOM_PROP_SUBJECT_TIMESTAMP, TP_PROPERTY_FLAG_READ, 0, - changed_flags); - - g_value_unset (&val); - - /* Emit signals */ - tp_properties_mixin_emit_changed (G_OBJECT (chan), changed_values); - tp_properties_mixin_emit_flags (G_OBJECT (chan), changed_flags); - tp_intset_destroy (changed_values); - tp_intset_destroy (changed_flags); - - if (priv->properties_ctx) - { - if (tp_properties_context_return_if_done (priv->properties_ctx)) - { - priv->properties_ctx = NULL; - } - } - - return; - } - else if (handle_type == TP_HANDLE_TYPE_ROOM) - { - NODE_DEBUG (msg->node, "ignoring message from channel"); - - return; - } - else if ((sender == chan->group.self_handle) && (timestamp == 0)) - { - /* If we sent the message and it's not delayed, just emit the sent - signal */ - timestamp = time (NULL); - tp_svc_channel_type_text_emit_sent (chan, timestamp, msg_type, text); - - return; - } - - /* Receive messages from other contacts and our own if they're delayed, and - * set the timestamp for non-delayed messages */ - if (timestamp == 0) - timestamp = time (NULL); - - tp_text_mixin_receive (G_OBJECT (chan), msg_type, sender, - timestamp, text); -} - -void -_gabble_muc_channel_handle_invited (GabbleMucChannel *chan, - TpHandle inviter, - const gchar *message) -{ - GabbleMucChannelPrivate *priv; - TpBaseConnection *conn; - TpHandle self_handle; - TpIntSet *set_members, *set_pending; - TpHandleRepoIface *contact_handles; - - g_assert (GABBLE_IS_MUC_CHANNEL (chan)); - - priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (chan); - conn = (TpBaseConnection *)priv->conn; - contact_handles = tp_base_connection_get_handles (conn, - TP_HANDLE_TYPE_CONTACT); - - /* add ourself to local pending and the inviter to members */ - set_members = tp_intset_new (); - set_pending = tp_intset_new (); - - tp_intset_add (set_members, inviter); - - /* get our own identity in the room */ - contact_handle_to_room_identity (chan, conn->self_handle, - &self_handle, &priv->self_jid); - tp_intset_add (set_pending, self_handle); - - tp_group_mixin_change_members ((GObject *)chan, message, set_members, - NULL, set_pending, NULL, inviter, - TP_CHANNEL_GROUP_CHANGE_REASON_INVITED); - - tp_intset_destroy (set_members); - tp_intset_destroy (set_pending); - tp_handle_unref (contact_handles, self_handle); - - /* queue the message */ - if (message && (message[0] != '\0')) - { - tp_text_mixin_receive (G_OBJECT (chan), - TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE, inviter, - time (NULL), message); - } - - /* emit READY signal so NewChannel is emitted */ - g_signal_emit (chan, signals[READY], 0); - priv->ready_emitted = TRUE; -} - -/** - * _gabble_muc_channel_state_receive - * - * Send the D-BUS signal ChatStateChanged - * on org.freedesktop.Telepathy.Channel.Interface.ChatState - */ -void -_gabble_muc_channel_state_receive (GabbleMucChannel *chan, - guint state, - guint from_handle) -{ - GabbleMucChannelPrivate *priv; - - g_assert (state < NUM_TP_CHANNEL_CHAT_STATES); - g_assert (GABBLE_IS_MUC_CHANNEL (chan)); - priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (chan); - - tp_svc_channel_interface_chat_state_emit_chat_state_changed (chan, - from_handle, state); -} - -/** - * gabble_muc_channel_close - * - * Implements D-Bus method Close - * on interface org.freedesktop.Telepathy.Channel - */ -static void -gabble_muc_channel_close (TpSvcChannel *iface, - DBusGMethodInvocation *context) -{ - GabbleMucChannel *self = GABBLE_MUC_CHANNEL (iface); - GabbleMucChannelPrivate *priv; - - g_assert (GABBLE_IS_MUC_CHANNEL (self)); - - priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (self); - - DEBUG ("called on %p", self); - - if (priv->closed) - { - GError already = { TP_ERRORS, TP_ERROR_NOT_AVAILABLE, - "Channel already closed"}; - DEBUG ("channel already closed"); - - dbus_g_method_return_error (context, &already); - return; - } - - close_channel (self, NULL, TRUE, 0, 0); - - tp_svc_channel_return_from_close (context); -} - - -/** - * gabble_muc_channel_get_channel_type - * - * Implements D-Bus method GetChannelType - * on interface org.freedesktop.Telepathy.Channel - */ -static void -gabble_muc_channel_get_channel_type (TpSvcChannel *iface, - DBusGMethodInvocation *context) -{ - tp_svc_channel_return_from_get_channel_type (context, - TP_IFACE_CHANNEL_TYPE_TEXT); -} - - -/** - * gabble_muc_channel_get_handle - * - * Implements D-Bus method GetHandle - * on interface org.freedesktop.Telepathy.Channel - */ -static void -gabble_muc_channel_get_handle (TpSvcChannel *iface, - DBusGMethodInvocation *context) -{ - GabbleMucChannel *self = GABBLE_MUC_CHANNEL (iface); - GabbleMucChannelPrivate *priv; - - g_assert (GABBLE_IS_MUC_CHANNEL (self)); - - priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (self); - - tp_svc_channel_return_from_get_handle (context, TP_HANDLE_TYPE_ROOM, - priv->handle); -} - - -/** - * gabble_muc_channel_get_interfaces - * - * Implements D-Bus method GetInterfaces - * on interface org.freedesktop.Telepathy.Channel - */ -static void -gabble_muc_channel_get_interfaces (TpSvcChannel *iface, - DBusGMethodInvocation *context) -{ - const gchar *interfaces[] = { - TP_IFACE_CHANNEL_INTERFACE_GROUP, - TP_IFACE_CHANNEL_INTERFACE_PASSWORD, - TP_IFACE_PROPERTIES_INTERFACE, - TP_IFACE_CHANNEL_INTERFACE_CHAT_STATE, - NULL - }; - - tp_svc_channel_return_from_get_interfaces (context, interfaces); -} - - -/** - * gabble_muc_channel_get_password_flags - * - * Implements D-Bus method GetPasswordFlags - * on interface org.freedesktop.Telepathy.Channel.Interface.Password - */ -static void -gabble_muc_channel_get_password_flags (TpSvcChannelInterfacePassword *iface, - DBusGMethodInvocation *context) -{ - GabbleMucChannel *self = GABBLE_MUC_CHANNEL (iface); - GabbleMucChannelPrivate *priv; - - g_assert (GABBLE_IS_MUC_CHANNEL (self)); - - priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (self); - - tp_svc_channel_interface_password_return_from_get_password_flags (context, - priv->password_flags); -} - - -/** - * gabble_muc_channel_provide_password - * - * Implements D-Bus method ProvidePassword - * on interface org.freedesktop.Telepathy.Channel.Interface.Password - * - * @context: The D-Bus invocation context to use to return values - * or throw an error. - */ -static void -gabble_muc_channel_provide_password (TpSvcChannelInterfacePassword *iface, - const gchar *password, - DBusGMethodInvocation *context) -{ - GabbleMucChannel *self = GABBLE_MUC_CHANNEL (iface); - GError *error = NULL; - GabbleMucChannelPrivate *priv; - - g_assert (GABBLE_IS_MUC_CHANNEL (self)); - - priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (self); - - if ((priv->password_flags & TP_CHANNEL_PASSWORD_FLAG_PROVIDE) == 0 || - priv->password_ctx != NULL) - { - error = g_error_new (TP_ERRORS, TP_ERROR_NOT_AVAILABLE, - "password cannot be provided in the current state"); - dbus_g_method_return_error (context, error); - g_error_free (error); - - return; - } - - if (!send_join_request (self, password, &error)) - { - dbus_g_method_return_error (context, error); - g_error_free (error); - - return; - } - - priv->password_ctx = context; -} - - -/** - * gabble_muc_channel_send - * - * Implements D-Bus method Send - * on interface org.freedesktop.Telepathy.Channel.Type.Text - */ -static void -gabble_muc_channel_send (TpSvcChannelTypeText *iface, - guint type, - const gchar *text, - DBusGMethodInvocation *context) -{ - GabbleMucChannel *self = GABBLE_MUC_CHANNEL (iface); - GabbleMucChannelPrivate *priv; - GError *error = NULL; - - g_assert (GABBLE_IS_MUC_CHANNEL (self)); - - priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (self); - - if (!gabble_text_mixin_send (G_OBJECT (self), type, - LM_MESSAGE_SUB_TYPE_GROUPCHAT, TP_CHANNEL_CHAT_STATE_ACTIVE, priv->jid, - text, priv->conn, FALSE /* emit_signal */, &error)) - { - dbus_g_method_return_error (context, error); - g_error_free (error); - - return; - } - - tp_svc_channel_type_text_return_from_send (context); -} - - -static gboolean -gabble_muc_channel_add_member (GObject *obj, - TpHandle handle, - const gchar *message, - GError **error) -{ - GabbleMucChannelPrivate *priv; - TpGroupMixin *mixin; - const gchar *jid; - LmMessage *msg; - LmMessageNode *x_node, *invite_node; - gboolean result; - - priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (GABBLE_MUC_CHANNEL (obj)); - - mixin = TP_GROUP_MIXIN (obj); - - if (handle == mixin->self_handle) - { - TpIntSet *set_empty, *set_members, *set_pending; - GArray *arr_members; - - /* are we already a member or in remote pending? */ - if (tp_handle_set_is_member (mixin->members, handle) || - tp_handle_set_is_member (mixin->remote_pending, handle)) - { - g_set_error (error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE, - "already a member or in remote pending"); - - return FALSE; - } - - /* add ourself to remote pending and remove the inviter's - * main jid from the member list */ - set_empty = tp_intset_new (); - set_members = tp_intset_new (); - set_pending = tp_intset_new (); - - arr_members = tp_handle_set_to_array (mixin->members); - if (arr_members->len > 0) - { - tp_intset_add (set_members, g_array_index (arr_members, guint, 0)); - } - g_array_free (arr_members, TRUE); - - tp_intset_add (set_pending, handle); - - tp_group_mixin_change_members (obj, "", set_empty, set_members, - set_empty, set_pending, 0, TP_CHANNEL_GROUP_CHANGE_REASON_INVITED); - - tp_intset_destroy (set_empty); - tp_intset_destroy (set_members); - tp_intset_destroy (set_pending); - - /* seek to enter the room */ - result = send_join_request (GABBLE_MUC_CHANNEL (obj), NULL, error); - - g_object_set (obj, "state", - (result) ? MUC_STATE_INITIATED : MUC_STATE_ENDED, - NULL); - - /* deny adding */ - tp_group_mixin_change_flags (obj, 0, TP_CHANNEL_GROUP_FLAG_CAN_ADD); - - /* clear message queue (which might contain an invite reason) */ - tp_text_mixin_clear (obj); - - return result; - } - - /* check that we're indeed a member when attempting to invite others */ - if (priv->state < MUC_STATE_JOINED) - { - g_set_error (error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE, - "channel membership is required for inviting others"); - - return FALSE; - } - - msg = lm_message_new (priv->jid, LM_MESSAGE_TYPE_MESSAGE); - - x_node = lm_message_node_add_child (msg->node, "x", NULL); - lm_message_node_set_attribute (x_node, "xmlns", NS_MUC_USER); - - invite_node = lm_message_node_add_child (x_node, "invite", NULL); - - jid = tp_handle_inspect (TP_GROUP_MIXIN (obj)->handle_repo, handle); - - lm_message_node_set_attribute (invite_node, "to", jid); - - if (*message != '\0') - { - lm_message_node_add_child (invite_node, "reason", message); - } - - DEBUG ("sending MUC invitation for room %s to contact %u (%s) with reason " - "\"%s\"", priv->jid, handle, jid, message); - - result = _gabble_connection_send (priv->conn, msg, error); - lm_message_unref (msg); - - return result; -} - -static LmHandlerResult -kick_request_reply_cb (GabbleConnection *conn, LmMessage *sent_msg, - LmMessage *reply_msg, GObject *object, - gpointer user_data) -{ - const gchar *jid = user_data; - - if (lm_message_get_sub_type (reply_msg) != LM_MESSAGE_SUB_TYPE_RESULT) - { - g_warning ("%s: Failed to kick user %s from room", G_STRFUNC, jid); - } - - return LM_HANDLER_RESULT_REMOVE_MESSAGE; -} - -static gboolean -gabble_muc_channel_remove_member (GObject *obj, - TpHandle handle, - const gchar *message, - GError **error) -{ - GabbleMucChannelPrivate *priv; - LmMessage *msg; - LmMessageNode *query_node, *item_node; - const gchar *jid, *nick; - gboolean result; - - priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (GABBLE_MUC_CHANNEL (obj)); - - msg = lm_message_new_with_sub_type (priv->jid, LM_MESSAGE_TYPE_IQ, - LM_MESSAGE_SUB_TYPE_SET); - - query_node = lm_message_node_add_child (msg->node, "query", NULL); - lm_message_node_set_attribute (query_node, "xmlns", NS_MUC_ADMIN); - - item_node = lm_message_node_add_child (query_node, "item", NULL); - - jid = tp_handle_inspect (TP_GROUP_MIXIN (obj)->handle_repo, handle); - - nick = strchr (jid, '/'); - - lm_message_node_set_attributes (item_node, - "nick", nick, - "role", "none", - NULL); - - if (*message != '\0') - { - lm_message_node_add_child (item_node, "reason", message); - } - - DEBUG ("sending MUC kick request for contact %u (%s) to room %s with reason " - "\"%s\"", handle, jid, priv->jid, message); - - result = _gabble_connection_send_with_reply (priv->conn, msg, - kick_request_reply_cb, - obj, (gpointer) jid, - error); - - lm_message_unref (msg); - - return result; -} - - -static LmHandlerResult request_config_form_reply_cb (GabbleConnection *conn, - LmMessage *sent_msg, LmMessage *reply_msg, GObject *object, - gpointer user_data); - -static gboolean -gabble_muc_channel_do_set_properties (GObject *obj, - TpPropertiesContext *ctx, - GError **error) -{ - GabbleMucChannelPrivate *priv; - LmMessage *msg; - LmMessageNode *node; - gboolean success; - - priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (GABBLE_MUC_CHANNEL (obj)); - - g_assert (priv->properties_ctx == NULL); - - /* Changing subject? */ - if (tp_properties_context_has (ctx, ROOM_PROP_SUBJECT)) - { - const gchar *str; - - str = g_value_get_string (tp_properties_context_get (ctx, - ROOM_PROP_SUBJECT)); - - msg = lm_message_new_with_sub_type (priv->jid, - LM_MESSAGE_TYPE_MESSAGE, LM_MESSAGE_SUB_TYPE_GROUPCHAT); - lm_message_node_add_child (msg->node, "subject", str); - - success = _gabble_connection_send (priv->conn, msg, error); - - lm_message_unref (msg); - - if (!success) - return FALSE; - } - - /* Changing any other properties? */ - if (tp_properties_context_has_other_than (ctx, ROOM_PROP_SUBJECT)) - { - msg = lm_message_new_with_sub_type (priv->jid, - LM_MESSAGE_TYPE_IQ, LM_MESSAGE_SUB_TYPE_GET); - node = lm_message_node_add_child (msg->node, "query", NULL); - lm_message_node_set_attribute (node, "xmlns", NS_MUC_OWNER); - - success = _gabble_connection_send_with_reply (priv->conn, msg, - request_config_form_reply_cb, G_OBJECT (obj), NULL, - error); - - lm_message_unref (msg); - - if (!success) - return FALSE; - } - - priv->properties_ctx = ctx; - return TRUE; -} - -static LmHandlerResult request_config_form_submit_reply_cb ( - GabbleConnection *conn, LmMessage *sent_msg, LmMessage *reply_msg, - GObject *object, gpointer user_data); - -static LmHandlerResult -request_config_form_reply_cb (GabbleConnection *conn, LmMessage *sent_msg, - LmMessage *reply_msg, GObject *object, - gpointer user_data) -{ - GabbleMucChannel *chan = GABBLE_MUC_CHANNEL (object); - GabbleMucChannelPrivate *priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (chan); - TpPropertiesContext *ctx = priv->properties_ctx; - GError *error = NULL; - LmMessage *msg = NULL; - LmMessageNode *submit_node, *form_node, *node; - guint i, props_left; - - if (lm_message_get_sub_type (reply_msg) != LM_MESSAGE_SUB_TYPE_RESULT) - { - error = g_error_new (TP_ERRORS, TP_ERROR_PERMISSION_DENIED, - "request for configuration form denied"); - - goto OUT; - } - - form_node = config_form_get_form_node (reply_msg); - if (form_node == NULL) - goto PARSE_ERROR; - - /* initialize */ - msg = lm_message_new_with_sub_type (priv->jid, LM_MESSAGE_TYPE_IQ, - LM_MESSAGE_SUB_TYPE_SET); - - node = lm_message_node_add_child (msg->node, "query", NULL); - lm_message_node_set_attribute (node, "xmlns", NS_MUC_OWNER); - - submit_node = lm_message_node_add_child (node, "x", NULL); - lm_message_node_set_attributes (submit_node, - "xmlns", NS_X_DATA, - "type", "submit", - NULL); - - /* we assume that the number of props will fit in a guint on all supported - * platforms, so fail at compile time if this is no longer the case - */ -#if NUM_ROOM_PROPS > 32 -#error GabbleMUCChannel request_config_form_reply_cb needs porting to TpIntSet -#endif - - props_left = 0; - for (i = 0; i < NUM_ROOM_PROPS; i++) - { - if (i == ROOM_PROP_SUBJECT) - continue; - - if (tp_properties_context_has (ctx, i)) - props_left |= 1 << i; - } - - for (node = form_node->children; node; node = node->next) - { - const gchar *var, *prev_value; - LmMessageNode *field_node, *value_node; - guint id; - GType type; - gboolean invert; - gchar buf[16]; - const gchar *val_str; - gboolean val_bool; - - if (strcmp (node->name, "field") != 0) - { - DEBUG ("skipping node '%s'", node->name); - continue; - } - - var = lm_message_node_get_attribute (node, "var"); - if (var == NULL) { - DEBUG ("skipping node '%s' because of lacking var attribute", - node->name); - continue; - } - - value_node = lm_message_node_get_child (node, "value"); - if (value_node == NULL) - { - DEBUG ("skipping var '%s' because of lacking value attribute", - var); - continue; - } - - prev_value = lm_message_node_get_value (value_node); - - /* add the corresponding field node to the reply message */ - field_node = lm_message_node_add_child (submit_node, "field", NULL); - - lm_message_node_set_attribute (field_node, "var", var); - - val_str = lm_message_node_get_attribute (node, "type"); - if (val_str) - { - lm_message_node_set_attribute (field_node, "type", val_str); - } - - value_node = lm_message_node_add_child (field_node, "value", prev_value); - - id = INVALID_ROOM_PROP; - type = G_TYPE_BOOLEAN; - invert = FALSE; - val_str = NULL; - - if (strcmp (var, "anonymous") == 0) - { - id = ROOM_PROP_ANONYMOUS; - } - else if (strcmp (var, "muc#roomconfig_whois") == 0) - { - id = ROOM_PROP_ANONYMOUS; - - if (tp_properties_context_has (ctx, id)) - { - val_bool = g_value_get_boolean ( - tp_properties_context_get (ctx, id)); - val_str = (val_bool) ? "moderators" : "anyone"; - } - } - else if (strcmp (var, "muc#owner_whois") == 0) - { - id = ROOM_PROP_ANONYMOUS; - - if (tp_properties_context_has (ctx, id)) - { - val_bool = g_value_get_boolean ( - tp_properties_context_get (ctx, id)); - val_str = (val_bool) ? "admins" : "anyone"; - } - } - else if (strcmp (var, "members_only") == 0 || - strcmp (var, "muc#roomconfig_membersonly") == 0 || - strcmp (var, "muc#owner_inviteonly") == 0) - { - id = ROOM_PROP_INVITE_ONLY; - } - else if (strcmp (var, "moderated") == 0 || - strcmp (var, "muc#roomconfig_moderatedroom") == 0 || - strcmp (var, "muc#owner_moderatedroom") == 0) - { - id = ROOM_PROP_MODERATED; - } - else if (strcmp (var, "title") == 0 || - strcmp (var, "muc#roomconfig_roomname") == 0 || - strcmp (var, "muc#owner_roomname") == 0) - { - id = ROOM_PROP_NAME; - type = G_TYPE_STRING; - } - else if (strcmp (var, "muc#roomconfig_roomdesc") == 0 || - strcmp (var, "muc#owner_roomdesc") == 0) - { - id = ROOM_PROP_DESCRIPTION; - type = G_TYPE_STRING; - } - else if (strcmp (var, "password") == 0 || - strcmp (var, "muc#roomconfig_roomsecret") == 0 || - strcmp (var, "muc#owner_roomsecret") == 0) - { - id = ROOM_PROP_PASSWORD; - type = G_TYPE_STRING; - } - else if (strcmp (var, "password_protected") == 0 || - strcmp (var, "muc#roomconfig_passwordprotectedroom") == 0 || - strcmp (var, "muc#owner_passwordprotectedroom") == 0) - { - id = ROOM_PROP_PASSWORD_REQUIRED; - } - else if (strcmp (var, "persistent") == 0 || - strcmp (var, "muc#roomconfig_persistentroom") == 0 || - strcmp (var, "muc#owner_persistentroom") == 0) - { - id = ROOM_PROP_PERSISTENT; - } - else if (strcmp (var, "public") == 0 || - strcmp (var, "muc#roomconfig_publicroom") == 0 || - strcmp (var, "muc#owner_publicroom") == 0) - { - id = ROOM_PROP_PRIVATE; - invert = TRUE; - } - else - { - g_warning ("%s: ignoring field '%s'", G_STRFUNC, var); - continue; - } - - DEBUG ("looking up %s", room_property_signatures[id].name); - - if (!tp_properties_context_has (ctx, id)) - continue; - - if (!val_str) - { - const GValue *provided_value; - - provided_value = tp_properties_context_get (ctx, id); - - switch (type) { - case G_TYPE_BOOLEAN: - val_bool = g_value_get_boolean (provided_value); - sprintf (buf, "%d", (invert) ? !val_bool : val_bool); - val_str = buf; - break; - case G_TYPE_STRING: - val_str = g_value_get_string (provided_value); - break; - default: - g_assert_not_reached (); - } - } - - lm_message_node_set_value (value_node, val_str); - - props_left &= ~(1 << id); - } - - if (props_left != 0) - { - printf (TP_ANSI_BOLD_ON TP_ANSI_FG_WHITE TP_ANSI_BG_RED - "\n%s: the following properties were not substituted:\n", - G_STRFUNC); - - for (i = 0; i < NUM_ROOM_PROPS; i++) - { - if ((props_left & (1 << i)) != 0) - { - printf (" %s\n", room_property_signatures[i].name); - } - } - - printf ("\nthis is a MUC server compatibility bug in gabble, please " - "report it with a full debug log attached (running gabble " - "with LM_DEBUG=net)" TP_ANSI_RESET "\n\n"); - fflush (stdout); - - error = g_error_new (TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, - "not all properties were substituted"); - goto OUT; - } - - _gabble_connection_send_with_reply (priv->conn, msg, - request_config_form_submit_reply_cb, G_OBJECT (object), - NULL, &error); - - goto OUT; - -PARSE_ERROR: - error = g_error_new (TP_ERRORS, TP_ERROR_NOT_AVAILABLE, - "error parsing reply from server"); - -OUT: - if (error) - { - tp_properties_context_return (ctx, error); - priv->properties_ctx = NULL; - } - - if (msg) - lm_message_unref (msg); - - return LM_HANDLER_RESULT_REMOVE_MESSAGE; -} - -static LmHandlerResult -request_config_form_submit_reply_cb (GabbleConnection *conn, - LmMessage *sent_msg, - LmMessage *reply_msg, - GObject *object, - gpointer user_data) -{ - GabbleMucChannel *chan = GABBLE_MUC_CHANNEL (object); - GabbleMucChannelPrivate *priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (chan); - TpPropertiesContext *ctx = priv->properties_ctx; - GError *error = NULL; - gboolean returned; - - if (lm_message_get_sub_type (reply_msg) != LM_MESSAGE_SUB_TYPE_RESULT) - { - error = g_error_new (TP_ERRORS, TP_ERROR_PERMISSION_DENIED, - "submitted configuration form was rejected"); - } - - if (!error) - { - guint i; - - for (i = 0; i < NUM_ROOM_PROPS; i++) - { - if (i != ROOM_PROP_SUBJECT) - tp_properties_context_remove (ctx, i); - } - - returned = tp_properties_context_return_if_done (ctx); - } - else - { - tp_properties_context_return (ctx, error); - returned = TRUE; - - /* Get the properties into a consistent state. */ - room_properties_update (chan); - } - - if (returned) - priv->properties_ctx = NULL; - - return LM_HANDLER_RESULT_REMOVE_MESSAGE; -} - -/** - * gabble_muc_channel_set_chat_state - * - * Implements D-Bus method SetChatState - * on interface org.freedesktop.Telepathy.Channel.Interface.ChatState - */ -static void -gabble_muc_channel_set_chat_state (TpSvcChannelInterfaceChatState *iface, - guint state, - DBusGMethodInvocation *context) -{ - GabbleMucChannel *self = GABBLE_MUC_CHANNEL (iface); - GabbleMucChannelPrivate *priv; - GError *error = NULL; - - g_assert (GABBLE_IS_MUC_CHANNEL (self)); - - priv = GABBLE_MUC_CHANNEL_GET_PRIVATE (self); - - if (state >= NUM_TP_CHANNEL_CHAT_STATES) - { - DEBUG ("invalid state %u", state); - - g_set_error (&error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, - "invalid state: %u", state); - } - - if (state == TP_CHANNEL_CHAT_STATE_GONE) - { - /* We cannot explicitly set the Gone state */ - DEBUG ("you may not explicitly set the Gone state"); - - g_set_error (&error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, - "you may not explicitly set the Gone state"); - } - - if (error != NULL || !gabble_text_mixin_send (G_OBJECT (self), - TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE, LM_MESSAGE_SUB_TYPE_GROUPCHAT, - state, priv->jid, NULL, priv->conn, FALSE /* emit_signal */, &error)) - { - dbus_g_method_return_error (context, error); - g_error_free (error); - - return; - } - - tp_svc_channel_interface_chat_state_return_from_set_chat_state (context); -} - -static void -channel_iface_init (gpointer g_iface, gpointer iface_data) -{ - TpSvcChannelClass *klass = (TpSvcChannelClass *)g_iface; - -#define IMPLEMENT(x) tp_svc_channel_implement_##x (\ - klass, gabble_muc_channel_##x) - IMPLEMENT(close); - IMPLEMENT(get_channel_type); - IMPLEMENT(get_handle); - IMPLEMENT(get_interfaces); -#undef IMPLEMENT -} - -static void -text_iface_init (gpointer g_iface, gpointer iface_data) -{ - TpSvcChannelTypeTextClass *klass = (TpSvcChannelTypeTextClass *)g_iface; - - tp_text_mixin_iface_init (g_iface, iface_data); -#define IMPLEMENT(x) tp_svc_channel_type_text_implement_##x (\ - klass, gabble_muc_channel_##x) - IMPLEMENT(send); -#undef IMPLEMENT -} - -static void -password_iface_init (gpointer g_iface, gpointer iface_data) -{ - TpSvcChannelInterfacePasswordClass *klass = - (TpSvcChannelInterfacePasswordClass *)g_iface; - -#define IMPLEMENT(x) tp_svc_channel_interface_password_implement_##x (\ - klass, gabble_muc_channel_##x) - IMPLEMENT(get_password_flags); - IMPLEMENT(provide_password); -#undef IMPLEMENT -} - -static void -chat_state_iface_init (gpointer g_iface, gpointer iface_data) -{ - TpSvcChannelInterfaceChatStateClass *klass = - (TpSvcChannelInterfaceChatStateClass *)g_iface; - -#define IMPLEMENT(x) tp_svc_channel_interface_chat_state_implement_##x (\ - klass, gabble_muc_channel_##x) - IMPLEMENT(set_chat_state); -#undef IMPLEMENT -} diff --git a/src/gabble-muc-channel.h b/src/gabble-muc-channel.h deleted file mode 100644 index 66b9dc564..000000000 --- a/src/gabble-muc-channel.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * gabble-muc-channel.h - Header for GabbleMucChannel - * Copyright (C) 2006 Collabora Ltd. - * Copyright (C) 2006 Nokia Corporation - * @author Ole Andre Vadla Ravnaas <ole.andre.ravnaas@collabora.co.uk> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef __GABBLE_MUC_CHANNEL_H__ -#define __GABBLE_MUC_CHANNEL_H__ - -#include <glib-object.h> - -#include <telepathy-glib/group-mixin.h> -#include <telepathy-glib/properties-mixin.h> -#include "text-mixin.h" - -G_BEGIN_DECLS - -typedef struct _GabbleMucChannel GabbleMucChannel; -typedef struct _GabbleMucChannelClass GabbleMucChannelClass; - -struct _GabbleMucChannelClass { - GObjectClass parent_class; - - TpGroupMixinClass group_class; - TpPropertiesMixinClass properties_class; - GabbleTextMixinClass text_class; -}; - -struct _GabbleMucChannel { - GObject parent; - - TpGroupMixin group; - TpPropertiesMixin properties; - GabbleTextMixin text; - - gpointer priv; -}; - -GType gabble_muc_channel_get_type (void); - -/* TYPE MACROS */ -#define GABBLE_TYPE_MUC_CHANNEL \ - (gabble_muc_channel_get_type ()) -#define GABBLE_MUC_CHANNEL(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_MUC_CHANNEL, \ - GabbleMucChannel)) -#define GABBLE_MUC_CHANNEL_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_MUC_CHANNEL,\ - GabbleMucChannelClass)) -#define GABBLE_IS_MUC_CHANNEL(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_MUC_CHANNEL)) -#define GABBLE_IS_MUC_CHANNEL_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_MUC_CHANNEL)) -#define GABBLE_MUC_CHANNEL_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_MUC_CHANNEL,\ - GabbleMucChannelClass)) - -gboolean _gabble_muc_channel_is_ready (GabbleMucChannel *chan); -void _gabble_muc_channel_presence_error (GabbleMucChannel *chan, - const gchar *jid, LmMessageNode *pres_node); -void _gabble_muc_channel_member_presence_updated (GabbleMucChannel *chan, - TpHandle handle, LmMessage *message, LmMessageNode *x_node); -void _gabble_muc_channel_receive (GabbleMucChannel *chan, - TpChannelTextMessageType msg_type, TpHandleType handle_type, - TpHandle sender, time_t timestamp, const gchar *text, LmMessage *msg); - -void _gabble_muc_channel_handle_invited (GabbleMucChannel *chan, - TpHandle inviter, const gchar *message); -void _gabble_muc_channel_state_receive (GabbleMucChannel *chan, - guint state, guint from_handle); - -G_END_DECLS - -#endif /* #ifndef __GABBLE_MUC_CHANNEL_H__*/ diff --git a/src/gabble-register.c b/src/gabble-register.c deleted file mode 100644 index bba748cf2..000000000 --- a/src/gabble-register.c +++ /dev/null @@ -1,548 +0,0 @@ -/* - * gabble-register.c - Source for Gabble account registration - * - * Copyright (C) 2006 Collabora Ltd. - * Copyright (C) 2006 Nokia Corporation - * @author Ole Andre Vadla Ravnaas <ole.andre.ravnaas@collabora.co.uk> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "gabble-register.h" - -#define DBUS_API_SUBJECT_TO_CHANGE - -#include <dbus/dbus-glib.h> -#include <dbus/dbus-glib-lowlevel.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <ctype.h> - -#include <telepathy-glib/dbus.h> -#include <telepathy-glib/errors.h> - -#include "gabble-connection.h" -#include "gabble-error.h" -#include "gabble-signals-marshal.h" -#include "namespaces.h" -#include "util.h" -#include "libmd5-rfc/md5.h" - -/* signal enum */ -enum -{ - FINISHED, - LAST_SIGNAL -}; - -static guint signals[LAST_SIGNAL] = { 0, }; - -/* properties */ -enum -{ - PROP_CONNECTION = 1, - LAST_PROPERTY -}; - -G_DEFINE_TYPE(GabbleRegister, gabble_register, G_TYPE_OBJECT); - -/* private structure */ -typedef struct _GabbleRegisterPrivate GabbleRegisterPrivate; -struct _GabbleRegisterPrivate -{ - GabbleConnection *conn; - - gboolean dispose_has_run; -}; - -#define GABBLE_REGISTER_GET_PRIVATE(o) \ - (G_TYPE_INSTANCE_GET_PRIVATE ((o), GABBLE_TYPE_REGISTER,\ - GabbleRegisterPrivate)) - -static void -gabble_register_init (GabbleRegister *obj) -{ -} - -static void gabble_register_set_property (GObject *object, guint property_id, - const GValue *value, GParamSpec *pspec); -static void gabble_register_get_property (GObject *object, guint property_id, - GValue *value, GParamSpec *pspec); -static void gabble_register_dispose (GObject *object); -static void gabble_register_finalize (GObject *object); - -static void -gabble_register_class_init (GabbleRegisterClass *gabble_register_class) -{ - GObjectClass *object_class = G_OBJECT_CLASS (gabble_register_class); - GParamSpec *param_spec; - - g_type_class_add_private (gabble_register_class, - sizeof (GabbleRegisterPrivate)); - - object_class->get_property = gabble_register_get_property; - object_class->set_property = gabble_register_set_property; - - object_class->dispose = gabble_register_dispose; - object_class->finalize = gabble_register_finalize; - - param_spec = g_param_spec_object ("connection", "GabbleConnection object", - "Gabble connection object that owns this " - "GabbleRegister object.", - GABBLE_TYPE_CONNECTION, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | - G_PARAM_STATIC_NICK | - G_PARAM_STATIC_BLURB); - g_object_class_install_property (object_class, PROP_CONNECTION, param_spec); - - signals[FINISHED] = - g_signal_new ("finished", - G_OBJECT_CLASS_TYPE (gabble_register_class), - G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, - 0, - NULL, NULL, - gabble_marshal_VOID__BOOLEAN_INT_STRING, - G_TYPE_NONE, 3, G_TYPE_BOOLEAN, G_TYPE_INT, G_TYPE_STRING); -} - -static void -gabble_register_get_property (GObject *object, - guint property_id, - GValue *value, - GParamSpec *pspec) -{ - GabbleRegister *chan = GABBLE_REGISTER (object); - GabbleRegisterPrivate *priv = GABBLE_REGISTER_GET_PRIVATE (chan); - - switch (property_id) { - case PROP_CONNECTION: - g_value_set_object (value, priv->conn); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } -} - -static void -gabble_register_set_property (GObject *object, - guint property_id, - const GValue *value, - GParamSpec *pspec) -{ - GabbleRegister *chan = GABBLE_REGISTER (object); - GabbleRegisterPrivate *priv = GABBLE_REGISTER_GET_PRIVATE (chan); - - switch (property_id) { - case PROP_CONNECTION: - priv->conn = g_value_get_object (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } -} - -void -gabble_register_dispose (GObject *object) -{ - GabbleRegister *self = GABBLE_REGISTER (object); - GabbleRegisterPrivate *priv = GABBLE_REGISTER_GET_PRIVATE (self); - - if (priv->dispose_has_run) - return; - - priv->dispose_has_run = TRUE; - - g_debug ("%s: dispose called", G_STRFUNC); - - if (G_OBJECT_CLASS (gabble_register_parent_class)->dispose) - G_OBJECT_CLASS (gabble_register_parent_class)->dispose (object); -} - -void -gabble_register_finalize (GObject *object) -{ - g_debug ("%s called with %p", G_STRFUNC, object); - - G_OBJECT_CLASS (gabble_register_parent_class)->finalize (object); -} - -/** - * gabble_register_new: - * - * Creates an object to use for account registration. - * - * @conn: The #GabbleConnection to register an account on - */ -GabbleRegister * -gabble_register_new (GabbleConnection *conn) -{ - g_return_val_if_fail (GABBLE_IS_CONNECTION (conn), NULL); - return GABBLE_REGISTER (g_object_new (GABBLE_TYPE_REGISTER, - "connection", conn, NULL)); -} - -typedef enum { STAGE_NOKIA_IV, STAGE_REGISTER } RegistrationStage; -static void send_registration (GabbleRegister *, RegistrationStage); - -static LmHandlerResult -nokia_iv_set_reply_cb (GabbleConnection *conn, - LmMessage *sent_msg, - LmMessage *reply_msg, - GObject *object, - gpointer user_data) -{ - if (lm_message_get_sub_type (reply_msg) != LM_MESSAGE_SUB_TYPE_RESULT) - { - LmMessageNode *node; - gint code = TP_ERROR_NOT_AVAILABLE; - GString *msg; - - msg = g_string_sized_new (30); - g_string_append (msg, "Request failed"); - - node = lm_message_node_get_child (reply_msg->node, "error"); - if (node) - { - GabbleXmppError error; - - error = gabble_xmpp_error_from_node (node); - - if (error != INVALID_XMPP_ERROR) - { - g_string_append_printf (msg, ": %s", - gabble_xmpp_error_string (error)); - } - } - - g_signal_emit (object, signals[FINISHED], 0, FALSE, code, msg->str); - g_string_free (msg, TRUE); - } - else - { - /* IV pre-authorization finished - move on to account registration */ - send_registration (GABBLE_REGISTER (object), STAGE_REGISTER); - } - - return LM_HANDLER_RESULT_REMOVE_MESSAGE; -} - -static LmHandlerResult -nokia_iv_get_reply_cb (GabbleConnection *conn, - LmMessage *sent_msg, - LmMessage *reply_msg, - GObject *object, - gpointer user_data) -{ - GabbleRegister *reg = GABBLE_REGISTER (object); - GabbleRegisterPrivate *priv = GABBLE_REGISTER_GET_PRIVATE (reg); - GError *error = NULL; - gint err_code = -1; - const gchar *err_msg = NULL; - LmMessage *msg = NULL; - LmMessageNode *query_node, *challenge_node; - gchar *auth_mac, *auth_btid; - gchar *challenge; - gchar response[33]; - guint i; - md5_byte_t digest[16]; - md5_state_t calculator; - - if (lm_message_get_sub_type (reply_msg) != LM_MESSAGE_SUB_TYPE_RESULT) - { - /* We tried, but the server doesn't seem to support it. Never mind, - let's try to log in anyway and see what happens. */ - g_signal_emit (object, signals[FINISHED], 0, TRUE, -1, NULL); - goto OUT; - } - - /* sanity check the reply to some degree ... */ - query_node = lm_message_node_get_child_with_namespace (reply_msg->node, - "query", NS_NOKIA_IV); - - if (query_node == NULL) - goto ERROR_MALFORMED_REPLY; - - challenge_node = lm_message_node_get_child (query_node, "challenge"); - if (!challenge_node) - goto ERROR_MALFORMED_REPLY; - challenge = g_strdup (lm_message_node_get_value (challenge_node)); - if (!challenge) - goto ERROR_MALFORMED_REPLY; - - /* craft a reply */ - msg = lm_message_new_with_sub_type (NULL, LM_MESSAGE_TYPE_IQ, - LM_MESSAGE_SUB_TYPE_SET); - - query_node = lm_message_node_add_child (msg->node, "query", NULL); - lm_message_node_set_attribute (query_node, "xmlns", NS_NOKIA_IV); - - g_object_get (priv->conn, - "auth-mac", &auth_mac, - "auth-btid", &auth_btid, - NULL); - - for (i = 0; i < strlen (auth_mac); i++) - auth_mac[i] = tolower (auth_mac[i]); - - for (i = 0; i < strlen (auth_btid); i++) - auth_btid[i] = tolower (auth_btid[i]); - - for (i = 0; i < strlen (challenge); i++) - challenge[i] = tolower (challenge[i]); - - md5_init (&calculator); - md5_append (&calculator, (const md5_byte_t *)auth_btid, - strlen (auth_btid)); - md5_append (&calculator, (const md5_byte_t *)":", 1); - md5_append (&calculator, (const md5_byte_t *)challenge, strlen (challenge)); - md5_finish (&calculator, digest); - - for (i = 0; i < 16; i++) - { - sprintf (response + i*2, "%02x",digest[i]); - } - - lm_message_node_add_child (query_node, "mac", auth_mac); - lm_message_node_add_child (query_node, "response", response); - - g_free (auth_mac); - g_free (auth_btid); - g_free (challenge); - - if (!_gabble_connection_send_with_reply (priv->conn, msg, - nokia_iv_set_reply_cb, - G_OBJECT (reg), NULL, &error)) - { - err_code = error->code; - err_msg = error->message; - } - - goto OUT; - -ERROR_MALFORMED_REPLY: - err_code = TP_ERROR_NOT_AVAILABLE; - err_msg = "Malformed reply"; - -OUT: - if (err_code != -1) - { - g_signal_emit (reg, signals[FINISHED], 0, FALSE, err_code, err_msg); - } - - if (msg) - lm_message_unref (msg); - - if (error) - g_error_free (error); - - return LM_HANDLER_RESULT_REMOVE_MESSAGE; -} - -static LmHandlerResult -set_reply_cb (GabbleConnection *conn, - LmMessage *sent_msg, - LmMessage *reply_msg, - GObject *object, - gpointer user_data) -{ - if (lm_message_get_sub_type (reply_msg) != LM_MESSAGE_SUB_TYPE_RESULT) - { - LmMessageNode *node; - gint code = TP_ERROR_NOT_AVAILABLE; - GString *msg; - - msg = g_string_sized_new (30); - g_string_append (msg, "Request failed"); - - node = lm_message_node_get_child (reply_msg->node, "error"); - if (node) - { - GabbleXmppError error; - - error = gabble_xmpp_error_from_node (node); - if (error == XMPP_ERROR_CONFLICT) - { - code = TP_ERROR_INVALID_ARGUMENT; - } - - if (error != INVALID_XMPP_ERROR) - { - g_string_append_printf (msg, ": %s", - gabble_xmpp_error_string (error)); - } - } - - g_signal_emit (object, signals[FINISHED], 0, FALSE, code, msg->str); - g_string_free (msg, TRUE); - } - else - { - g_signal_emit (object, signals[FINISHED], 0, TRUE, -1, NULL); - } - - return LM_HANDLER_RESULT_REMOVE_MESSAGE; -} - -static LmHandlerResult -get_reply_cb (GabbleConnection *conn, - LmMessage *sent_msg, - LmMessage *reply_msg, - GObject *object, - gpointer user_data) -{ - GabbleRegister *reg = GABBLE_REGISTER (object); - GabbleRegisterPrivate *priv = GABBLE_REGISTER_GET_PRIVATE (reg); - GError *error = NULL; - gint err_code = -1; - const gchar *err_msg = NULL; - LmMessage *msg = NULL; - LmMessageNode *query_node; - gchar *username, *password; - - if (lm_message_get_sub_type (reply_msg) != LM_MESSAGE_SUB_TYPE_RESULT) - { - err_code = TP_ERROR_NOT_AVAILABLE; - err_msg = "Server doesn't support " NS_REGISTER; - - goto OUT; - } - - /* sanity check the reply to some degree ... */ - query_node = lm_message_node_get_child_with_namespace (reply_msg->node, - "query", NS_REGISTER); - - if (query_node == NULL) - goto ERROR_MALFORMED_REPLY; - - if (!lm_message_node_get_child (query_node, "username")) - goto ERROR_MALFORMED_REPLY; - - if (!lm_message_node_get_child (query_node, "password")) - goto ERROR_MALFORMED_REPLY; - - /* craft a reply */ - msg = lm_message_new_with_sub_type (NULL, LM_MESSAGE_TYPE_IQ, - LM_MESSAGE_SUB_TYPE_SET); - - query_node = lm_message_node_add_child (msg->node, "query", NULL); - lm_message_node_set_attribute (query_node, "xmlns", NS_REGISTER); - - g_object_get (priv->conn, - "username", &username, - "password", &password, - NULL); - - lm_message_node_add_child (query_node, "username", username); - lm_message_node_add_child (query_node, "password", password); - - g_free (username); - g_free (password); - - if (!_gabble_connection_send_with_reply (priv->conn, msg, set_reply_cb, - G_OBJECT (reg), NULL, &error)) - { - err_code = error->code; - err_msg = error->message; - } - - goto OUT; - -ERROR_MALFORMED_REPLY: - err_code = TP_ERROR_NOT_AVAILABLE; - err_msg = "Malformed reply"; - -OUT: - if (err_code != -1) - { - g_signal_emit (reg, signals[FINISHED], 0, FALSE, err_code, err_msg); - } - - if (msg) - lm_message_unref (msg); - - if (error) - g_error_free (error); - - return LM_HANDLER_RESULT_REMOVE_MESSAGE; -} - -static void -send_registration (GabbleRegister *reg, RegistrationStage stage) -{ - GabbleRegisterPrivate *priv = GABBLE_REGISTER_GET_PRIVATE (reg); - LmMessage *msg; - LmMessageNode *node; - GError *error = NULL; - GabbleConnectionMsgReplyFunc handler; - - msg = lm_message_new_with_sub_type (NULL, LM_MESSAGE_TYPE_IQ, - LM_MESSAGE_SUB_TYPE_GET); - node = lm_message_node_add_child (msg->node, "query", NULL); - if (stage == STAGE_NOKIA_IV) - { - lm_message_node_set_attribute (node, "xmlns", NS_NOKIA_IV); - handler = nokia_iv_get_reply_cb; - } - else - { - lm_message_node_set_attribute (node, "xmlns", NS_REGISTER); - handler = get_reply_cb; - } - if (!_gabble_connection_send_with_reply (priv->conn, msg, handler, - G_OBJECT (reg), NULL, &error)) - { - g_signal_emit (reg, signals[FINISHED], 0, FALSE, error->code, - error->message); - g_error_free (error); - } - - lm_message_unref (msg); -} - -/** - * gabble_register_start: - * - * Start account registration. - * - * @reg: The #GabbleRegister object performing the registration - */ -void gabble_register_start (GabbleRegister *reg) -{ - GabbleRegisterPrivate *priv = GABBLE_REGISTER_GET_PRIVATE (reg); - gchar *auth_mac, *auth_btid; - - g_object_get (priv->conn, "auth-mac", &auth_mac, NULL); - g_object_get (priv->conn, "auth-btid", &auth_btid, NULL); - - if (auth_mac && auth_btid) - { - send_registration (reg, STAGE_NOKIA_IV); - } - else - { - if (auth_mac || auth_btid) - { - g_warning ("Only one of 'mac', 'btid' supplied - not performing " - "privileged device authorization"); - } - send_registration (reg, STAGE_REGISTER); - } - g_free (auth_mac); - g_free (auth_btid); -} diff --git a/src/gabble-register.h b/src/gabble-register.h deleted file mode 100644 index c6fc41c5b..000000000 --- a/src/gabble-register.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * gabble-register.h - Headers for Gabble account registration - * - * Copyright (C) 2006 Collabora Ltd. - * Copyright (C) 2006 Nokia Corporation - * @author Ole Andre Vadla Ravnaas <ole.andre.ravnaas@collabora.co.uk> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef __GABBLE_REGISTER_H__ -#define __GABBLE_REGISTER_H__ - -#include <glib-object.h> - -#include <loudmouth/loudmouth.h> - -#include "gabble-connection.h" - -G_BEGIN_DECLS - -typedef struct _GabbleRegister GabbleRegister; -typedef struct _GabbleRegisterClass GabbleRegisterClass; - -GType gabble_register_get_type (void); - -/* TYPE MACROS */ -#define GABBLE_TYPE_REGISTER \ - (gabble_register_get_type ()) -#define GABBLE_REGISTER(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_REGISTER, GabbleRegister)) -#define GABBLE_REGISTER_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_REGISTER, GabbleRegisterClass)) -#define GABBLE_IS_REGISTER(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_REGISTER)) -#define GABBLE_IS_REGISTER_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_REGISTER)) -#define GABBLE_REGISTER_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_REGISTER,\ - GabbleRegisterClass)) - -struct _GabbleRegisterClass { - GObjectClass parent_class; -}; - -struct _GabbleRegister { - GObject parent; -}; - -GabbleRegister *gabble_register_new (GabbleConnection *conn); -void gabble_register_start (GabbleRegister *reg); - -G_END_DECLS - -#endif /* #ifndef __GABBLE_REGISTER_H__ */ diff --git a/src/gabble-roomlist-channel.c b/src/gabble-roomlist-channel.c deleted file mode 100644 index 44bc6479b..000000000 --- a/src/gabble-roomlist-channel.c +++ /dev/null @@ -1,712 +0,0 @@ -/* - * gabble-roomlist-channel.c - Source for GabbleRoomlistChannel - * Copyright (C) 2005 Collabora Ltd. - * Copyright (C) 2005 Nokia Corporation - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "gabble-roomlist-channel.h" - -#include <dbus/dbus-glib.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#define DEBUG_FLAG GABBLE_DEBUG_ROOMLIST - -#include "debug.h" -#include "disco.h" -#include "gabble-connection.h" -#include <telepathy-glib/dbus.h> -#include <telepathy-glib/enums.h> -#include <telepathy-glib/interfaces.h> -#include <telepathy-glib/channel-iface.h> -#include <telepathy-glib/svc-channel.h> -#include "namespaces.h" -#include "util.h" - -#define TP_TYPE_ROOM_STRUCT (dbus_g_type_get_struct ("GValueArray", \ - G_TYPE_UINT, \ - G_TYPE_STRING, \ - dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_VALUE), \ - G_TYPE_INVALID)) - -#define TP_TYPE_ROOM_LIST (dbus_g_type_get_collection ("GPtrArray", \ - TP_TYPE_ROOM_STRUCT)) - -static void channel_iface_init (gpointer, gpointer); -static void roomlist_iface_init (gpointer, gpointer); - -G_DEFINE_TYPE_WITH_CODE (GabbleRoomlistChannel, gabble_roomlist_channel, - G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL, channel_iface_init); - G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_TYPE_ROOM_LIST, - roomlist_iface_init); - G_IMPLEMENT_INTERFACE (TP_TYPE_CHANNEL_IFACE, NULL) - ); - -/* properties */ -enum -{ - PROP_OBJECT_PATH = 1, - PROP_CHANNEL_TYPE, - PROP_HANDLE_TYPE, - PROP_HANDLE, - PROP_CONNECTION, - PROP_CONFERENCE_SERVER, - LAST_PROPERTY -}; - -/* private structure */ -typedef struct _GabbleRoomlistChannelPrivate GabbleRoomlistChannelPrivate; - -struct _GabbleRoomlistChannelPrivate -{ - GabbleConnection *conn; - gchar *object_path; - gchar *conference_server; - - gboolean closed; - gboolean listing; - - gpointer disco_pipeline; - TpHandleSet *signalled_rooms; - - GPtrArray *pending_room_signals; - guint timer_source_id; - - gboolean dispose_has_run; -}; - -#define GABBLE_ROOMLIST_CHANNEL_GET_PRIVATE(obj) \ - ((GabbleRoomlistChannelPrivate *)obj->priv) - -#define ROOM_SIGNAL_INTERVAL 300 - -static gboolean emit_room_signal (gpointer data); - -static void -gabble_roomlist_channel_init (GabbleRoomlistChannel *self) -{ - GabbleRoomlistChannelPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self, - GABBLE_TYPE_ROOMLIST_CHANNEL, GabbleRoomlistChannelPrivate); - - self->priv = priv; - - priv->pending_room_signals = g_ptr_array_new (); -} - - -static GObject * -gabble_roomlist_channel_constructor (GType type, guint n_props, - GObjectConstructParam *props) -{ - GObject *obj; - GabbleRoomlistChannelPrivate *priv; - DBusGConnection *bus; - - obj = G_OBJECT_CLASS (gabble_roomlist_channel_parent_class)-> - constructor (type, n_props, props); - priv = GABBLE_ROOMLIST_CHANNEL_GET_PRIVATE (GABBLE_ROOMLIST_CHANNEL (obj)); - - bus = tp_get_bus (); - dbus_g_connection_register_g_object (bus, priv->object_path, obj); - - return obj; -} - -static void -gabble_roomlist_channel_get_property (GObject *object, - guint property_id, - GValue *value, - GParamSpec *pspec) -{ - GabbleRoomlistChannel *chan = GABBLE_ROOMLIST_CHANNEL (object); - GabbleRoomlistChannelPrivate *priv = - GABBLE_ROOMLIST_CHANNEL_GET_PRIVATE (chan); - - switch (property_id) { - case PROP_OBJECT_PATH: - g_value_set_string (value, priv->object_path); - break; - case PROP_CHANNEL_TYPE: - g_value_set_static_string (value, TP_IFACE_CHANNEL_TYPE_ROOM_LIST); - break; - case PROP_HANDLE_TYPE: - g_value_set_uint (value, TP_HANDLE_TYPE_NONE); - break; - case PROP_HANDLE: - g_value_set_uint (value, 0); - break; - case PROP_CONNECTION: - g_value_set_object (value, priv->conn); - break; - case PROP_CONFERENCE_SERVER: - g_value_set_string (value, priv->conference_server); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } -} - -static void -gabble_roomlist_channel_set_property (GObject *object, - guint property_id, - const GValue *value, - GParamSpec *pspec) -{ - GabbleRoomlistChannel *chan = GABBLE_ROOMLIST_CHANNEL (object); - GabbleRoomlistChannelPrivate *priv = - GABBLE_ROOMLIST_CHANNEL_GET_PRIVATE (chan); - TpBaseConnection *conn; - TpHandleRepoIface *room_handles; - TpHandleSet *new_signalled_rooms; - - switch (property_id) { - case PROP_OBJECT_PATH: - g_free (priv->object_path); - priv->object_path = g_value_dup_string (value); - break; - case PROP_HANDLE: - /* this property is writable in the interface, but not actually - * meaningfully changable on this channel, so we do nothing */ - break; - case PROP_HANDLE_TYPE: - /* this property is writable in the interface, but not actually - * meaningfully changable on this channel, so we do nothing */ - break; - case PROP_CONNECTION: - priv->conn = g_value_get_object (value); - conn = (TpBaseConnection *)priv->conn; - - room_handles = tp_base_connection_get_handles (conn, - TP_HANDLE_TYPE_ROOM); - - new_signalled_rooms = tp_handle_set_new (room_handles); - if (priv->signalled_rooms != NULL) - { - const TpIntSet *add; - TpIntSet *tmp; - add = tp_handle_set_peek (priv->signalled_rooms); - tmp = tp_handle_set_update (new_signalled_rooms, add); - tp_handle_set_destroy (priv->signalled_rooms); - tp_intset_destroy (tmp); - } - priv->signalled_rooms = new_signalled_rooms; - break; - case PROP_CONFERENCE_SERVER: - g_free (priv->conference_server); - priv->conference_server = g_value_dup_string (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } -} - -static void gabble_roomlist_channel_dispose (GObject *object); -static void gabble_roomlist_channel_finalize (GObject *object); - -static void -gabble_roomlist_channel_class_init (GabbleRoomlistChannelClass *gabble_roomlist_channel_class) -{ - GObjectClass *object_class = G_OBJECT_CLASS (gabble_roomlist_channel_class); - GParamSpec *param_spec; - - g_type_class_add_private (gabble_roomlist_channel_class, - sizeof (GabbleRoomlistChannelPrivate)); - - object_class->constructor = gabble_roomlist_channel_constructor; - - object_class->get_property = gabble_roomlist_channel_get_property; - object_class->set_property = gabble_roomlist_channel_set_property; - - object_class->dispose = gabble_roomlist_channel_dispose; - object_class->finalize = gabble_roomlist_channel_finalize; - - g_object_class_override_property (object_class, PROP_OBJECT_PATH, - "object-path"); - g_object_class_override_property (object_class, PROP_CHANNEL_TYPE, - "channel-type"); - g_object_class_override_property (object_class, PROP_HANDLE_TYPE, - "handle-type"); - g_object_class_override_property (object_class, PROP_HANDLE, - "handle"); - - param_spec = g_param_spec_object ("connection", "GabbleConnection object", - "Gabble connection object that owns this " - "room list channel object.", - GABBLE_TYPE_CONNECTION, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | - G_PARAM_STATIC_NICK | - G_PARAM_STATIC_BLURB); - g_object_class_install_property (object_class, PROP_CONNECTION, param_spec); - - param_spec = g_param_spec_string ("conference-server", - "Name of conference server to use", - "Name of the XMPP conference server " - "on which to list rooms", - "", - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_WRITABLE | - G_PARAM_STATIC_NAME | - G_PARAM_STATIC_BLURB); - g_object_class_install_property (object_class, PROP_CONFERENCE_SERVER, - param_spec); -} - -static void stop_listing (GabbleRoomlistChannel *self); - -void -gabble_roomlist_channel_dispose (GObject *object) -{ - GabbleRoomlistChannel *self = GABBLE_ROOMLIST_CHANNEL (object); - GabbleRoomlistChannelPrivate *priv = - GABBLE_ROOMLIST_CHANNEL_GET_PRIVATE (self); - - if (priv->dispose_has_run) - return; - - priv->dispose_has_run = TRUE; - - stop_listing (self); - - if (!priv->closed) - { - tp_svc_channel_emit_closed ((TpSvcChannel *)object); - priv->closed = TRUE; - } - - g_assert (priv->pending_room_signals != NULL); - g_assert (priv->pending_room_signals->len == 0); - g_ptr_array_free (priv->pending_room_signals, TRUE); - priv->pending_room_signals = NULL; - - if (G_OBJECT_CLASS (gabble_roomlist_channel_parent_class)->dispose) - G_OBJECT_CLASS (gabble_roomlist_channel_parent_class)->dispose (object); -} - -void -gabble_roomlist_channel_finalize (GObject *object) -{ - GabbleRoomlistChannel *self = GABBLE_ROOMLIST_CHANNEL (object); - GabbleRoomlistChannelPrivate *priv = - GABBLE_ROOMLIST_CHANNEL_GET_PRIVATE (self); - - /* free any data held directly by the object here */ - - g_free (priv->object_path); - g_free (priv->conference_server); - - if (priv->signalled_rooms != NULL) - tp_handle_set_destroy (priv->signalled_rooms); - - G_OBJECT_CLASS (gabble_roomlist_channel_parent_class)->finalize (object); -} - -GabbleRoomlistChannel * -_gabble_roomlist_channel_new (GabbleConnection *conn, - const gchar *object_path, - const gchar *conference_server) -{ - g_return_val_if_fail (GABBLE_IS_CONNECTION (conn), NULL); - g_return_val_if_fail (object_path != NULL, NULL); - g_return_val_if_fail (conference_server != NULL, NULL); - - return GABBLE_ROOMLIST_CHANNEL ( - g_object_new (GABBLE_TYPE_ROOMLIST_CHANNEL, - "connection", conn, - "object-path", object_path, - "conference-server", conference_server, NULL)); -} - -static gboolean -emit_room_signal (gpointer data) -{ - GabbleRoomlistChannel *chan = data; - GabbleRoomlistChannelPrivate *priv = - GABBLE_ROOMLIST_CHANNEL_GET_PRIVATE (chan); - - if (!priv->listing) - return FALSE; - - if (priv->pending_room_signals->len == 0) - return TRUE; - - tp_svc_channel_type_room_list_emit_got_rooms ( - (TpSvcChannelTypeRoomList *)chan, priv->pending_room_signals); - - while (priv->pending_room_signals->len != 0) - { - gpointer boxed = g_ptr_array_index (priv->pending_room_signals, 0); - g_boxed_free (TP_TYPE_ROOM_STRUCT, boxed); - g_ptr_array_remove_index_fast (priv->pending_room_signals, 0); - } - - return TRUE; -} - -static void -room_info_cb (gpointer pipeline, GabbleDiscoItem *item, gpointer user_data) -{ - GabbleRoomlistChannel *chan = user_data; - GabbleRoomlistChannelPrivate *priv; - TpHandleRepoIface *room_handles; - const char *jid, *category, *type, *var, *name; - TpHandle handle; - GHashTable *keys; - GValue room = {0,}; - GValue *tmp; - gpointer k, v; - - #define INSERT_KEY(hash, name, type, type2, value) \ - do {\ - tmp = g_slice_new0 (GValue); \ - g_value_init (tmp, (type)); \ - g_value_set_##type2 (tmp, (value)); \ - g_hash_table_insert (hash, (name), tmp); \ - } while (0) - - g_assert (GABBLE_IS_ROOMLIST_CHANNEL (chan)); - priv = GABBLE_ROOMLIST_CHANNEL_GET_PRIVATE (chan); - room_handles = tp_base_connection_get_handles ( - (TpBaseConnection *)priv->conn, TP_HANDLE_TYPE_ROOM); - - jid = item->jid; - name = item->name; - category = item->category; - type = item->type; - - if (0 != strcmp (category, "conference") || - 0 != strcmp (type, "text")) - return; - - if (!g_hash_table_lookup_extended (item->features, - "http://jabber.org/protocol/muc", &k, &v)) - { - /* not muc */ - return; - } - - handle = tp_handle_ensure (room_handles, jid, NULL, NULL); - if (handle == 0) - { - DEBUG ("ignoring listed room with invalid JID '%s'", jid); - return; - } - - DEBUG ("got room identity, name=%s, category=%s, type=%s", name, - category, type); - - keys = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, - (GDestroyNotify) tp_g_value_slice_free); - - INSERT_KEY (keys, "name", G_TYPE_STRING, string, name); - - if (g_hash_table_lookup_extended (item->features, "muc_membersonly", &k, &v)) - INSERT_KEY (keys, "invite-only", G_TYPE_BOOLEAN, boolean, TRUE); - if (g_hash_table_lookup_extended (item->features, "muc_open", &k, &v)) - INSERT_KEY (keys, "invite-only", G_TYPE_BOOLEAN, boolean, FALSE); - if (g_hash_table_lookup_extended (item->features, - "muc_passwordprotected", &k, &v)) - INSERT_KEY (keys, "password", G_TYPE_BOOLEAN, boolean, TRUE); - if (g_hash_table_lookup_extended (item->features, "muc_unsecure", &k, &v)) - INSERT_KEY (keys, "password", G_TYPE_BOOLEAN, boolean, FALSE); - if (g_hash_table_lookup_extended (item->features, "muc_unsecured", &k, &v)) - INSERT_KEY (keys, "password", G_TYPE_BOOLEAN, boolean, FALSE); - if (g_hash_table_lookup_extended (item->features, "muc_hidden", &k, &v)) - INSERT_KEY (keys, "hidden", G_TYPE_BOOLEAN, boolean, TRUE); - if (g_hash_table_lookup_extended (item->features, "muc_public", &k, &v)) - INSERT_KEY (keys, "hidden", G_TYPE_BOOLEAN, boolean, FALSE); - if (g_hash_table_lookup_extended (item->features, "muc_membersonly", &k, &v)) - INSERT_KEY (keys, "members-only", G_TYPE_BOOLEAN, boolean, TRUE); - if (g_hash_table_lookup_extended (item->features, "muc_open", &k, &v)) - INSERT_KEY (keys, "members-only", G_TYPE_BOOLEAN, boolean, FALSE); - if (g_hash_table_lookup_extended (item->features, "muc_moderated", &k, &v)) - INSERT_KEY (keys, "moderated", G_TYPE_BOOLEAN, boolean, TRUE); - if (g_hash_table_lookup_extended (item->features, "muc_unmoderated", &k, &v)) - INSERT_KEY (keys, "moderated", G_TYPE_BOOLEAN, boolean, FALSE); - if (g_hash_table_lookup_extended (item->features, - "muc_nonanonymous", &k, &v)) - INSERT_KEY (keys, "anonymous", G_TYPE_BOOLEAN, boolean, FALSE); - if (g_hash_table_lookup_extended (item->features, "muc_anonymous", &k, &v)) - INSERT_KEY (keys, "anonymous", G_TYPE_BOOLEAN, boolean, TRUE); - if (g_hash_table_lookup_extended (item->features, - "muc_semianonymous", &k, &v)) - INSERT_KEY (keys, "anonymous", G_TYPE_BOOLEAN, boolean, FALSE); - if (g_hash_table_lookup_extended (item->features, "muc_persistent", &k, &v)) - INSERT_KEY (keys, "persistent", G_TYPE_BOOLEAN, boolean, TRUE); - if (g_hash_table_lookup_extended (item->features, "muc_temporary", &k, &v)) - INSERT_KEY (keys, "persistent", G_TYPE_BOOLEAN, boolean, FALSE); - - var = g_hash_table_lookup (item->features, "muc#roominfo_description"); - if (var != NULL) - INSERT_KEY (keys, "description", G_TYPE_STRING, string, var); - - var = g_hash_table_lookup (item->features, "muc#roominfo_occupants"); - if (var != NULL) - INSERT_KEY (keys, "members", G_TYPE_UINT, uint, - (guint) g_ascii_strtoull (var, NULL, 10)); - - var = g_hash_table_lookup (item->features, "muc#roominfo_lang"); - if (var != NULL) - INSERT_KEY (keys, "language", G_TYPE_STRING, string, var); - - /* transfer the room handle ref to signalled_rooms */ - tp_handle_set_add (priv->signalled_rooms, handle); - tp_handle_unref (room_handles, handle); - - g_value_init (&room, TP_TYPE_ROOM_STRUCT); - g_value_take_boxed (&room, - dbus_g_type_specialized_construct (TP_TYPE_ROOM_STRUCT)); - - dbus_g_type_struct_set (&room, - 0, handle, - 1, "org.freedesktop.Telepathy.Channel.Type.Text", - 2, keys, - G_MAXUINT); - - DEBUG ("adding new room signal data to pending: %s", jid); - g_ptr_array_add (priv->pending_room_signals, g_value_get_boxed (&room)); - g_hash_table_destroy (keys); -} - -static void -rooms_end_cb (gpointer data, gpointer user_data) -{ - GabbleRoomlistChannel *chan = user_data; - GabbleRoomlistChannelPrivate *priv = - GABBLE_ROOMLIST_CHANNEL_GET_PRIVATE (chan); - - emit_room_signal (chan); - - priv->listing = FALSE; - tp_svc_channel_type_room_list_emit_listing_rooms ( - (TpSvcChannelTypeRoomList *)chan, FALSE); - - g_source_remove (priv->timer_source_id); - priv->timer_source_id = 0; -} - -static void -stop_listing (GabbleRoomlistChannel *self) -{ - GabbleRoomlistChannelPrivate *priv = - GABBLE_ROOMLIST_CHANNEL_GET_PRIVATE (self); - - if (priv->listing) - { - emit_room_signal (self); - priv->listing = FALSE; - tp_svc_channel_type_room_list_emit_listing_rooms ( - (TpSvcChannelTypeRoomList *)self, FALSE); - } - - if (priv->disco_pipeline != NULL) - { - gabble_disco_pipeline_destroy (priv->disco_pipeline); - priv->disco_pipeline = NULL; - } - - if (priv->timer_source_id) - { - g_source_remove (priv->timer_source_id); - priv->timer_source_id = 0; - } - - g_assert (priv->pending_room_signals->len == 0); -} - - -/************************* D-Bus Method definitions **************************/ - -/** - * gabble_roomlist_channel_close - * - * Implements D-Bus method Close - * on interface org.freedesktop.Telepathy.Channel - */ -static void -gabble_roomlist_channel_close (TpSvcChannel *iface, - DBusGMethodInvocation *context) -{ - GabbleRoomlistChannel *self = GABBLE_ROOMLIST_CHANNEL (iface); - g_assert (GABBLE_IS_ROOMLIST_CHANNEL (self)); - - DEBUG ("called on %p", self); - - g_object_run_dispose (G_OBJECT (self)); - - tp_svc_channel_return_from_close (context); -} - - -/** - * gabble_roomlist_channel_get_channel_type - * - * Implements D-Bus method GetChannelType - * on interface org.freedesktop.Telepathy.Channel - */ -static void -gabble_roomlist_channel_get_channel_type (TpSvcChannel *iface, - DBusGMethodInvocation *context) -{ - tp_svc_channel_return_from_get_channel_type (context, - TP_IFACE_CHANNEL_TYPE_ROOM_LIST); -} - - -/** - * gabble_roomlist_channel_get_handle - * - * Implements D-Bus method GetHandle - * on interface org.freedesktop.Telepathy.Channel - */ -static void -gabble_roomlist_channel_get_handle (TpSvcChannel *self, - DBusGMethodInvocation *context) -{ - tp_svc_channel_return_from_get_handle (context, 0, 0); -} - - -/** - * gabble_roomlist_channel_get_interfaces - * - * Implements D-Bus method GetInterfaces - * on interface org.freedesktop.Telepathy.Channel - */ -static void -gabble_roomlist_channel_get_interfaces (TpSvcChannel *iface, - DBusGMethodInvocation *context) -{ - const char *interfaces[] = { NULL }; - - tp_svc_channel_return_from_get_interfaces (context, interfaces); -} - - -/** - * gabble_roomlist_channel_get_listing_rooms - * - * Implements D-Bus method GetListingRooms - * on interface org.freedesktop.Telepathy.Channel.Type.RoomList - * - * @error: Used to return a pointer to a GError detailing any error - * that occurred, D-Bus will throw the error only if this - * function returns FALSE. - * - * Returns: TRUE if successful, FALSE if an error was thrown. - */ -static void -gabble_roomlist_channel_get_listing_rooms (TpSvcChannelTypeRoomList *iface, - DBusGMethodInvocation *context) -{ - GabbleRoomlistChannel *self = GABBLE_ROOMLIST_CHANNEL (iface); - GabbleRoomlistChannelPrivate *priv; - - g_assert (GABBLE_IS_ROOMLIST_CHANNEL (self)); - - priv = GABBLE_ROOMLIST_CHANNEL_GET_PRIVATE (self); - tp_svc_channel_type_room_list_return_from_get_listing_rooms ( - context, priv->listing); -} - - -/** - * gabble_roomlist_channel_list_rooms - * - * Implements D-Bus method ListRooms - * on interface org.freedesktop.Telepathy.Channel.Type.RoomList - * - * @error: Used to return a pointer to a GError detailing any error - * that occurred, D-Bus will throw the error only if this - * function returns FALSE. - * - * Returns: TRUE if successful, FALSE if an error was thrown. - */ -static void -gabble_roomlist_channel_list_rooms (TpSvcChannelTypeRoomList *iface, - DBusGMethodInvocation *context) -{ - GabbleRoomlistChannel *self = GABBLE_ROOMLIST_CHANNEL (iface); - GabbleRoomlistChannelPrivate *priv; - - g_assert (GABBLE_IS_ROOMLIST_CHANNEL (self)); - - priv = GABBLE_ROOMLIST_CHANNEL_GET_PRIVATE (self); - - priv->listing = TRUE; - tp_svc_channel_type_room_list_emit_listing_rooms (iface, TRUE); - - if (priv->disco_pipeline == NULL) - priv->disco_pipeline = gabble_disco_pipeline_init (priv->conn->disco, - room_info_cb, rooms_end_cb, self); - - gabble_disco_pipeline_run (priv->disco_pipeline, priv->conference_server); - - priv->timer_source_id = g_timeout_add (ROOM_SIGNAL_INTERVAL, - emit_room_signal, self); - - tp_svc_channel_type_room_list_return_from_list_rooms (context); -} - -/** - * gabble_roomlist_channel_stop_listing - * - * Implements D-Bus method StopListing - * on interface org.freedesktop.Telepathy.Channel.Type.RoomList - */ -static void -gabble_roomlist_channel_stop_listing (TpSvcChannelTypeRoomList *iface, - DBusGMethodInvocation *context) -{ - GabbleRoomlistChannel *self = GABBLE_ROOMLIST_CHANNEL (iface); - - g_assert (GABBLE_IS_ROOMLIST_CHANNEL (self)); - - stop_listing (self); -} - -static void -channel_iface_init (gpointer g_iface, gpointer iface_data) -{ - TpSvcChannelClass *klass = (TpSvcChannelClass *)g_iface; - -#define IMPLEMENT(x) tp_svc_channel_implement_##x (\ - klass, gabble_roomlist_channel_##x) - IMPLEMENT(close); - IMPLEMENT(get_channel_type); - IMPLEMENT(get_handle); - IMPLEMENT(get_interfaces); -#undef IMPLEMENT -} - -static void -roomlist_iface_init (gpointer g_iface, gpointer iface_data) -{ - TpSvcChannelTypeRoomListClass *klass = - (TpSvcChannelTypeRoomListClass *)g_iface; - -#define IMPLEMENT(x) tp_svc_channel_type_room_list_implement_##x (\ - klass, gabble_roomlist_channel_##x) - IMPLEMENT(get_listing_rooms); - IMPLEMENT(list_rooms); - IMPLEMENT(stop_listing); -#undef IMPLEMENT -} diff --git a/src/gabble-roomlist-channel.h b/src/gabble-roomlist-channel.h deleted file mode 100644 index 8fd5da2b8..000000000 --- a/src/gabble-roomlist-channel.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * gabble-roomlist-channel.h - Header for GabbleRoomlistChannel - * Copyright (C) 2005 Collabora Ltd. - * Copyright (C) 2005 Nokia Corporation - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef __GABBLE_ROOMLIST_CHANNEL_H__ -#define __GABBLE_ROOMLIST_CHANNEL_H__ - -#include <glib-object.h> - -#include "gabble-connection.h" - -G_BEGIN_DECLS - -typedef struct _GabbleRoomlistChannel GabbleRoomlistChannel; -typedef struct _GabbleRoomlistChannelClass GabbleRoomlistChannelClass; - -struct _GabbleRoomlistChannelClass { - GObjectClass parent_class; -}; - -struct _GabbleRoomlistChannel { - GObject parent; - - gpointer priv; -}; - -GType gabble_roomlist_channel_get_type (void); - -/* TYPE MACROS */ -#define GABBLE_TYPE_ROOMLIST_CHANNEL \ - (gabble_roomlist_channel_get_type ()) -#define GABBLE_ROOMLIST_CHANNEL(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_ROOMLIST_CHANNEL,\ - GabbleRoomlistChannel)) -#define GABBLE_ROOMLIST_CHANNEL_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_ROOMLIST_CHANNEL,\ - GabbleRoomlistChannelClass)) -#define GABBLE_IS_ROOMLIST_CHANNEL(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_ROOMLIST_CHANNEL)) -#define GABBLE_IS_ROOMLIST_CHANNEL_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_ROOMLIST_CHANNEL)) -#define GABBLE_ROOMLIST_CHANNEL_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_ROOMLIST_CHANNEL,\ - GabbleRoomlistChannelClass)) - - -GabbleRoomlistChannel *_gabble_roomlist_channel_new (GabbleConnection *conn, - const gchar *object_path, const gchar *conference_server); - - -G_END_DECLS - -#endif /* #ifndef __GABBLE_ROOMLIST_CHANNEL_H__*/ diff --git a/src/gabble-roster-channel.c b/src/gabble-roster-channel.c deleted file mode 100644 index 2f208f8fd..000000000 --- a/src/gabble-roster-channel.c +++ /dev/null @@ -1,641 +0,0 @@ -/* - * gabble-roster-channel.c - Source for GabbleRosterChannel - * Copyright (C) 2005, 2006 Collabora Ltd. - * Copyright (C) 2005 Nokia Corporation - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "gabble-roster-channel.h" - -#include <dbus/dbus-glib.h> -#include <stdio.h> -#include <stdlib.h> - -#define DEBUG_FLAG GABBLE_DEBUG_ROSTER - -#include "debug.h" -#include "gabble-connection.h" -#include <telepathy-glib/group-mixin.h> -#include "roster.h" -#include <telepathy-glib/dbus.h> -#include <telepathy-glib/errors.h> -#include <telepathy-glib/interfaces.h> -#include <telepathy-glib/channel-iface.h> -#include <telepathy-glib/svc-channel.h> -#include "util.h" - -static void channel_iface_init (gpointer, gpointer); - -G_DEFINE_TYPE_WITH_CODE (GabbleRosterChannel, gabble_roster_channel, - G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL, channel_iface_init); - G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_INTERFACE_GROUP, - tp_group_mixin_iface_init); - G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CHANNEL_TYPE_CONTACT_LIST, NULL); - G_IMPLEMENT_INTERFACE (TP_TYPE_CHANNEL_IFACE, NULL)); - -/* properties */ -enum -{ - PROP_OBJECT_PATH = 1, - PROP_CHANNEL_TYPE, - PROP_HANDLE_TYPE, - PROP_HANDLE, - PROP_CONNECTION, - LAST_PROPERTY -}; - -/* private structure */ -typedef struct _GabbleRosterChannelPrivate GabbleRosterChannelPrivate; - -struct _GabbleRosterChannelPrivate -{ - GabbleConnection *conn; - char *object_path; - TpHandle handle; - guint handle_type; - - gboolean dispose_has_run; - gboolean closed; -}; - -#define GABBLE_ROSTER_CHANNEL_GET_PRIVATE(obj) \ - ((GabbleRosterChannelPrivate *)obj->priv) - -static void -gabble_roster_channel_init (GabbleRosterChannel *self) -{ - GabbleRosterChannelPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self, - GABBLE_TYPE_ROSTER_CHANNEL, GabbleRosterChannelPrivate); - - self->priv = priv; - - /* allocate any data required by the object here */ -} - -static GObject * -gabble_roster_channel_constructor (GType type, guint n_props, - GObjectConstructParam *props) -{ - GObject *obj; - GabbleRosterChannelPrivate *priv; - DBusGConnection *bus; - TpBaseConnection *conn; - TpHandle self_handle; - guint handle_type; - TpHandleRepoIface *handle_repo, *contact_repo; - - obj = G_OBJECT_CLASS (gabble_roster_channel_parent_class)-> - constructor (type, n_props, props); - priv = GABBLE_ROSTER_CHANNEL_GET_PRIVATE (GABBLE_ROSTER_CHANNEL (obj)); - conn = (TpBaseConnection *)priv->conn; - handle_type = priv->handle_type; - handle_repo = tp_base_connection_get_handles (conn, handle_type); - contact_repo = tp_base_connection_get_handles (conn, TP_HANDLE_TYPE_CONTACT); - self_handle = conn->self_handle; - - /* register object on the bus */ - bus = tp_get_bus (); - dbus_g_connection_register_g_object (bus, priv->object_path, obj); - - g_assert (handle_type == TP_HANDLE_TYPE_GROUP - || handle_type == TP_HANDLE_TYPE_LIST); - - /* ref our list handle */ - tp_handle_ref (handle_repo, priv->handle); - - /* initialize group mixin */ - tp_group_mixin_init (obj, - G_STRUCT_OFFSET (GabbleRosterChannel, group), - contact_repo, - self_handle); - - if (handle_type == TP_HANDLE_TYPE_GROUP) - { - tp_group_mixin_change_flags (obj, - TP_CHANNEL_GROUP_FLAG_CAN_ADD | - TP_CHANNEL_GROUP_FLAG_CAN_REMOVE, - 0); - } - else if (handle_type != TP_HANDLE_TYPE_LIST) - { - g_assert_not_reached (); - } - /* magic contact lists from here down... */ - else if (GABBLE_LIST_HANDLE_PUBLISH == priv->handle) - { - tp_group_mixin_change_flags (obj, - TP_CHANNEL_GROUP_FLAG_CAN_REMOVE | - TP_CHANNEL_GROUP_FLAG_MESSAGE_ACCEPT | - TP_CHANNEL_GROUP_FLAG_MESSAGE_REMOVE, - 0); - } - else if (GABBLE_LIST_HANDLE_SUBSCRIBE == priv->handle) - { - tp_group_mixin_change_flags (obj, - TP_CHANNEL_GROUP_FLAG_CAN_ADD | - TP_CHANNEL_GROUP_FLAG_CAN_REMOVE | - TP_CHANNEL_GROUP_FLAG_CAN_RESCIND | - TP_CHANNEL_GROUP_FLAG_MESSAGE_ADD | - TP_CHANNEL_GROUP_FLAG_MESSAGE_REMOVE | - TP_CHANNEL_GROUP_FLAG_MESSAGE_RESCIND, - 0); - } - else if (GABBLE_LIST_HANDLE_KNOWN == priv->handle) - { - tp_group_mixin_change_flags (obj, - TP_CHANNEL_GROUP_FLAG_CAN_REMOVE, - 0); - } - else if (GABBLE_LIST_HANDLE_DENY == priv->handle) - { - tp_group_mixin_change_flags (obj, - TP_CHANNEL_GROUP_FLAG_CAN_ADD | - TP_CHANNEL_GROUP_FLAG_CAN_REMOVE, - 0); - } - else - { - g_assert_not_reached (); - } - - return obj; -} - -static void -gabble_roster_channel_get_property (GObject *object, - guint property_id, - GValue *value, - GParamSpec *pspec) -{ - GabbleRosterChannel *chan = GABBLE_ROSTER_CHANNEL (object); - GabbleRosterChannelPrivate *priv = GABBLE_ROSTER_CHANNEL_GET_PRIVATE (chan); - - switch (property_id) { - case PROP_OBJECT_PATH: - g_value_set_string (value, priv->object_path); - break; - case PROP_CHANNEL_TYPE: - g_value_set_static_string (value, TP_IFACE_CHANNEL_TYPE_CONTACT_LIST); - break; - case PROP_HANDLE_TYPE: - g_value_set_uint (value, priv->handle_type); - break; - case PROP_HANDLE: - g_value_set_uint (value, priv->handle); - break; - case PROP_CONNECTION: - g_value_set_object (value, priv->conn); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } -} - -static void -gabble_roster_channel_set_property (GObject *object, - guint property_id, - const GValue *value, - GParamSpec *pspec) -{ - GabbleRosterChannel *chan = GABBLE_ROSTER_CHANNEL (object); - GabbleRosterChannelPrivate *priv = GABBLE_ROSTER_CHANNEL_GET_PRIVATE (chan); - - switch (property_id) { - case PROP_OBJECT_PATH: - g_free (priv->object_path); - priv->object_path = g_value_dup_string (value); - break; - case PROP_HANDLE_TYPE: - priv->handle_type = g_value_get_uint (value); - break; - case PROP_HANDLE: - priv->handle = g_value_get_uint (value); - break; - case PROP_CONNECTION: - priv->conn = g_value_get_object (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } -} - -static void gabble_roster_channel_dispose (GObject *object); -static void gabble_roster_channel_finalize (GObject *object); - -static gboolean _gabble_roster_channel_add_member_cb (GObject *obj, - TpHandle handle, const gchar *message, GError **error); -static gboolean _gabble_roster_channel_remove_member_cb (GObject *obj, - TpHandle handle, const gchar *message, GError **error); - -static void -gabble_roster_channel_class_init (GabbleRosterChannelClass *gabble_roster_channel_class) -{ - GObjectClass *object_class = G_OBJECT_CLASS (gabble_roster_channel_class); - GParamSpec *param_spec; - - g_type_class_add_private (gabble_roster_channel_class, - sizeof (GabbleRosterChannelPrivate)); - - object_class->constructor = gabble_roster_channel_constructor; - - object_class->get_property = gabble_roster_channel_get_property; - object_class->set_property = gabble_roster_channel_set_property; - - object_class->dispose = gabble_roster_channel_dispose; - object_class->finalize = gabble_roster_channel_finalize; - - param_spec = g_param_spec_object ("connection", "GabbleConnection object", - "Gabble connection object that owns this " - "Roster channel object.", - GABBLE_TYPE_CONNECTION, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | - G_PARAM_STATIC_NICK | - G_PARAM_STATIC_BLURB); - g_object_class_install_property (object_class, PROP_CONNECTION, param_spec); - - g_object_class_override_property (object_class, PROP_OBJECT_PATH, - "object-path"); - g_object_class_override_property (object_class, PROP_CHANNEL_TYPE, - "channel-type"); - g_object_class_override_property (object_class, PROP_HANDLE_TYPE, - "handle-type"); - g_object_class_override_property (object_class, PROP_HANDLE, "handle"); - - tp_group_mixin_class_init (object_class, - G_STRUCT_OFFSET (GabbleRosterChannelClass, group_class), - _gabble_roster_channel_add_member_cb, - _gabble_roster_channel_remove_member_cb); -} - -void -gabble_roster_channel_dispose (GObject *object) -{ - GabbleRosterChannel *self = GABBLE_ROSTER_CHANNEL (object); - GabbleRosterChannelPrivate *priv = GABBLE_ROSTER_CHANNEL_GET_PRIVATE (self); - - if (priv->dispose_has_run) - return; - - priv->dispose_has_run = TRUE; - - if (!priv->closed) - tp_svc_channel_emit_closed ((TpSvcChannel *)object); - - /* release any references held by the object here */ - - if (G_OBJECT_CLASS (gabble_roster_channel_parent_class)->dispose) - G_OBJECT_CLASS (gabble_roster_channel_parent_class)->dispose (object); -} - -void -gabble_roster_channel_finalize (GObject *object) -{ - GabbleRosterChannel *self = GABBLE_ROSTER_CHANNEL (object); - GabbleRosterChannelPrivate *priv = GABBLE_ROSTER_CHANNEL_GET_PRIVATE (self); - TpBaseConnection *conn = (TpBaseConnection *)priv->conn; - TpHandleRepoIface *handle_repo = tp_base_connection_get_handles (conn, - priv->handle_type); - - /* free any data held directly by the object here */ - - g_free (priv->object_path); - - tp_handle_unref (handle_repo, priv->handle); - - tp_group_mixin_finalize (object); - - G_OBJECT_CLASS (gabble_roster_channel_parent_class)->finalize (object); -} - - -static gboolean -_gabble_roster_channel_send_presence (GabbleRosterChannel *chan, - LmMessageSubType sub_type, - TpHandle handle, - const gchar *status, - GError **error) -{ - GabbleRosterChannelPrivate *priv; - TpBaseConnection *conn; - TpHandleRepoIface *repo; - const char *contact; - LmMessage *message; - gboolean result; - - priv = GABBLE_ROSTER_CHANNEL_GET_PRIVATE (chan); - conn = (TpBaseConnection *)priv->conn; - repo = tp_base_connection_get_handles (conn, TP_HANDLE_TYPE_CONTACT); - contact = tp_handle_inspect (repo, handle); - - message = lm_message_new_with_sub_type (contact, - LM_MESSAGE_TYPE_PRESENCE, - sub_type); - - if (LM_MESSAGE_SUB_TYPE_SUBSCRIBE == sub_type) - lm_message_node_add_own_nick (message->node, priv->conn); - - if (status != NULL && status[0] != '\0') - lm_message_node_add_child (message->node, "status", status); - - result = _gabble_connection_send (priv->conn, message, error); - - lm_message_unref (message); - - return result; -} - - -/** - * _gabble_roster_channel_add_member_cb - * - * Called by the group mixin to add one member. - */ -static gboolean -_gabble_roster_channel_add_member_cb (GObject *obj, - TpHandle handle, - const gchar *message, - GError **error) -{ - GabbleRosterChannelPrivate *priv; - gboolean ret = FALSE; -#ifdef ENABLE_DEBUG - TpHandleRepoIface *handle_repo, *contact_repo; -#endif - - priv = GABBLE_ROSTER_CHANNEL_GET_PRIVATE (GABBLE_ROSTER_CHANNEL (obj)); -#ifdef ENABLE_DEBUG - handle_repo = tp_base_connection_get_handles ((TpBaseConnection *)priv->conn, - priv->handle_type); - contact_repo = tp_base_connection_get_handles ( - (TpBaseConnection *)priv->conn, TP_HANDLE_TYPE_CONTACT); -#endif - - DEBUG ("called on %s with handle %u (%s) \"%s\"", - tp_handle_inspect (handle_repo, priv->handle), handle, - tp_handle_inspect (contact_repo, handle), message); - - if (TP_HANDLE_TYPE_GROUP == priv->handle_type) - { - ret = gabble_roster_handle_add_to_group (priv->conn->roster, - handle, priv->handle, error); - } - else if (TP_HANDLE_TYPE_LIST != priv->handle_type) - { - g_assert_not_reached (); - return FALSE; - } - /* "magic" contact lists, from here down... */ - /* publish list */ - else if (GABBLE_LIST_HANDLE_PUBLISH == priv->handle) - { - /* send <presence type="subscribed"> */ - ret = _gabble_roster_channel_send_presence (GABBLE_ROSTER_CHANNEL (obj), - LM_MESSAGE_SUB_TYPE_SUBSCRIBED, handle, message, error); - } - /* subscribe list */ - else if (GABBLE_LIST_HANDLE_SUBSCRIBE == priv->handle) - { - /* add item to the roster (GTalk depends on this, clearing the H flag) */ - gabble_roster_handle_add (priv->conn->roster, handle, NULL); - - /* send <presence type="subscribe"> */ - ret = _gabble_roster_channel_send_presence (GABBLE_ROSTER_CHANNEL (obj), - LM_MESSAGE_SUB_TYPE_SUBSCRIBE, handle, message, error); - } - /* deny list */ - else if (GABBLE_LIST_HANDLE_DENY == priv->handle) - { - /* block contact */ - ret = gabble_roster_handle_set_blocked (priv->conn->roster, handle, TRUE, - error); - } - else - { - g_assert_not_reached (); - } - - return ret; -} - - -/** - * _gabble_roster_channel_remove_member_cb - * - * Called by the group mixin to remove one member. - */ -static gboolean -_gabble_roster_channel_remove_member_cb (GObject *obj, - TpHandle handle, - const gchar *message, - GError **error) -{ - GabbleRosterChannelPrivate *priv; -#ifdef ENABLE_DEBUG - TpBaseConnection *conn; - TpHandleRepoIface *handle_repo, *contact_repo; -#endif - gboolean ret = FALSE; - - priv = GABBLE_ROSTER_CHANNEL_GET_PRIVATE (GABBLE_ROSTER_CHANNEL (obj)); -#ifdef ENABLE_DEBUG - conn = (TpBaseConnection *)priv->conn; - handle_repo = tp_base_connection_get_handles (conn, priv->handle_type); - contact_repo = tp_base_connection_get_handles (conn, TP_HANDLE_TYPE_CONTACT); -#endif - - DEBUG ("called on %s with handle %u (%s) \"%s\"", - tp_handle_inspect (handle_repo, priv->handle), handle, - tp_handle_inspect (contact_repo, handle), message); - - if (TP_HANDLE_TYPE_GROUP == priv->handle_type) - { - ret = gabble_roster_handle_remove_from_group (priv->conn->roster, - handle, priv->handle, error); - } - else if (TP_HANDLE_TYPE_LIST != priv->handle_type) - { - g_assert_not_reached (); - return FALSE; - } - /* "magic" contact lists, from here down... */ - /* publish list */ - else if (GABBLE_LIST_HANDLE_PUBLISH == priv->handle) - { - /* send <presence type="unsubscribed"> */ - ret = _gabble_roster_channel_send_presence (GABBLE_ROSTER_CHANNEL (obj), - LM_MESSAGE_SUB_TYPE_UNSUBSCRIBED, handle, message, error); - - /* remove it from local_pending here, because roster callback doesn't - know if it can (subscription='none' is used both during request and - when it's rejected) */ - if (tp_handle_set_is_member (GABBLE_ROSTER_CHANNEL (obj)->group.local_pending, handle)) - { - TpIntSet *rem = tp_intset_new (); - - tp_intset_add (rem, handle); - tp_group_mixin_change_members (obj, "", NULL, rem, NULL, NULL, - 0, 0); - - tp_intset_destroy (rem); - } - } - /* subscribe list */ - else if (GABBLE_LIST_HANDLE_SUBSCRIBE == priv->handle) - { - /* send <presence type="unsubscribe"> */ - ret = _gabble_roster_channel_send_presence (GABBLE_ROSTER_CHANNEL (obj), - LM_MESSAGE_SUB_TYPE_UNSUBSCRIBE, handle, message, error); - } - /* known list */ - else if (GABBLE_LIST_HANDLE_KNOWN == priv->handle) - { - /* send roster subscription=remove IQ */ - ret = gabble_roster_handle_remove (priv->conn->roster, handle, error); - } - /* deny list */ - else if (GABBLE_LIST_HANDLE_DENY == priv->handle) - { - /* unblock contact */ - ret = gabble_roster_handle_set_blocked (priv->conn->roster, handle, - FALSE, error); - } - else - { - g_assert_not_reached (); - } - - return ret; -} - - -/** - * gabble_roster_channel_close - * - * Implements D-Bus method Close - * on interface org.freedesktop.Telepathy.Channel - */ -static void -gabble_roster_channel_close (TpSvcChannel *iface, - DBusGMethodInvocation *context) -{ - GabbleRosterChannel *self = GABBLE_ROSTER_CHANNEL (iface); - GabbleRosterChannelPrivate *priv; - - g_assert (GABBLE_IS_ROSTER_CHANNEL (self)); - - priv = GABBLE_ROSTER_CHANNEL_GET_PRIVATE (self); - - if (priv->handle_type == TP_HANDLE_TYPE_LIST) - { - GError e = { TP_ERRORS, TP_ERROR_NOT_IMPLEMENTED, - "you may not close contact list channels" }; - - dbus_g_method_return_error (context, &e); - } - else /* TP_HANDLE_TYPE_GROUP */ - { - if (tp_handle_set_size (self->group.members) == 0) - { - /* deleting groups isn't a concept that exists on XMPP, - * so just close the channel */ - - priv->closed = TRUE; - tp_svc_channel_emit_closed ((TpSvcChannel *)self); - tp_svc_channel_return_from_close (context); - return; - } - - else - { - GError e = { TP_ERRORS, TP_ERROR_NOT_AVAILABLE, - "you may not close this group, because it's not empty" }; - - dbus_g_method_return_error (context, &e); - } - } -} - - -/** - * gabble_roster_channel_get_channel_type - * - * Implements D-Bus method GetChannelType - * on interface org.freedesktop.Telepathy.Channel - */ -static void -gabble_roster_channel_get_channel_type (TpSvcChannel *iface, - DBusGMethodInvocation *context) -{ - tp_svc_channel_return_from_get_channel_type (context, - TP_IFACE_CHANNEL_TYPE_CONTACT_LIST); -} - - -/** - * gabble_roster_channel_get_handle - * - * Implements D-Bus method GetHandle - * on interface org.freedesktop.Telepathy.Channel - */ -static void -gabble_roster_channel_get_handle (TpSvcChannel *iface, - DBusGMethodInvocation *context) -{ - GabbleRosterChannel *self = GABBLE_ROSTER_CHANNEL (iface); - GabbleRosterChannelPrivate *priv; - - g_assert (GABBLE_IS_ROSTER_CHANNEL (self)); - - priv = GABBLE_ROSTER_CHANNEL_GET_PRIVATE (self); - - tp_svc_channel_return_from_get_handle (context, priv->handle_type, - priv->handle); -} - - -/** - * gabble_roster_channel_get_interfaces - * - * Implements D-Bus method GetInterfaces - * on interface org.freedesktop.Telepathy.Channel - */ -static void -gabble_roster_channel_get_interfaces (TpSvcChannel *self, - DBusGMethodInvocation *context) -{ - const char *interfaces[] = { TP_IFACE_CHANNEL_INTERFACE_GROUP, NULL }; - - tp_svc_channel_return_from_get_interfaces (context, interfaces); -} - - -static void -channel_iface_init (gpointer g_iface, gpointer iface_data) -{ - TpSvcChannelClass *klass = (TpSvcChannelClass *)g_iface; - -#define IMPLEMENT(x) tp_svc_channel_implement_##x (\ - klass, gabble_roster_channel_##x) - IMPLEMENT(close); - IMPLEMENT(get_channel_type); - IMPLEMENT(get_handle); - IMPLEMENT(get_interfaces); -#undef IMPLEMENT -} diff --git a/src/gabble-roster-channel.h b/src/gabble-roster-channel.h deleted file mode 100644 index 29f8bd5e3..000000000 --- a/src/gabble-roster-channel.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * gabble-roster-channel.h - Header for GabbleRosterChannel - * Copyright (C) 2005 Collabora Ltd. - * Copyright (C) 2005 Nokia Corporation - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef __GABBLE_ROSTER_CHANNEL_H__ -#define __GABBLE_ROSTER_CHANNEL_H__ - -#include <glib-object.h> -#include <telepathy-glib/group-mixin.h> -#include <telepathy-glib/intset.h> - -#include "gabble-types.h" - -G_BEGIN_DECLS - -typedef struct _GabbleRosterChannelClass GabbleRosterChannelClass; - -struct _GabbleRosterChannelClass { - GObjectClass parent_class; - - TpGroupMixinClass group_class; -}; - -struct _GabbleRosterChannel { - GObject parent; - - TpGroupMixin group; - - gpointer priv; -}; - -GType gabble_roster_channel_get_type (void); - -/* TYPE MACROS */ -#define GABBLE_TYPE_ROSTER_CHANNEL \ - (gabble_roster_channel_get_type ()) -#define GABBLE_ROSTER_CHANNEL(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_ROSTER_CHANNEL,\ - GabbleRosterChannel)) -#define GABBLE_ROSTER_CHANNEL_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_ROSTER_CHANNEL,\ - GabbleRosterChannelClass)) -#define GABBLE_IS_ROSTER_CHANNEL(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_ROSTER_CHANNEL)) -#define GABBLE_IS_ROSTER_CHANNEL_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_ROSTER_CHANNEL)) -#define GABBLE_ROSTER_CHANNEL_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_ROSTER_CHANNEL,\ - GabbleRosterChannelClass)) - -G_END_DECLS - -#endif /* #ifndef __GABBLE_ROSTER_CHANNEL_H__*/ diff --git a/src/gabble-signals-marshal.list b/src/gabble-signals-marshal.list deleted file mode 100644 index 56f418a08..000000000 --- a/src/gabble-signals-marshal.list +++ /dev/null @@ -1,7 +0,0 @@ -VOID:BOOLEAN,INT,STRING -VOID:STRING,BOXED -VOID:STRING,STRING -VOID:STRING,UINT,UINT,UINT -VOID:UINT,STRING -VOID:UINT,UINT -VOID:UINT,UINT,UINT diff --git a/src/gabble-types.h b/src/gabble-types.h deleted file mode 100644 index b9ee55309..000000000 --- a/src/gabble-types.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * gabble-types.h - Header for Gabble type definitions - * - * Copyright (C) 2006 Collabora Ltd. - * Copyright (C) 2006 Nokia Corporation - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef __GABBLE_TYPES_H__ -#define __GABBLE_TYPES_H__ - -#include <telepathy-glib/handle.h> - -G_BEGIN_DECLS - -typedef struct _GabbleConnection GabbleConnection; -typedef struct _GabbleDisco GabbleDisco; -typedef struct _GabblePresence GabblePresence; -typedef struct _GabblePresenceCache GabblePresenceCache; -typedef struct _GabbleRoster GabbleRoster; -typedef struct _GabbleRosterChannel GabbleRosterChannel; -typedef struct _GabbleVCardManager GabbleVCardManager; - -typedef enum { - INITIATOR_INVALID = -1, - INITIATOR_LOCAL = 0, - INITIATOR_REMOTE, -} JingleInitiator; - -G_END_DECLS - -#endif diff --git a/src/gabble.c b/src/gabble.c deleted file mode 100644 index e690d3dd5..000000000 --- a/src/gabble.c +++ /dev/null @@ -1,50 +0,0 @@ -/* - * gabble.h - entry point and utility functions for telepathy-gabble - * Copyright (C) 2005 Collabora Ltd. - * Copyright (C) 2005 Nokia Corporation - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include <telepathy-glib/debug.h> -#include <telepathy-glib/run.h> -#include "debug.h" -#include "gabble-connection-manager.h" - -static TpBaseConnectionManager * -construct_cm (void) -{ - return (TpBaseConnectionManager *)g_object_new ( - GABBLE_TYPE_CONNECTION_MANAGER, NULL); -} - -int -main (int argc, - char **argv) -{ -#ifdef ENABLE_DEBUG - gabble_debug_set_flags_from_env (); - - /* backwards compatibility */ - if (g_getenv ("GABBLE_PERSIST")) - { - gabble_debug_set_flags (0xffff); - tp_debug_set_all_flags (); - } -#endif - - return tp_run_connection_manager ("telepathy-gabble", VERSION, - construct_cm, argc, argv); -} diff --git a/src/gabble.h b/src/gabble.h deleted file mode 100644 index a426e7c13..000000000 --- a/src/gabble.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * gabble.h - entry point and utility functions for telepathy-gabble - * Copyright (C) 2005 Collabora Ltd. - * Copyright (C) 2005 Nokia Corporation - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef __GABBLE_H__ -#define __GABBLE_H__ - -#include <dbus/dbus-glib.h> - -G_BEGIN_DECLS - -G_END_DECLS - -#endif /* #ifndef __GABBLE_H__*/ - diff --git a/src/im-factory.c b/src/im-factory.c deleted file mode 100644 index e8c16d152..000000000 --- a/src/im-factory.c +++ /dev/null @@ -1,483 +0,0 @@ -/* - * im-factory.c - Source for GabbleImFactory - * Copyright (C) 2006 Collabora Ltd. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "im-factory.h" - -#define DBUS_API_SUBJECT_TO_CHANGE - -#include <stdlib.h> -#include <string.h> -#include <time.h> - -#include <glib.h> - -#include <dbus/dbus-glib.h> -#include <dbus/dbus-glib-lowlevel.h> - -#include <loudmouth/loudmouth.h> - -#define DEBUG_FLAG GABBLE_DEBUG_IM - -#include "debug.h" -#include "disco.h" -#include "gabble-connection.h" -#include "gabble-im-channel.h" -#include <telepathy-glib/interfaces.h> -#include "text-mixin.h" -#include <telepathy-glib/channel-factory-iface.h> - -static void gabble_im_factory_iface_init (gpointer g_iface, - gpointer iface_data); - -G_DEFINE_TYPE_WITH_CODE (GabbleImFactory, gabble_im_factory, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (TP_TYPE_CHANNEL_FACTORY_IFACE, - gabble_im_factory_iface_init)); - -/* properties */ -enum -{ - PROP_CONNECTION = 1, - LAST_PROPERTY -}; - -typedef struct _GabbleImFactoryPrivate GabbleImFactoryPrivate; -struct _GabbleImFactoryPrivate -{ - GabbleConnection *conn; - LmMessageHandler *message_cb; - GHashTable *channels; - - gboolean dispose_has_run; -}; - -#define GABBLE_IM_FACTORY_GET_PRIVATE(o) \ - (G_TYPE_INSTANCE_GET_PRIVATE ((o), GABBLE_TYPE_IM_FACTORY,\ - GabbleImFactoryPrivate)) - -static GObject *gabble_im_factory_constructor (GType type, guint n_props, - GObjectConstructParam *props); - -static void -gabble_im_factory_init (GabbleImFactory *fac) -{ - GabbleImFactoryPrivate *priv = GABBLE_IM_FACTORY_GET_PRIVATE (fac); - - priv->channels = g_hash_table_new_full (g_direct_hash, g_direct_equal, - NULL, g_object_unref); - - priv->message_cb = NULL; - - priv->conn = NULL; - priv->dispose_has_run = FALSE; -} - -static GObject * -gabble_im_factory_constructor (GType type, guint n_props, - GObjectConstructParam *props) -{ - GObject *obj; - /* GabbleImFactoryPrivate *priv; */ - - obj = G_OBJECT_CLASS (gabble_im_factory_parent_class)-> - constructor (type, n_props, props); - /* priv = GABBLE_IM_FACTORY_GET_PRIVATE (obj); */ - - return obj; -} - - -static void -gabble_im_factory_dispose (GObject *object) -{ - GabbleImFactory *fac = GABBLE_IM_FACTORY (object); - GabbleImFactoryPrivate *priv = GABBLE_IM_FACTORY_GET_PRIVATE (fac); - - if (priv->dispose_has_run) - return; - - DEBUG ("dispose called"); - priv->dispose_has_run = TRUE; - - tp_channel_factory_iface_close_all (TP_CHANNEL_FACTORY_IFACE (object)); - g_assert (priv->channels == NULL); - - if (G_OBJECT_CLASS (gabble_im_factory_parent_class)->dispose) - G_OBJECT_CLASS (gabble_im_factory_parent_class)->dispose (object); -} - -static void -gabble_im_factory_get_property (GObject *object, - guint property_id, - GValue *value, - GParamSpec *pspec) -{ - GabbleImFactory *fac = GABBLE_IM_FACTORY (object); - GabbleImFactoryPrivate *priv = GABBLE_IM_FACTORY_GET_PRIVATE (fac); - - switch (property_id) { - case PROP_CONNECTION: - g_value_set_object (value, priv->conn); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } -} - -static void -gabble_im_factory_set_property (GObject *object, - guint property_id, - const GValue *value, - GParamSpec *pspec) -{ - GabbleImFactory *fac = GABBLE_IM_FACTORY (object); - GabbleImFactoryPrivate *priv = GABBLE_IM_FACTORY_GET_PRIVATE (fac); - - switch (property_id) { - case PROP_CONNECTION: - priv->conn = g_value_get_object (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } -} - -static void -gabble_im_factory_class_init (GabbleImFactoryClass *gabble_im_factory_class) -{ - GObjectClass *object_class = G_OBJECT_CLASS (gabble_im_factory_class); - GParamSpec *param_spec; - - g_type_class_add_private (gabble_im_factory_class, - sizeof (GabbleImFactoryPrivate)); - - object_class->constructor = gabble_im_factory_constructor; - object_class->dispose = gabble_im_factory_dispose; - - object_class->get_property = gabble_im_factory_get_property; - object_class->set_property = gabble_im_factory_set_property; - - param_spec = g_param_spec_object ("connection", "GabbleConnection object", - "Gabble connection object that owns this " - "IM channel factory object.", - GABBLE_TYPE_CONNECTION, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | - G_PARAM_STATIC_NICK | - G_PARAM_STATIC_BLURB); - g_object_class_install_property (object_class, PROP_CONNECTION, param_spec); - -} - -static GabbleIMChannel *new_im_channel (GabbleImFactory *fac, TpHandle handle); - -static void im_channel_closed_cb (GabbleIMChannel *chan, gpointer user_data); - - -/** - * im_factory_message_cb: - * - * Called by loudmouth when we get an incoming <message>. - */ -static LmHandlerResult -im_factory_message_cb (LmMessageHandler *handler, - LmConnection *lmconn, - LmMessage *message, - gpointer user_data) -{ - GabbleImFactory *fac = GABBLE_IM_FACTORY (user_data); - GabbleImFactoryPrivate *priv = GABBLE_IM_FACTORY_GET_PRIVATE (fac); - TpBaseConnection *conn = (TpBaseConnection *)priv->conn; - TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (conn, - TP_HANDLE_TYPE_CONTACT); - const gchar *from, *body; - time_t stamp; - TpChannelTextMessageType msgtype; - TpHandle handle; - GabbleIMChannel *chan; - gint state; - TpChannelTextSendError send_error; - - if (!gabble_text_mixin_parse_incoming_message (message, &from, &stamp, - &msgtype, &body, &state, &send_error)) - return LM_HANDLER_RESULT_REMOVE_MESSAGE; - - if (body == NULL && state == -1) - { - return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; - } - - handle = tp_handle_ensure (contact_repo, from, NULL, NULL); - if (handle == 0) - { - NODE_DEBUG (message->node, "ignoring message node from malformed jid"); - return LM_HANDLER_RESULT_REMOVE_MESSAGE; - } - - chan = g_hash_table_lookup (priv->channels, GINT_TO_POINTER (handle)); - - if (chan == NULL) - { - if (send_error != TP_CHANNEL_SEND_NO_ERROR) - { - DEBUG ("ignoring message error; no sending channel"); - tp_handle_unref (contact_repo, handle); - return LM_HANDLER_RESULT_REMOVE_MESSAGE; - } - - DEBUG ("found no IM channel, creating one"); - - chan = new_im_channel (fac, handle); - } - - g_assert (chan != NULL); - - /* now the channel is referencing the handle, so if we unref it, that's - * not a problem */ - tp_handle_unref (contact_repo, handle); - - if (send_error != TP_CHANNEL_SEND_NO_ERROR) - { - if (body == NULL) - { - DEBUG ("ignoring error sending chat state to %s (handle %u)", from, - handle); - return LM_HANDLER_RESULT_REMOVE_MESSAGE; - } - - DEBUG ("got error sending to %s (handle %u), msgtype %u, body:\n%s", - from, handle, msgtype, body); - - tp_svc_channel_type_text_emit_send_error ((TpSvcChannelTypeText *)chan, - send_error, stamp, msgtype, body); - - return LM_HANDLER_RESULT_REMOVE_MESSAGE; - } - - if (state != -1) - _gabble_im_channel_state_receive (chan, state); - - if (body != NULL) - _gabble_im_channel_receive (chan, msgtype, handle, from, stamp, body); - - return LM_HANDLER_RESULT_REMOVE_MESSAGE; -} - -/** - * im_channel_closed_cb: - * - * Signal callback for when an IM channel is closed. Removes the references - * that #GabbleConnection holds to them. - */ -static void -im_channel_closed_cb (GabbleIMChannel *chan, gpointer user_data) -{ - GabbleImFactory *conn = GABBLE_IM_FACTORY (user_data); - GabbleImFactoryPrivate *priv = GABBLE_IM_FACTORY_GET_PRIVATE (conn); - TpHandle contact_handle; - - if (priv->channels) - { - g_object_get (chan, "handle", &contact_handle, NULL); - - DEBUG ("removing channel with handle %d", contact_handle); - - g_hash_table_remove (priv->channels, GINT_TO_POINTER (contact_handle)); - } -} - -/** - * new_im_channel - */ -static GabbleIMChannel * -new_im_channel (GabbleImFactory *fac, TpHandle handle) -{ - GabbleImFactoryPrivate *priv; - TpBaseConnection *conn; - GabbleIMChannel *chan; - char *object_path; - - g_assert (GABBLE_IS_IM_FACTORY (fac)); - - priv = GABBLE_IM_FACTORY_GET_PRIVATE (fac); - conn = (TpBaseConnection *)priv->conn; - - object_path = g_strdup_printf ("%s/ImChannel%u", - conn->object_path, handle); - - chan = g_object_new (GABBLE_TYPE_IM_CHANNEL, - "connection", priv->conn, - "object-path", object_path, - "handle", handle, - NULL); - - DEBUG ("object path %s", object_path); - - g_signal_connect (chan, "closed", (GCallback) im_channel_closed_cb, fac); - - g_hash_table_insert (priv->channels, GINT_TO_POINTER (handle), chan); - - tp_channel_factory_iface_emit_new_channel (fac, (TpChannelIface *)chan, - NULL); - - g_free (object_path); - - return chan; -} - -static void -gabble_im_factory_iface_close_all (TpChannelFactoryIface *iface) -{ - GabbleImFactory *fac = GABBLE_IM_FACTORY (iface); - GabbleImFactoryPrivate *priv = GABBLE_IM_FACTORY_GET_PRIVATE (fac); - - DEBUG ("closing channels"); - - if (priv->channels) - { - GHashTable *tmp = priv->channels; - priv->channels = NULL; - g_hash_table_destroy (tmp); - } -} - -static void -gabble_im_factory_iface_connecting (TpChannelFactoryIface *iface) -{ - GabbleImFactory *fac = GABBLE_IM_FACTORY (iface); - GabbleImFactoryPrivate *priv = GABBLE_IM_FACTORY_GET_PRIVATE (fac); - - DEBUG ("adding callbacks"); - - g_assert (priv->message_cb == NULL); - - priv->message_cb = lm_message_handler_new (im_factory_message_cb, fac, NULL); - lm_connection_register_message_handler (priv->conn->lmconn, priv->message_cb, - LM_MESSAGE_TYPE_MESSAGE, - LM_HANDLER_PRIORITY_LAST); -} - - - -static void -gabble_im_factory_iface_connected (TpChannelFactoryIface *iface) -{ - /* nothing to do */ -} - -static void -gabble_im_factory_iface_disconnected (TpChannelFactoryIface *iface) -{ - GabbleImFactory *fac = GABBLE_IM_FACTORY (iface); - GabbleImFactoryPrivate *priv = GABBLE_IM_FACTORY_GET_PRIVATE (fac); - - DEBUG ("removing callbacks"); - - g_assert (priv->message_cb != NULL); - - lm_connection_unregister_message_handler (priv->conn->lmconn, - priv->message_cb, LM_MESSAGE_TYPE_MESSAGE); - lm_message_handler_unref (priv->message_cb); - priv->message_cb = NULL; -} - -struct _ForeachData -{ - TpChannelFunc foreach; - gpointer user_data; -}; - -static void -_foreach_slave (gpointer key, gpointer value, gpointer user_data) -{ - struct _ForeachData *data = (struct _ForeachData *) user_data; - TpChannelIface *chan = TP_CHANNEL_IFACE (value); - - data->foreach (chan, data->user_data); -} - -static void -gabble_im_factory_iface_foreach (TpChannelFactoryIface *iface, - TpChannelFunc foreach, - gpointer user_data) -{ - GabbleImFactory *fac = GABBLE_IM_FACTORY (iface); - GabbleImFactoryPrivate *priv = GABBLE_IM_FACTORY_GET_PRIVATE (fac); - struct _ForeachData data; - - data.user_data = user_data; - data.foreach = foreach; - - g_hash_table_foreach (priv->channels, _foreach_slave, &data); -} - -static TpChannelFactoryRequestStatus -gabble_im_factory_iface_request (TpChannelFactoryIface *iface, - const gchar *chan_type, - TpHandleType handle_type, - guint handle, - gpointer request, - TpChannelIface **ret, - GError **error) -{ - GabbleImFactory *fac = GABBLE_IM_FACTORY (iface); - GabbleImFactoryPrivate *priv = GABBLE_IM_FACTORY_GET_PRIVATE (fac); - TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( - (TpBaseConnection *)priv->conn, TP_HANDLE_TYPE_CONTACT); - GabbleIMChannel *chan; - TpChannelFactoryRequestStatus status; - - if (strcmp (chan_type, TP_IFACE_CHANNEL_TYPE_TEXT)) - return TP_CHANNEL_FACTORY_REQUEST_STATUS_NOT_IMPLEMENTED; - - if (handle_type != TP_HANDLE_TYPE_CONTACT) - return TP_CHANNEL_FACTORY_REQUEST_STATUS_NOT_AVAILABLE; - - if (!tp_handle_is_valid (contact_repo, handle, error)) - return TP_CHANNEL_FACTORY_REQUEST_STATUS_ERROR; - - chan = g_hash_table_lookup (priv->channels, GINT_TO_POINTER (handle)); - - status = TP_CHANNEL_FACTORY_REQUEST_STATUS_EXISTING; - if (!chan) - { - status = TP_CHANNEL_FACTORY_REQUEST_STATUS_CREATED; - chan = new_im_channel (fac, handle); - } - - g_assert (chan); - *ret = TP_CHANNEL_IFACE (chan); - return status; -} - -static void -gabble_im_factory_iface_init (gpointer g_iface, - gpointer iface_data) -{ - TpChannelFactoryIfaceClass *klass = (TpChannelFactoryIfaceClass *) g_iface; - - klass->close_all = gabble_im_factory_iface_close_all; - klass->connecting = gabble_im_factory_iface_connecting; - klass->connected = gabble_im_factory_iface_connected; - klass->disconnected = gabble_im_factory_iface_disconnected; - klass->foreach = gabble_im_factory_iface_foreach; - klass->request = gabble_im_factory_iface_request; -} - diff --git a/src/im-factory.h b/src/im-factory.h deleted file mode 100644 index b895ed4aa..000000000 --- a/src/im-factory.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * im-factory.h - Header for GabbleImFactory - * Copyright (C) 2006 Collabora Ltd. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef __IM_FACTORY_H__ -#define __IM_FACTORY_H__ - -#include <glib-object.h> - -#include "gabble-im-channel.h" - -G_BEGIN_DECLS - -typedef struct _GabbleImFactory GabbleImFactory; -typedef struct _GabbleImFactoryClass GabbleImFactoryClass; - -struct _GabbleImFactoryClass { - GObjectClass parent_class; -}; - -struct _GabbleImFactory { - GObject parent; -}; - -GType gabble_im_factory_get_type (void); - -/* TYPE MACROS */ -#define GABBLE_TYPE_IM_FACTORY \ - (gabble_im_factory_get_type ()) -#define GABBLE_IM_FACTORY(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_IM_FACTORY, GabbleImFactory)) -#define GABBLE_IM_FACTORY_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_IM_FACTORY,\ - GabbleImFactoryClass)) -#define GABBLE_IS_IM_FACTORY(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_IM_FACTORY)) -#define GABBLE_IS_IM_FACTORY_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_IM_FACTORY)) -#define GABBLE_IM_FACTORY_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_IM_FACTORY,\ - GabbleImFactoryClass)) - - -G_END_DECLS - -#endif /* #ifndef __IM_FACTORY_H__ */ - diff --git a/src/libmd5-rfc/.git-darcs-dir b/src/libmd5-rfc/.git-darcs-dir deleted file mode 100644 index e69de29bb..000000000 --- a/src/libmd5-rfc/.git-darcs-dir +++ /dev/null diff --git a/src/libmd5-rfc/md5.c b/src/libmd5-rfc/md5.c deleted file mode 100644 index c35d96c5e..000000000 --- a/src/libmd5-rfc/md5.c +++ /dev/null @@ -1,381 +0,0 @@ -/* - Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved. - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - L. Peter Deutsch - ghost@aladdin.com - - */ -/* $Id: md5.c,v 1.6 2002/04/13 19:20:28 lpd Exp $ */ -/* - Independent implementation of MD5 (RFC 1321). - - This code implements the MD5 Algorithm defined in RFC 1321, whose - text is available at - http://www.ietf.org/rfc/rfc1321.txt - The code is derived from the text of the RFC, including the test suite - (section A.5) but excluding the rest of Appendix A. It does not include - any code or documentation that is identified in the RFC as being - copyrighted. - - The original and principal author of md5.c is L. Peter Deutsch - <ghost@aladdin.com>. Other authors are noted in the change history - that follows (in reverse chronological order): - - 2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order - either statically or dynamically; added missing #include <string.h> - in library. - 2002-03-11 lpd Corrected argument list for main(), and added int return - type, in test program and T value program. - 2002-02-21 lpd Added missing #include <stdio.h> in test program. - 2000-07-03 lpd Patched to eliminate warnings about "constant is - unsigned in ANSI C, signed in traditional"; made test program - self-checking. - 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. - 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5). - 1999-05-03 lpd Original version. - */ - -#include "md5.h" -#include <string.h> - -#undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */ -#ifdef ARCH_IS_BIG_ENDIAN -# define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1) -#else -# define BYTE_ORDER 0 -#endif - -#define T_MASK ((md5_word_t)~0) -#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87) -#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9) -#define T3 0x242070db -#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111) -#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050) -#define T6 0x4787c62a -#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec) -#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe) -#define T9 0x698098d8 -#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850) -#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e) -#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841) -#define T13 0x6b901122 -#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c) -#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71) -#define T16 0x49b40821 -#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d) -#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf) -#define T19 0x265e5a51 -#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855) -#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2) -#define T22 0x02441453 -#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e) -#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437) -#define T25 0x21e1cde6 -#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829) -#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278) -#define T28 0x455a14ed -#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa) -#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07) -#define T31 0x676f02d9 -#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375) -#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd) -#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e) -#define T35 0x6d9d6122 -#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3) -#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb) -#define T38 0x4bdecfa9 -#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f) -#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f) -#define T41 0x289b7ec6 -#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805) -#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a) -#define T44 0x04881d05 -#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6) -#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a) -#define T47 0x1fa27cf8 -#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a) -#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb) -#define T50 0x432aff97 -#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58) -#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6) -#define T53 0x655b59c3 -#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d) -#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82) -#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e) -#define T57 0x6fa87e4f -#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f) -#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb) -#define T60 0x4e0811a1 -#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d) -#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca) -#define T63 0x2ad7d2bb -#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e) - - -static void -md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/) -{ - md5_word_t - a = pms->abcd[0], b = pms->abcd[1], - c = pms->abcd[2], d = pms->abcd[3]; - md5_word_t t; -#if BYTE_ORDER > 0 - /* Define storage only for big-endian CPUs. */ - md5_word_t X[16]; -#else - /* Define storage for little-endian or both types of CPUs. */ - md5_word_t xbuf[16]; - const md5_word_t *X; -#endif - - { -#if BYTE_ORDER == 0 - /* - * Determine dynamically whether this is a big-endian or - * little-endian machine, since we can use a more efficient - * algorithm on the latter. - */ - static const int w = 1; - - if (*((const md5_byte_t *)&w)) /* dynamic little-endian */ -#endif -#if BYTE_ORDER <= 0 /* little-endian */ - { - /* - * On little-endian machines, we can process properly aligned - * data without copying it. - */ - if (!((data - (const md5_byte_t *)0) & 3)) { - /* data are properly aligned */ - X = (const md5_word_t *)data; - } else { - /* not aligned */ - memcpy(xbuf, data, 64); - X = xbuf; - } - } -#endif -#if BYTE_ORDER == 0 - else /* dynamic big-endian */ -#endif -#if BYTE_ORDER >= 0 /* big-endian */ - { - /* - * On big-endian machines, we must arrange the bytes in the - * right order. - */ - const md5_byte_t *xp = data; - int i; - -# if BYTE_ORDER == 0 - X = xbuf; /* (dynamic only) */ -# else -# define xbuf X /* (static only) */ -# endif - for (i = 0; i < 16; ++i, xp += 4) - xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24); - } -#endif - } - -#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) - - /* Round 1. */ - /* Let [abcd k s i] denote the operation - a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */ -#define F(x, y, z) (((x) & (y)) | (~(x) & (z))) -#define SET(a, b, c, d, k, s, Ti)\ - t = a + F(b,c,d) + X[k] + Ti;\ - a = ROTATE_LEFT(t, s) + b - /* Do the following 16 operations. */ - SET(a, b, c, d, 0, 7, T1); - SET(d, a, b, c, 1, 12, T2); - SET(c, d, a, b, 2, 17, T3); - SET(b, c, d, a, 3, 22, T4); - SET(a, b, c, d, 4, 7, T5); - SET(d, a, b, c, 5, 12, T6); - SET(c, d, a, b, 6, 17, T7); - SET(b, c, d, a, 7, 22, T8); - SET(a, b, c, d, 8, 7, T9); - SET(d, a, b, c, 9, 12, T10); - SET(c, d, a, b, 10, 17, T11); - SET(b, c, d, a, 11, 22, T12); - SET(a, b, c, d, 12, 7, T13); - SET(d, a, b, c, 13, 12, T14); - SET(c, d, a, b, 14, 17, T15); - SET(b, c, d, a, 15, 22, T16); -#undef SET - - /* Round 2. */ - /* Let [abcd k s i] denote the operation - a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */ -#define G(x, y, z) (((x) & (z)) | ((y) & ~(z))) -#define SET(a, b, c, d, k, s, Ti)\ - t = a + G(b,c,d) + X[k] + Ti;\ - a = ROTATE_LEFT(t, s) + b - /* Do the following 16 operations. */ - SET(a, b, c, d, 1, 5, T17); - SET(d, a, b, c, 6, 9, T18); - SET(c, d, a, b, 11, 14, T19); - SET(b, c, d, a, 0, 20, T20); - SET(a, b, c, d, 5, 5, T21); - SET(d, a, b, c, 10, 9, T22); - SET(c, d, a, b, 15, 14, T23); - SET(b, c, d, a, 4, 20, T24); - SET(a, b, c, d, 9, 5, T25); - SET(d, a, b, c, 14, 9, T26); - SET(c, d, a, b, 3, 14, T27); - SET(b, c, d, a, 8, 20, T28); - SET(a, b, c, d, 13, 5, T29); - SET(d, a, b, c, 2, 9, T30); - SET(c, d, a, b, 7, 14, T31); - SET(b, c, d, a, 12, 20, T32); -#undef SET - - /* Round 3. */ - /* Let [abcd k s t] denote the operation - a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */ -#define H(x, y, z) ((x) ^ (y) ^ (z)) -#define SET(a, b, c, d, k, s, Ti)\ - t = a + H(b,c,d) + X[k] + Ti;\ - a = ROTATE_LEFT(t, s) + b - /* Do the following 16 operations. */ - SET(a, b, c, d, 5, 4, T33); - SET(d, a, b, c, 8, 11, T34); - SET(c, d, a, b, 11, 16, T35); - SET(b, c, d, a, 14, 23, T36); - SET(a, b, c, d, 1, 4, T37); - SET(d, a, b, c, 4, 11, T38); - SET(c, d, a, b, 7, 16, T39); - SET(b, c, d, a, 10, 23, T40); - SET(a, b, c, d, 13, 4, T41); - SET(d, a, b, c, 0, 11, T42); - SET(c, d, a, b, 3, 16, T43); - SET(b, c, d, a, 6, 23, T44); - SET(a, b, c, d, 9, 4, T45); - SET(d, a, b, c, 12, 11, T46); - SET(c, d, a, b, 15, 16, T47); - SET(b, c, d, a, 2, 23, T48); -#undef SET - - /* Round 4. */ - /* Let [abcd k s t] denote the operation - a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */ -#define I(x, y, z) ((y) ^ ((x) | ~(z))) -#define SET(a, b, c, d, k, s, Ti)\ - t = a + I(b,c,d) + X[k] + Ti;\ - a = ROTATE_LEFT(t, s) + b - /* Do the following 16 operations. */ - SET(a, b, c, d, 0, 6, T49); - SET(d, a, b, c, 7, 10, T50); - SET(c, d, a, b, 14, 15, T51); - SET(b, c, d, a, 5, 21, T52); - SET(a, b, c, d, 12, 6, T53); - SET(d, a, b, c, 3, 10, T54); - SET(c, d, a, b, 10, 15, T55); - SET(b, c, d, a, 1, 21, T56); - SET(a, b, c, d, 8, 6, T57); - SET(d, a, b, c, 15, 10, T58); - SET(c, d, a, b, 6, 15, T59); - SET(b, c, d, a, 13, 21, T60); - SET(a, b, c, d, 4, 6, T61); - SET(d, a, b, c, 11, 10, T62); - SET(c, d, a, b, 2, 15, T63); - SET(b, c, d, a, 9, 21, T64); -#undef SET - - /* Then perform the following additions. (That is increment each - of the four registers by the value it had before this block - was started.) */ - pms->abcd[0] += a; - pms->abcd[1] += b; - pms->abcd[2] += c; - pms->abcd[3] += d; -} - -void -md5_init(md5_state_t *pms) -{ - pms->count[0] = pms->count[1] = 0; - pms->abcd[0] = 0x67452301; - pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476; - pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301; - pms->abcd[3] = 0x10325476; -} - -void -md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes) -{ - const md5_byte_t *p = data; - int left = nbytes; - int offset = (pms->count[0] >> 3) & 63; - md5_word_t nbits = (md5_word_t)(nbytes << 3); - - if (nbytes <= 0) - return; - - /* Update the message length. */ - pms->count[1] += nbytes >> 29; - pms->count[0] += nbits; - if (pms->count[0] < nbits) - pms->count[1]++; - - /* Process an initial partial block. */ - if (offset) { - int copy = (offset + nbytes > 64 ? 64 - offset : nbytes); - - memcpy(pms->buf + offset, p, copy); - if (offset + copy < 64) - return; - p += copy; - left -= copy; - md5_process(pms, pms->buf); - } - - /* Process full blocks. */ - for (; left >= 64; p += 64, left -= 64) - md5_process(pms, p); - - /* Process a final partial block. */ - if (left) - memcpy(pms->buf, p, left); -} - -void -md5_finish(md5_state_t *pms, md5_byte_t digest[16]) -{ - static const md5_byte_t pad[64] = { - 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - }; - md5_byte_t data[8]; - int i; - - /* Save the length before padding. */ - for (i = 0; i < 8; ++i) - data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3)); - /* Pad to 56 bytes mod 64. */ - md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1); - /* Append the length. */ - md5_append(pms, data, 8); - for (i = 0; i < 16; ++i) - digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3)); -} diff --git a/src/libmd5-rfc/md5.h b/src/libmd5-rfc/md5.h deleted file mode 100644 index 698c995d8..000000000 --- a/src/libmd5-rfc/md5.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved. - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - L. Peter Deutsch - ghost@aladdin.com - - */ -/* $Id: md5.h,v 1.4 2002/04/13 19:20:28 lpd Exp $ */ -/* - Independent implementation of MD5 (RFC 1321). - - This code implements the MD5 Algorithm defined in RFC 1321, whose - text is available at - http://www.ietf.org/rfc/rfc1321.txt - The code is derived from the text of the RFC, including the test suite - (section A.5) but excluding the rest of Appendix A. It does not include - any code or documentation that is identified in the RFC as being - copyrighted. - - The original and principal author of md5.h is L. Peter Deutsch - <ghost@aladdin.com>. Other authors are noted in the change history - that follows (in reverse chronological order): - - 2002-04-13 lpd Removed support for non-ANSI compilers; removed - references to Ghostscript; clarified derivation from RFC 1321; - now handles byte order either statically or dynamically. - 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. - 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5); - added conditionalization for C++ compilation from Martin - Purschke <purschke@bnl.gov>. - 1999-05-03 lpd Original version. - */ - -#ifndef md5_INCLUDED -# define md5_INCLUDED - -/* - * This package supports both compile-time and run-time determination of CPU - * byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be - * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is - * defined as non-zero, the code will be compiled to run only on big-endian - * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to - * run on either big- or little-endian CPUs, but will run slightly less - * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined. - */ - -typedef unsigned char md5_byte_t; /* 8-bit byte */ -typedef unsigned int md5_word_t; /* 32-bit word */ - -/* Define the state of the MD5 Algorithm. */ -typedef struct md5_state_s { - md5_word_t count[2]; /* message length in bits, lsw first */ - md5_word_t abcd[4]; /* digest buffer */ - md5_byte_t buf[64]; /* accumulate block */ -} md5_state_t; - -#ifdef __cplusplus -extern "C" -{ -#endif - -/* Initialize the algorithm. */ -void md5_init(md5_state_t *pms); - -/* Append a string to the message. */ -void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes); - -/* Finish the message and return the digest. */ -void md5_finish(md5_state_t *pms, md5_byte_t digest[16]); - -#ifdef __cplusplus -} /* end extern "C" */ -#endif - -#endif /* md5_INCLUDED */ diff --git a/src/libmd5-rfc/md5main.c b/src/libmd5-rfc/md5main.c deleted file mode 100644 index 625a6198e..000000000 --- a/src/libmd5-rfc/md5main.c +++ /dev/null @@ -1,139 +0,0 @@ -/* - Copyright (C) 2002 Aladdin Enterprises. All rights reserved. - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - L. Peter Deutsch - ghost@aladdin.com - - */ -/* $Id: md5main.c,v 1.1 2002/04/13 19:20:28 lpd Exp $ */ -/* - Independent implementation of MD5 (RFC 1321). - - This code implements the MD5 Algorithm defined in RFC 1321, whose - text is available at - http://www.ietf.org/rfc/rfc1321.txt - The code is derived from the text of the RFC, including the test suite - (section A.5) but excluding the rest of Appendix A. It does not include - any code or documentation that is identified in the RFC as being - copyrighted. - - The original and principal author of md5.c is L. Peter Deutsch - <ghost@aladdin.com>. Other authors are noted in the change history - that follows (in reverse chronological order): - - 2002-04-13 lpd Splits off main program into a separate file, md5main.c. - */ - -#include "md5.h" -#include <math.h> -#include <stdio.h> -#include <string.h> - -/* - * This file builds an executable that performs various functions related - * to the MD5 library. Typical compilation: - * gcc -o md5main -lm md5main.c md5.c - */ -static const char *const usage = "\ -Usage:\n\ - md5main --test # run the self-test (A.5 of RFC 1321)\n\ - md5main --t-values # print the T values for the library\n\ - md5main --version # print the version of the package\n\ -"; -static const char *const version = "2002-04-13"; - -/* Run the self-test. */ -static int -do_test(void) -{ - static const char *const test[7*2] = { - "", "d41d8cd98f00b204e9800998ecf8427e", - "a", "0cc175b9c0f1b6a831c399e269772661", - "abc", "900150983cd24fb0d6963f7d28e17f72", - "message digest", "f96b697d7cb7938d525a2f31aaf161d0", - "abcdefghijklmnopqrstuvwxyz", "c3fcd3d76192e4007dfb496cca67e13b", - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", - "d174ab98d277d9f5a5611c2c9f419d9f", - "12345678901234567890123456789012345678901234567890123456789012345678901234567890", "57edf4a22be3c955ac49da2e2107b67a" - }; - int i; - int status = 0; - - for (i = 0; i < 7*2; i += 2) { - md5_state_t state; - md5_byte_t digest[16]; - char hex_output[16*2 + 1]; - int di; - - md5_init(&state); - md5_append(&state, (const md5_byte_t *)test[i], strlen(test[i])); - md5_finish(&state, digest); - for (di = 0; di < 16; ++di) - sprintf(hex_output + di * 2, "%02x", digest[di]); - if (strcmp(hex_output, test[i + 1])) { - printf("MD5 (\"%s\") = ", test[i]); - puts(hex_output); - printf("**** ERROR, should be: %s\n", test[i + 1]); - status = 1; - } - } - if (status == 0) - puts("md5 self-test completed successfully."); - return status; -} - -/* Print the T values. */ -static int -do_t_values(void) -{ - int i; - for (i = 1; i <= 64; ++i) { - unsigned long v = (unsigned long)(4294967296.0 * fabs(sin((double)i))); - - /* - * The following nonsense is only to avoid compiler warnings about - * "integer constant is unsigned in ANSI C, signed with -traditional". - */ - if (v >> 31) { - printf("#define T%d /* 0x%08lx */ (T_MASK ^ 0x%08lx)\n", i, - v, (unsigned long)(unsigned int)(~v)); - } else { - printf("#define T%d 0x%08lx\n", i, v); - } - } - return 0; -} - -/* Main program */ -int -main(int argc, char *argv[]) -{ - if (argc == 2) { - if (!strcmp(argv[1], "--test")) - return do_test(); - if (!strcmp(argv[1], "--t-values")) - return do_t_values(); - if (!strcmp(argv[1], "--version")) { - puts(version); - return 0; - } - } - puts(usage); - return 0; -} diff --git a/src/media-factory.c b/src/media-factory.c deleted file mode 100644 index b24088a71..000000000 --- a/src/media-factory.c +++ /dev/null @@ -1,875 +0,0 @@ -/* - * media-factory.c - Source for GabbleMediaFactory - * Copyright (C) 2006 Collabora Ltd. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "media-factory.h" - -#define DBUS_API_SUBJECT_TO_CHANGE - -#include <stdlib.h> -#include <string.h> -#include <time.h> - -#include <glib.h> - -#include <dbus/dbus-glib.h> -#include <dbus/dbus-glib-lowlevel.h> - -#include <loudmouth/loudmouth.h> - -#define DEBUG_FLAG GABBLE_DEBUG_MEDIA - -#include "debug.h" -#include "gabble-connection.h" -#include "gabble-media-channel.h" -#include "namespaces.h" -#include <telepathy-glib/interfaces.h> -#include "text-mixin.h" -#include <telepathy-glib/channel-factory-iface.h> -#include "util.h" - -static void gabble_media_factory_iface_init (gpointer g_iface, - gpointer iface_data); -static LmHandlerResult media_factory_jingle_cb (LmMessageHandler *, - LmConnection *, LmMessage *, gpointer); - -G_DEFINE_TYPE_WITH_CODE (GabbleMediaFactory, gabble_media_factory, - G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (TP_TYPE_CHANNEL_FACTORY_IFACE, - gabble_media_factory_iface_init)); - -/* properties */ -enum -{ - PROP_CONNECTION = 1, - LAST_PROPERTY -}; - -typedef struct _GabbleMediaFactoryPrivate GabbleMediaFactoryPrivate; -struct _GabbleMediaFactoryPrivate -{ - GabbleConnection *conn; - LmMessageHandler *jingle_cb; - LmMessageHandler *jingle_info_cb; - - GPtrArray *channels; - guint channel_index; - - GHashTable *session_chans; - - gboolean get_stun_from_jingle; - gchar *stun_server; - guint16 stun_port; - gchar *relay_token; - - gboolean dispose_has_run; -}; - -#define GABBLE_MEDIA_FACTORY_GET_PRIVATE(o) \ - (G_TYPE_INSTANCE_GET_PRIVATE ((o), GABBLE_TYPE_MEDIA_FACTORY, \ - GabbleMediaFactoryPrivate)) - -static GObject *gabble_media_factory_constructor (GType type, guint n_props, - GObjectConstructParam *props); - -static void -gabble_media_factory_init (GabbleMediaFactory *fac) -{ - GabbleMediaFactoryPrivate *priv = GABBLE_MEDIA_FACTORY_GET_PRIVATE (fac); - - priv->channels = g_ptr_array_sized_new (1); - priv->channel_index = 0; - - priv->jingle_cb = NULL; - priv->jingle_info_cb = NULL; - - priv->conn = NULL; - priv->dispose_has_run = FALSE; - - priv->session_chans = g_hash_table_new_full (g_str_hash, g_str_equal, - g_free, NULL); -} - -static GObject * -gabble_media_factory_constructor (GType type, guint n_props, - GObjectConstructParam *props) -{ - GObject *obj; - GabbleMediaFactoryPrivate *priv; - - obj = G_OBJECT_CLASS (gabble_media_factory_parent_class)-> - constructor (type, n_props, props); - priv = GABBLE_MEDIA_FACTORY_GET_PRIVATE (obj); - - return obj; -} - - -static void -gabble_media_factory_dispose (GObject *object) -{ - GabbleMediaFactory *fac = GABBLE_MEDIA_FACTORY (object); - GabbleMediaFactoryPrivate *priv = GABBLE_MEDIA_FACTORY_GET_PRIVATE (fac); - - if (priv->dispose_has_run) - return; - - DEBUG ("dispose called"); - priv->dispose_has_run = TRUE; - - g_assert (priv->jingle_cb == NULL); - g_assert (priv->jingle_info_cb == NULL); - - tp_channel_factory_iface_close_all (TP_CHANNEL_FACTORY_IFACE (object)); - g_assert (priv->channels == NULL); - - if (priv->session_chans) - { - g_assert (g_hash_table_size (priv->session_chans) == 0); - g_hash_table_destroy (priv->session_chans); - priv->session_chans = NULL; - } - - g_free (priv->stun_server); - g_free (priv->relay_token); - - if (G_OBJECT_CLASS (gabble_media_factory_parent_class)->dispose) - G_OBJECT_CLASS (gabble_media_factory_parent_class)->dispose (object); -} - -static void -gabble_media_factory_get_property (GObject *object, - guint property_id, - GValue *value, - GParamSpec *pspec) -{ - GabbleMediaFactory *fac = GABBLE_MEDIA_FACTORY (object); - GabbleMediaFactoryPrivate *priv = GABBLE_MEDIA_FACTORY_GET_PRIVATE (fac); - - switch (property_id) { - case PROP_CONNECTION: - g_value_set_object (value, priv->conn); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } -} - -static void -gabble_media_factory_set_property (GObject *object, - guint property_id, - const GValue *value, - GParamSpec *pspec) -{ - GabbleMediaFactory *fac = GABBLE_MEDIA_FACTORY (object); - GabbleMediaFactoryPrivate *priv = GABBLE_MEDIA_FACTORY_GET_PRIVATE (fac); - - switch (property_id) { - case PROP_CONNECTION: - priv->conn = g_value_get_object (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } -} - -static void -gabble_media_factory_class_init (GabbleMediaFactoryClass *gabble_media_factory_class) -{ - GObjectClass *object_class = G_OBJECT_CLASS (gabble_media_factory_class); - GParamSpec *param_spec; - - g_type_class_add_private (gabble_media_factory_class, - sizeof (GabbleMediaFactoryPrivate)); - - object_class->constructor = gabble_media_factory_constructor; - object_class->dispose = gabble_media_factory_dispose; - - object_class->get_property = gabble_media_factory_get_property; - object_class->set_property = gabble_media_factory_set_property; - - param_spec = g_param_spec_object ("connection", "GabbleConnection object", - "Gabble connection object that owns this " - "media channel factory object.", - GABBLE_TYPE_CONNECTION, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | - G_PARAM_STATIC_NICK | - G_PARAM_STATIC_BLURB); - g_object_class_install_property (object_class, PROP_CONNECTION, param_spec); - -} - -static gboolean _gabble_media_factory_sid_in_use (GabbleMediaFactory *fac, - const gchar *sid); -static GabbleMediaChannel *new_media_channel (GabbleMediaFactory *fac, - TpHandle handle); -static void media_channel_closed_cb (GabbleMediaChannel *chan, - gpointer user_data); - -/** - * media_factory_jingle_cb - * - * Called by loudmouth when we get an incoming <iq>. This handler - * is concerned only with jingle session queries, and allows other - * handlers to be called for other queries. - */ -static LmHandlerResult -media_factory_jingle_cb (LmMessageHandler *handler, - LmConnection *lmconn, - LmMessage *message, - gpointer user_data) -{ - GabbleMediaFactory *fac = GABBLE_MEDIA_FACTORY (user_data); - GabbleMediaFactoryPrivate *priv = GABBLE_MEDIA_FACTORY_GET_PRIVATE (fac); - TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( - (TpBaseConnection *)priv->conn, TP_HANDLE_TYPE_CONTACT); - LmMessageNode *iq_node, *session_node; - const gchar *from, *id, *action, *sid, *resource; - TpHandle handle = 0; - GabbleMediaChannel *chan = NULL; - gboolean chan_is_new = FALSE; - GError *error = NULL; - - g_assert (lmconn == priv->conn->lmconn); - - /* all jingle actions are sets */ - if (LM_MESSAGE_SUB_TYPE_SET != lm_message_get_sub_type (message)) - return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; - - /* is it for us? */ - iq_node = lm_message_get_node (message); - session_node = lm_message_node_get_child_with_namespace (message->node, - "jingle", NS_JINGLE); - - if (session_node != NULL) - { - action = lm_message_node_get_attribute (session_node, "action"); - } - else - { - session_node = lm_message_node_get_child_with_namespace (iq_node, - "session", NS_GOOGLE_SESSION); - - if (session_node == NULL) - return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; - - action = lm_message_node_get_attribute (session_node, "type"); - } - - if (action == NULL) - { - NODE_DEBUG (iq_node, "session action not found"); - goto BAD_REQUEST; - } - - from = lm_message_node_get_attribute (iq_node, "from"); - if (from == NULL) - { - NODE_DEBUG (iq_node, "'from' attribute not found"); - goto BAD_REQUEST; - } - - handle = tp_handle_ensure (contact_repo, from, NULL, NULL); - if (handle == 0) - { - NODE_DEBUG (iq_node, "unable to get handle for sender"); - goto BAD_REQUEST; - } - - resource = strchr (from, '/'); - if (resource == NULL || *resource == '\0') - { - NODE_DEBUG (iq_node, "sender with no resource"); - goto BAD_REQUEST; - } - - id = lm_message_node_get_attribute (iq_node, "id"); - if (id == NULL) - { - NODE_DEBUG (iq_node, "'id' attribute not found"); - goto BAD_REQUEST; - } - - /* does the session exist? */ - sid = lm_message_node_get_attribute (session_node, "sid"); - if (sid == NULL) - sid = lm_message_node_get_attribute (session_node, "id"); - - if (sid == NULL) - { - NODE_DEBUG (iq_node, "unable to get session id"); - goto BAD_REQUEST; - } - - if (_gabble_media_factory_sid_in_use (fac, sid)) - { - /* if it's media session, we should have it in here */ - chan = g_hash_table_lookup (priv->session_chans, sid); - } - - /* it's a new session */ - if (chan == NULL) - { - /* if the session is unknown, the only allowed action is "initiate" */ - if (tp_strdiff (action, "initiate") && - tp_strdiff (action, "session-initiate")) - { - NODE_DEBUG (iq_node, - "action is not \"initiate\" or \"session-initiate\", rejecting"); - goto BAD_REQUEST; - } - - DEBUG ("creating media channel"); - - chan = new_media_channel (fac, handle); - chan_is_new = TRUE; - } - - g_assert (chan != NULL); - - DEBUG ("dispatching to session %s", sid); - g_object_ref (chan); - - if (_gabble_media_channel_dispatch_session_action (chan, handle, resource, - sid, message, session_node, action, &error)) - { - if (chan_is_new) - tp_channel_factory_iface_emit_new_channel (fac, - (TpChannelIface *)chan, NULL); - } - else - { - if (chan_is_new) - gabble_media_channel_close (chan); - - g_assert (error != NULL); - _gabble_connection_send_iq_error (priv->conn, message, error->code, - error->message); - } - - g_object_unref (chan); - if (handle) - tp_handle_unref (contact_repo, handle); - - return LM_HANDLER_RESULT_REMOVE_MESSAGE; - -BAD_REQUEST: - _gabble_connection_send_iq_error ( - priv->conn, message, XMPP_ERROR_BAD_REQUEST, NULL); - - if (handle) - tp_handle_unref (contact_repo, handle); - return LM_HANDLER_RESULT_REMOVE_MESSAGE; -} - -static const gchar * -_gabble_media_factory_get_unique_sid (GabbleMediaFactory *fac) -{ - GabbleMediaFactoryPrivate *priv = GABBLE_MEDIA_FACTORY_GET_PRIVATE (fac); - guint32 val; - gchar *sid = NULL; - gboolean unique = FALSE; - - while (!unique) - { - val = g_random_int_range (1000000, G_MAXINT); - - g_free (sid); - sid = g_strdup_printf ("%u", val); - - unique = !_gabble_media_factory_sid_in_use (fac, sid); - } - - g_hash_table_insert (priv->session_chans, sid, NULL); - - return sid; -} - -static gboolean -_gabble_media_factory_sid_in_use (GabbleMediaFactory *fac, const gchar *sid) -{ - GabbleMediaFactoryPrivate *priv = GABBLE_MEDIA_FACTORY_GET_PRIVATE (fac); - gpointer key, value; - - return g_hash_table_lookup_extended (priv->session_chans, sid, &key, &value); -} - -const gchar * -_gabble_media_factory_allocate_sid (GabbleMediaFactory *fac, - GabbleMediaChannel *chan) -{ - const gchar *sid = _gabble_media_factory_get_unique_sid (fac); - - g_return_val_if_fail (sid, NULL); - - return _gabble_media_factory_register_sid (fac, sid, chan); -} - -const gchar * -_gabble_media_factory_register_sid (GabbleMediaFactory *fac, - const gchar *sid, - GabbleMediaChannel *chan) -{ - GabbleMediaFactoryPrivate *priv = GABBLE_MEDIA_FACTORY_GET_PRIVATE (fac); - gchar *sid_copy = g_strdup (sid); - - g_hash_table_replace (priv->session_chans, sid_copy, chan); - - return sid_copy; -} - -void -_gabble_media_factory_free_sid (GabbleMediaFactory *fac, const gchar *sid) -{ - GabbleMediaFactoryPrivate *priv = GABBLE_MEDIA_FACTORY_GET_PRIVATE (fac); - if (g_hash_table_lookup (priv->session_chans, sid)) - { - g_hash_table_remove (priv->session_chans, sid); - } -} - -static gboolean -_remove_sid_mapping (gpointer key, gpointer value, gpointer user_data) -{ - GabbleMediaChannel *chan = GABBLE_MEDIA_CHANNEL (value); - GabbleMediaChannel *target_chan = GABBLE_MEDIA_CHANNEL (user_data); - - if (chan == target_chan) return TRUE; - return FALSE; -} - -/** - * media_channel_closed_cb: - * - * Signal callback for when a media channel is closed. Removes the references - * that #GabbleMediaFactory holds to them. Also removes all the sessions for - * the closed channel. - */ -static void -media_channel_closed_cb (GabbleMediaChannel *chan, gpointer user_data) -{ - GabbleMediaFactory *fac = GABBLE_MEDIA_FACTORY (user_data); - GabbleMediaFactoryPrivate *priv = GABBLE_MEDIA_FACTORY_GET_PRIVATE (fac); - - if (priv->channels) - { - DEBUG ("removing media channel %p with ref count %d", - chan, G_OBJECT (chan)->ref_count); - - g_ptr_array_remove (priv->channels, chan); - g_object_unref (chan); - } - - if (priv->session_chans) - { - g_hash_table_foreach_remove (priv->session_chans, _remove_sid_mapping, - chan); - } -} - -/** - * new_media_channel - * - * Creates a new empty GabbleMediaChannel. - */ -static GabbleMediaChannel * -new_media_channel (GabbleMediaFactory *fac, TpHandle creator) -{ - GabbleMediaFactoryPrivate *priv; - TpBaseConnection *conn; - GabbleMediaChannel *chan; - gchar *object_path; - - g_assert (GABBLE_IS_MEDIA_FACTORY (fac)); - - priv = GABBLE_MEDIA_FACTORY_GET_PRIVATE (fac); - conn = (TpBaseConnection *)priv->conn; - - object_path = g_strdup_printf ("%s/MediaChannel%u", - conn->object_path, priv->channel_index); - priv->channel_index += 1; - - chan = g_object_new (GABBLE_TYPE_MEDIA_CHANNEL, - "connection", priv->conn, - "factory", fac, - "object-path", object_path, - "creator", creator, - NULL); - - if (priv->stun_server != NULL) - { - g_object_set ((GObject *) chan, "stun-server", priv->stun_server, NULL); - - if (priv->stun_port != 0) - g_object_set ((GObject *) chan, "stun-port", priv->stun_port, NULL); - } - - if (priv->relay_token != NULL) - { - g_object_set ((GObject *) chan, "gtalk-p2p-relay-token", - priv->relay_token, NULL); - } - - DEBUG ("object path %s", object_path); - - g_signal_connect (chan, "closed", (GCallback) media_channel_closed_cb, fac); - - g_ptr_array_add (priv->channels, chan); - - g_free (object_path); - - return chan; -} - - -static void -jingle_info_send_request (GabbleMediaFactory *fac) -{ - GabbleMediaFactoryPrivate *priv = GABBLE_MEDIA_FACTORY_GET_PRIVATE (fac); - TpBaseConnection *base = (TpBaseConnection *) priv->conn; - LmMessage *msg; - LmMessageNode *node; - const gchar *jid; - GError *error = NULL; - TpHandleRepoIface *contact_handles = tp_base_connection_get_handles (base, - TP_HANDLE_TYPE_CONTACT); - - jid = tp_handle_inspect (contact_handles, base->self_handle); - msg = lm_message_new_with_sub_type (jid, LM_MESSAGE_TYPE_IQ, - LM_MESSAGE_SUB_TYPE_GET); - - node = lm_message_node_add_child (msg->node, "query", NULL); - lm_message_node_set_attribute (node, "xmlns", NS_GOOGLE_JINGLE_INFO); - - if (!_gabble_connection_send (priv->conn, msg, &error)) - { - DEBUG ("jingle info send failed: %s\n", error->message); - g_error_free (error); - } - - lm_message_unref (msg); -} - - -/** - * jingle_info_iq_callback - * - * Called by loudmouth when we get an incoming <iq>. This handler - * is concerned only with Jingle info queries. - */ -LmHandlerResult -jingle_info_iq_callback (LmMessageHandler *handler, - LmConnection *lmconn, - LmMessage *message, - gpointer user_data) -{ - GabbleMediaFactory *fac = GABBLE_MEDIA_FACTORY (user_data); - GabbleMediaFactoryPrivate *priv = GABBLE_MEDIA_FACTORY_GET_PRIVATE (fac); - LmMessageSubType sub_type; - LmMessageNode *query_node, *node; - - query_node = lm_message_node_get_child_with_namespace (message->node, - "query", NS_GOOGLE_JINGLE_INFO); - - if (query_node == NULL) - return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; - - sub_type = lm_message_get_sub_type (message); - - if (sub_type == LM_MESSAGE_SUB_TYPE_ERROR) - { - GabbleXmppError xmpp_error = INVALID_XMPP_ERROR; - - node = lm_message_node_get_child (message->node, "error"); - if (node != NULL) - { - xmpp_error = gabble_xmpp_error_from_node (node); - } - - DEBUG ("jingle info error: %s", xmpp_error == INVALID_XMPP_ERROR ? - "unknown error" : gabble_xmpp_error_string (xmpp_error)); - - return LM_HANDLER_RESULT_REMOVE_MESSAGE; - } - - if (sub_type != LM_MESSAGE_SUB_TYPE_RESULT && - sub_type != LM_MESSAGE_SUB_TYPE_SET) - { - DEBUG ("jingle info: unexpected IQ type, ignoring"); - - return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; - } - - if (priv->get_stun_from_jingle) - node = lm_message_node_get_child (query_node, "stun"); - else - node = NULL; - - if (node != NULL) - { - node = lm_message_node_get_child (node, "server"); - - if (node != NULL) - { - const gchar *server; - const gchar *port; - - server = lm_message_node_get_attribute (node, "host"); - port = lm_message_node_get_attribute (node, "udp"); - - if (server != NULL) - { - DEBUG ("jingle info: got stun server %s", server); - g_free (priv->stun_server); - priv->stun_server = g_strdup (server); - } - - if (port != NULL) - { - DEBUG ("jingle info: got stun port %s", port); - priv->stun_port = atoi (port); - } - } - } - - node = lm_message_node_get_child (query_node, "relay"); - - if (node != NULL) - { - node = lm_message_node_get_child (node, "token"); - - if (node != NULL) - { - const gchar *token; - - token = lm_message_node_get_value (node); - - if (token != NULL) - { - DEBUG ("jingle info: got relay token %s", token); - g_free (priv->relay_token); - priv->relay_token = g_strdup (token); - } - } - } - - if (sub_type == LM_MESSAGE_SUB_TYPE_SET) - { - _gabble_connection_acknowledge_set_iq (priv->conn, message); - } - - return LM_HANDLER_RESULT_REMOVE_MESSAGE; -} - - -static void -gabble_media_factory_iface_close_all (TpChannelFactoryIface *iface) -{ - GabbleMediaFactory *fac = GABBLE_MEDIA_FACTORY (iface); - GabbleMediaFactoryPrivate *priv = GABBLE_MEDIA_FACTORY_GET_PRIVATE (fac); - - DEBUG ("closing channels"); - - if (priv->channels) - { - GPtrArray *tmp = priv->channels; - priv->channels = NULL; - - guint i; - - for (i = 0; i < tmp->len; i++) - { - GabbleMediaChannel *chan = g_ptr_array_index (tmp, i); - - DEBUG ("about to unref channel with ref_count %d", - G_OBJECT (chan)->ref_count); - - g_object_unref (chan); - } - - g_ptr_array_free (tmp, TRUE); - } - - if (priv->session_chans) - { - g_hash_table_destroy (priv->session_chans); - priv->session_chans = NULL; - } -} - -static void -gabble_media_factory_iface_connecting (TpChannelFactoryIface *iface) -{ - GabbleMediaFactory *fac = GABBLE_MEDIA_FACTORY (iface); - GabbleMediaFactoryPrivate *priv = GABBLE_MEDIA_FACTORY_GET_PRIVATE (fac); - - g_assert (priv->conn != NULL); - g_assert (priv->conn->lmconn != NULL); - - DEBUG ("adding callbacks"); - - g_assert (priv->jingle_cb == NULL); - g_assert (priv->jingle_info_cb == NULL); - - priv->jingle_cb = lm_message_handler_new (media_factory_jingle_cb, fac, - NULL); - lm_connection_register_message_handler (priv->conn->lmconn, priv->jingle_cb, - LM_MESSAGE_TYPE_IQ, - LM_HANDLER_PRIORITY_NORMAL); - - priv->jingle_info_cb = lm_message_handler_new (jingle_info_iq_callback, fac, - NULL); - lm_connection_register_message_handler (priv->conn->lmconn, - priv->jingle_info_cb, - LM_MESSAGE_TYPE_IQ, - LM_HANDLER_PRIORITY_NORMAL); -} - -static void -gabble_media_factory_iface_connected (TpChannelFactoryIface *iface) -{ - GabbleMediaFactory *fac = GABBLE_MEDIA_FACTORY (iface); - GabbleMediaFactoryPrivate *priv = GABBLE_MEDIA_FACTORY_GET_PRIVATE (fac); - gchar *stun_server = NULL; - guint stun_port = 0; - - g_object_get (priv->conn, - "stun-server", &stun_server, - "stun-port", &stun_port, - NULL); - - if (stun_server == NULL) - { - priv->get_stun_from_jingle = TRUE; - } - else - { - g_free (priv->stun_server); - priv->stun_server = stun_server; - priv->stun_port = stun_port; - } - - if (priv->conn->features & GABBLE_CONNECTION_FEATURES_GOOGLE_JINGLE_INFO) - { - jingle_info_send_request (fac); - } -} - -static void -gabble_media_factory_iface_disconnected (TpChannelFactoryIface *iface) -{ - GabbleMediaFactory *fac = GABBLE_MEDIA_FACTORY (iface); - GabbleMediaFactoryPrivate *priv = GABBLE_MEDIA_FACTORY_GET_PRIVATE (fac); - - DEBUG ("removing callbacks"); - - g_assert (priv->jingle_cb != NULL); - g_assert (priv->jingle_info_cb != NULL); - - lm_connection_unregister_message_handler (priv->conn->lmconn, - priv->jingle_cb, LM_MESSAGE_TYPE_IQ); - lm_message_handler_unref (priv->jingle_cb); - priv->jingle_cb = NULL; - - lm_connection_unregister_message_handler (priv->conn->lmconn, - priv->jingle_info_cb, LM_MESSAGE_TYPE_IQ); - lm_message_handler_unref (priv->jingle_info_cb); - priv->jingle_info_cb = NULL; - -} - -static void -gabble_media_factory_iface_foreach (TpChannelFactoryIface *iface, - TpChannelFunc foreach, - gpointer user_data) -{ - GabbleMediaFactory *fac = GABBLE_MEDIA_FACTORY (iface); - GabbleMediaFactoryPrivate *priv = GABBLE_MEDIA_FACTORY_GET_PRIVATE (fac); - guint i; - - for (i = 0; i < priv->channels->len; i++) - { - foreach (TP_CHANNEL_IFACE (g_ptr_array_index (priv->channels, i)), - user_data); - } -} - -static TpChannelFactoryRequestStatus -gabble_media_factory_iface_request (TpChannelFactoryIface *iface, - const gchar *chan_type, - TpHandleType handle_type, - guint handle, - gpointer request, - TpChannelIface **ret, - GError **error) -{ - GabbleMediaFactory *fac = GABBLE_MEDIA_FACTORY (iface); - GabbleMediaFactoryPrivate *priv = GABBLE_MEDIA_FACTORY_GET_PRIVATE (fac); - TpBaseConnection *conn = (TpBaseConnection *)priv->conn; - GabbleMediaChannel *chan = NULL; - - if (strcmp (chan_type, TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA)) - return TP_CHANNEL_FACTORY_REQUEST_STATUS_NOT_IMPLEMENTED; - - if (handle_type == 0) - { - /* create an empty channel */ - chan = new_media_channel (fac, conn->self_handle); - } - else if (handle_type == TP_HANDLE_TYPE_CONTACT) - { - chan = new_media_channel (fac, conn->self_handle); - - if (!_gabble_media_channel_add_member ((GObject *)chan, handle, "", - error)) - { - gabble_media_channel_close (chan); - - return TP_CHANNEL_FACTORY_REQUEST_STATUS_ERROR; - } - } - else - { - return TP_CHANNEL_FACTORY_REQUEST_STATUS_INVALID_HANDLE; - } - - g_assert (chan != NULL); - tp_channel_factory_iface_emit_new_channel (fac, (TpChannelIface *)chan, - request); - - *ret = TP_CHANNEL_IFACE (chan); - return TP_CHANNEL_FACTORY_REQUEST_STATUS_CREATED; -} - -static void -gabble_media_factory_iface_init (gpointer g_iface, - gpointer iface_data) -{ - TpChannelFactoryIfaceClass *klass = (TpChannelFactoryIfaceClass *) g_iface; - - klass->close_all = gabble_media_factory_iface_close_all; - klass->connected = gabble_media_factory_iface_connected; - klass->connecting = gabble_media_factory_iface_connecting; - klass->disconnected = gabble_media_factory_iface_disconnected; - klass->foreach = gabble_media_factory_iface_foreach; - klass->request = gabble_media_factory_iface_request; -} - diff --git a/src/media-factory.h b/src/media-factory.h deleted file mode 100644 index 90b19c7a3..000000000 --- a/src/media-factory.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * media-factory.h - Header for GabbleMediaFactory - * Copyright (C) 2006 Collabora Ltd. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef __MEDIA_FACTORY_H__ -#define __MEDIA_FACTORY_H__ - -#include <glib-object.h> - -#include "gabble-media-channel.h" - -G_BEGIN_DECLS - -typedef struct _GabbleMediaFactory GabbleMediaFactory; -typedef struct _GabbleMediaFactoryClass GabbleMediaFactoryClass; - -struct _GabbleMediaFactoryClass { - GObjectClass parent_class; -}; - -struct _GabbleMediaFactory { - GObject parent; -}; - -GType gabble_media_factory_get_type (void); - -/* TYPE MACROS */ -#define GABBLE_TYPE_MEDIA_FACTORY \ - (gabble_media_factory_get_type ()) -#define GABBLE_MEDIA_FACTORY(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_MEDIA_FACTORY,\ - GabbleMediaFactory)) -#define GABBLE_MEDIA_FACTORY_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_MEDIA_FACTORY,\ - GabbleMediaFactoryClass)) -#define GABBLE_IS_MEDIA_FACTORY(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_MEDIA_FACTORY)) -#define GABBLE_IS_MEDIA_FACTORY_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_MEDIA_FACTORY)) -#define GABBLE_MEDIA_FACTORY_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_MEDIA_FACTORY,\ - GabbleMediaFactoryClass)) - -const gchar * _gabble_media_factory_allocate_sid (GabbleMediaFactory *fac, - GabbleMediaChannel *chan); -const gchar * _gabble_media_factory_register_sid (GabbleMediaFactory *fac, - const gchar *sid, GabbleMediaChannel *chan); -void _gabble_media_factory_free_sid (GabbleMediaFactory *fac, - const gchar *sid); - -G_END_DECLS - -#endif /* #ifndef __MEDIA_FACTORY_H__ */ - diff --git a/src/muc-factory.c b/src/muc-factory.c deleted file mode 100644 index 088d434a7..000000000 --- a/src/muc-factory.c +++ /dev/null @@ -1,1004 +0,0 @@ -/* - * muc-factory.c - Source for GabbleMucFactory - * Copyright (C) 2006 Collabora Ltd. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "muc-factory.h" - -#define DBUS_API_SUBJECT_TO_CHANGE - -#include <stdlib.h> -#include <string.h> -#include <time.h> - -#include <glib.h> - -#include <dbus/dbus-glib.h> -#include <dbus/dbus-glib-lowlevel.h> - -#include <loudmouth/loudmouth.h> - -#define DEBUG_FLAG GABBLE_DEBUG_MUC - -#include "debug.h" -#include "disco.h" -#include "gabble-connection.h" -#include "presence-cache.h" -#include "gabble-muc-channel.h" -#include "gabble-roomlist-channel.h" -#include "namespaces.h" -#include <telepathy-glib/interfaces.h> -#include "text-mixin.h" -#include <telepathy-glib/channel-factory-iface.h> -#include "util.h" - -static void gabble_muc_factory_iface_init (gpointer g_iface, - gpointer iface_data); - -G_DEFINE_TYPE_WITH_CODE (GabbleMucFactory, gabble_muc_factory, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (TP_TYPE_CHANNEL_FACTORY_IFACE, - gabble_muc_factory_iface_init)); - -/* properties */ -enum -{ - PROP_CONNECTION = 1, - LAST_PROPERTY -}; - -typedef struct _GabbleMucFactoryPrivate GabbleMucFactoryPrivate; -struct _GabbleMucFactoryPrivate -{ - GabbleConnection *conn; - - LmMessageHandler *message_cb; - LmMessageHandler *presence_cb; - - GHashTable *channels; - GabbleRoomlistChannel *roomlist_channel; - - GHashTable *disco_requests; - - gboolean dispose_has_run; -}; - -#define GABBLE_MUC_FACTORY_GET_PRIVATE(o) \ - (G_TYPE_INSTANCE_GET_PRIVATE ((o), GABBLE_TYPE_MUC_FACTORY, \ - GabbleMucFactoryPrivate)) - -static GObject *gabble_muc_factory_constructor (GType type, guint n_props, - GObjectConstructParam *props); - -static void -gabble_muc_factory_init (GabbleMucFactory *fac) -{ - GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac); - - priv->channels = g_hash_table_new_full (g_direct_hash, g_direct_equal, - NULL, g_object_unref); - - priv->disco_requests = g_hash_table_new_full (g_direct_hash, g_direct_equal, - NULL, NULL); - - priv->message_cb = NULL; - priv->presence_cb = NULL; - - priv->conn = NULL; - priv->dispose_has_run = FALSE; -} - -static GObject * -gabble_muc_factory_constructor (GType type, guint n_props, - GObjectConstructParam *props) -{ - GObject *obj; - /* GabbleMucFactoryPrivate *priv; */ - - obj = G_OBJECT_CLASS (gabble_muc_factory_parent_class)-> - constructor (type, n_props, props); - /* priv = GABBLE_MUC_FACTORY_GET_PRIVATE (obj); */ - - return obj; -} - -static void -cancel_disco_request (gpointer key, gpointer value, gpointer user_data) -{ - GabbleDisco *disco = GABBLE_DISCO (user_data); - GabbleDiscoRequest *request = (GabbleDiscoRequest *) key; - - gabble_disco_cancel_request (disco, request); -} - -static void -gabble_muc_factory_dispose (GObject *object) -{ - GabbleMucFactory *fac = GABBLE_MUC_FACTORY (object); - GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac); - - if (priv->dispose_has_run) - return; - - DEBUG ("dispose called"); - priv->dispose_has_run = TRUE; - - tp_channel_factory_iface_close_all (TP_CHANNEL_FACTORY_IFACE (object)); - - g_hash_table_foreach (priv->disco_requests, cancel_disco_request, - priv->conn->disco); - g_hash_table_destroy (priv->disco_requests); - - if (G_OBJECT_CLASS (gabble_muc_factory_parent_class)->dispose) - G_OBJECT_CLASS (gabble_muc_factory_parent_class)->dispose (object); -} - -static void -gabble_muc_factory_get_property (GObject *object, - guint property_id, - GValue *value, - GParamSpec *pspec) -{ - GabbleMucFactory *fac = GABBLE_MUC_FACTORY (object); - GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac); - - switch (property_id) { - case PROP_CONNECTION: - g_value_set_object (value, priv->conn); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } -} - -static void -gabble_muc_factory_set_property (GObject *object, - guint property_id, - const GValue *value, - GParamSpec *pspec) -{ - GabbleMucFactory *fac = GABBLE_MUC_FACTORY (object); - GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac); - - switch (property_id) { - case PROP_CONNECTION: - priv->conn = g_value_get_object (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } -} - -static void -gabble_muc_factory_class_init (GabbleMucFactoryClass *gabble_muc_factory_class) -{ - GObjectClass *object_class = G_OBJECT_CLASS (gabble_muc_factory_class); - GParamSpec *param_spec; - - g_type_class_add_private (gabble_muc_factory_class, - sizeof (GabbleMucFactoryPrivate)); - - object_class->constructor = gabble_muc_factory_constructor; - object_class->dispose = gabble_muc_factory_dispose; - - object_class->get_property = gabble_muc_factory_get_property; - object_class->set_property = gabble_muc_factory_set_property; - - param_spec = g_param_spec_object ("connection", "GabbleConnection object", - "Gabble connection object that owns this " - "MUC factory object.", - GABBLE_TYPE_CONNECTION, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | - G_PARAM_STATIC_NICK | - G_PARAM_STATIC_BLURB); - g_object_class_install_property (object_class, PROP_CONNECTION, param_spec); -} - - -static GabbleMucChannel * -get_muc_from_jid (GabbleMucFactory *fac, const gchar *jid) -{ - GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac); - TpBaseConnection *conn = (TpBaseConnection *)priv->conn; - TpHandleRepoIface *room_repo = tp_base_connection_get_handles (conn, - TP_HANDLE_TYPE_ROOM); - TpHandle handle; - GabbleMucChannel *chan = NULL; - gchar *room; - - room = gabble_remove_resource (jid); - if (!room) - return NULL; - - handle = tp_handle_lookup (room_repo, room, NULL, NULL); - g_free (room); - if (handle) - chan = g_hash_table_lookup (priv->channels, GUINT_TO_POINTER (handle)); - - return chan; -} - - -/** - * muc_channel_closed_cb: - * - * Signal callback for when a MUC channel is closed. Removes the references - * that MucFactory holds to them. - */ -static void -muc_channel_closed_cb (GabbleMucChannel *chan, gpointer user_data) -{ - GabbleMucFactory *fac = GABBLE_MUC_FACTORY (user_data); - GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac); - TpHandle room_handle; - - if (priv->channels != NULL) - { - g_object_get (chan, "handle", &room_handle, NULL); - - DEBUG ("removing MUC channel with handle %d", room_handle); - - g_hash_table_remove (priv->channels, GINT_TO_POINTER (room_handle)); - } -} - -static void -muc_ready_cb (GabbleMucChannel *chan, - gpointer data) -{ - GabbleMucFactory *fac = GABBLE_MUC_FACTORY (data); - - DEBUG ("chan=%p", chan); - - tp_channel_factory_iface_emit_new_channel (fac, (TpChannelIface *)chan, - NULL); -} - -static void -muc_join_error_cb (GabbleMucChannel *chan, - GError *error, - gpointer data) -{ - GabbleMucFactory *fac = GABBLE_MUC_FACTORY (data); - - DEBUG ("error->code=%u, error->message=\"%s\"", error->code, error->message); - - tp_channel_factory_iface_emit_channel_error (fac, (TpChannelIface *)chan, - error, NULL); -} - -/** - * new_muc_channel - */ -static GabbleMucChannel * -new_muc_channel (GabbleMucFactory *fac, TpHandle handle, gboolean invite_self) -{ - GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac); - TpBaseConnection *conn = (TpBaseConnection *)priv->conn; - GabbleMucChannel *chan; - char *object_path; - - g_assert (g_hash_table_lookup (priv->channels, GINT_TO_POINTER (handle)) == NULL); - - object_path = g_strdup_printf ("%s/MucChannel%u", - conn->object_path, handle); - - DEBUG ("creating new chan, object path %s", object_path); - - chan = g_object_new (GABBLE_TYPE_MUC_CHANNEL, - "connection", priv->conn, - "object-path", object_path, - "handle", handle, - "invite-self", invite_self, - NULL); - - g_signal_connect (chan, "closed", (GCallback) muc_channel_closed_cb, fac); - - g_hash_table_insert (priv->channels, GINT_TO_POINTER (handle), chan); - - g_free (object_path); - - g_signal_connect (chan, "ready", G_CALLBACK (muc_ready_cb), fac); - g_signal_connect (chan, "join-error", G_CALLBACK (muc_join_error_cb), - fac); - - return chan; -} - -static void -do_invite (GabbleMucFactory *fac, - const gchar *room, - TpHandle inviter_handle, - const gchar *reason) -{ - GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac); - TpHandleRepoIface *room_repo = tp_base_connection_get_handles ( - (TpBaseConnection *)priv->conn, TP_HANDLE_TYPE_ROOM); - TpHandle room_handle; - - room_handle = tp_handle_ensure (room_repo, room, NULL, NULL); - - if (room_handle == 0) - { - DEBUG ("got a MUC invitation message with invalid room JID \"%s\"; " - "ignoring", room); - return; - } - - if (g_hash_table_lookup (priv->channels, GUINT_TO_POINTER (room_handle)) == - NULL) - { - GabbleMucChannel *chan = new_muc_channel (fac, room_handle, FALSE); - _gabble_muc_channel_handle_invited (chan, inviter_handle, reason); - } - else - { - DEBUG ("ignoring invite to room \"%s\"; we're already there", room); - } -} - -struct DiscoInviteData { - GabbleMucFactory *factory; - gchar *reason; - TpHandle inviter; -}; - -/** - * obsolete_invite_disco_cb: - * - * Callback for disco request we fired upon encountering obsolete disco. - * If the object is in fact MUC room, create a channel for it. - */ -static void -obsolete_invite_disco_cb (GabbleDisco *self, - GabbleDiscoRequest *request, - const gchar *jid, - const gchar *node, - LmMessageNode *query_result, - GError* error, - gpointer user_data) -{ - struct DiscoInviteData *data = (struct DiscoInviteData *) user_data; - - GabbleMucFactory *fac = GABBLE_MUC_FACTORY (data->factory); - GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac); - TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( - (TpBaseConnection *)priv->conn, TP_HANDLE_TYPE_CONTACT); - LmMessageNode *identity; - const char *category = NULL, *type = NULL; - - g_hash_table_remove (priv->disco_requests, request); - - if (error != NULL) - { - DEBUG ("ignoring obsolete invite to room '%s'; got disco error: %s", - jid, error->message); - goto out; - } - - identity = lm_message_node_get_child (query_result, "identity"); - if (identity != NULL) - { - category = lm_message_node_get_attribute (identity, "category"); - type = lm_message_node_get_attribute (identity, "type"); - } - - if (tp_strdiff (category, "conference") || - tp_strdiff (type, "text")) - { - DEBUG ("obsolete invite request specified inappropriate jid '%s' " - "(not a text conference); ignoring request", jid); - goto out; - } - - /* OK, it's MUC after all, create a new channel */ - do_invite (fac, jid, data->inviter, data->reason); - -out: - tp_handle_unref (contact_repo, data->inviter); - g_free (data->reason); - g_slice_free (struct DiscoInviteData, data); -} - -static gboolean -process_muc_invite (GabbleMucFactory *fac, - LmMessage *message, - const gchar *from, - TpChannelTextSendError send_error) -{ - GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac); - TpBaseConnection *conn = (TpBaseConnection *)priv->conn; - TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (conn, - TP_HANDLE_TYPE_CONTACT); - - LmMessageNode *x_node, *invite_node, *reason_node; - const gchar *invite_from, *reason = NULL; - TpHandle inviter_handle; - gchar *room; - - /* does it have a muc subnode? */ - x_node = lm_message_node_get_child_with_namespace (message->node, "x", - NS_MUC_USER); - - if (x_node == NULL) - return FALSE; - - /* and an invitation? */ - invite_node = lm_message_node_get_child (x_node, "invite"); - - if (invite_node == NULL) - return FALSE; - - /* FIXME: do something with these? */ - if (send_error != TP_CHANNEL_SEND_NO_ERROR) - { - NODE_DEBUG (message->node, "got a MUC invitation message with a send " - "error; ignoring"); - - return TRUE; - } - - invite_from = lm_message_node_get_attribute (invite_node, "from"); - if (invite_from == NULL) - { - NODE_DEBUG (message->node, "got a MUC invitation message with no JID; " - "ignoring"); - - return TRUE; - } - - inviter_handle = tp_handle_ensure (contact_repo, invite_from, - NULL, NULL); - if (inviter_handle == 0) - { - NODE_DEBUG (message->node, "got a MUC invitation message with invalid " - "inviter JID; ignoring"); - - return TRUE; - } - - reason_node = lm_message_node_get_child (invite_node, "reason"); - - if (reason_node != NULL) - reason = lm_message_node_get_value (reason_node); - - if (reason == NULL) - reason = ""; - - /* create the channel */ - room = gabble_remove_resource (from); - do_invite (fac, room, inviter_handle, reason); - g_free (room); - - tp_handle_unref (contact_repo, inviter_handle); - - return TRUE; -} - -static gboolean -process_obsolete_invite (GabbleMucFactory *fac, - LmMessage *message, - const gchar *from, - const gchar *body, - TpChannelTextSendError send_error) -{ - GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac); - TpBaseConnection *conn = (TpBaseConnection *)priv->conn; - TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (conn, - TP_HANDLE_TYPE_CONTACT); - - LmMessageNode *x_node; - const gchar *room; - TpHandle inviter_handle; - GabbleDiscoRequest *request; - struct DiscoInviteData *disco_udata; - - /* check for obsolete invite method */ - x_node = lm_message_node_get_child_with_namespace (message->node, "x", - NS_X_CONFERENCE); - if (x_node == NULL) - return FALSE; - - /* this can only happen if the user sent an obsolete invite with another - * client or something */ - if (send_error != TP_CHANNEL_SEND_NO_ERROR) - { - NODE_DEBUG (message->node, "got an obsolete MUC invitation message with " - "a send error; ignoring"); - - return TRUE; - } - - /* the room JID is in x */ - room = lm_message_node_get_attribute (x_node, "jid"); - if (room == NULL) - { - NODE_DEBUG (message->node, "got a obsolete MUC invitation with no room " - "JID; ignoring"); - - return TRUE; - } - - /* the inviter JID is in "from" */ - inviter_handle = tp_handle_ensure (contact_repo, from, NULL, NULL); - if (inviter_handle == 0) - { - NODE_DEBUG (message->node, "got an obsolete MUC invitation message from " - "an invalid JID; ignoring"); - - return TRUE; - } - - disco_udata = g_slice_new0 (struct DiscoInviteData); - disco_udata->factory = fac; - disco_udata->reason = g_strdup (body); - disco_udata->inviter = inviter_handle; - - DEBUG ("received obsolete MUC invite from handle %u (%s), discoing room %s", - inviter_handle, from, room); - - request = gabble_disco_request (priv->conn->disco, GABBLE_DISCO_TYPE_INFO, - room, NULL, obsolete_invite_disco_cb, disco_udata, G_OBJECT (fac), NULL); - - if (request != NULL) - { - g_hash_table_insert (priv->disco_requests, request, NULL); - } - else - { - DEBUG ("obsolete MUC invite disco failed, freeing info"); - - tp_handle_unref (contact_repo, inviter_handle); - g_free (disco_udata->reason); - g_slice_free (struct DiscoInviteData, disco_udata); - } - - return TRUE; -} - -/** - * muc_factory_message_cb: - * - * Called by loudmouth when we get an incoming <message>. - * We filter only groupchat and MUC messages, ignoring the rest. - */ -static LmHandlerResult -muc_factory_message_cb (LmMessageHandler *handler, - LmConnection *connection, - LmMessage *message, - gpointer user_data) -{ - GabbleMucFactory *fac = GABBLE_MUC_FACTORY (user_data); - GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac); - TpBaseConnection *conn = (TpBaseConnection *)priv->conn; - TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (conn, - TP_HANDLE_TYPE_CONTACT); - TpHandleRepoIface *room_repo = tp_base_connection_get_handles (conn, - TP_HANDLE_TYPE_ROOM); - - const gchar *from, *body; - time_t stamp; - TpChannelTextMessageType msgtype; - TpHandleRepoIface *handle_source; - TpHandleType handle_type; - TpHandle room_handle, handle; - GabbleMucChannel *chan; - gint state; - TpChannelTextSendError send_error; - gchar *room; - - if (!gabble_text_mixin_parse_incoming_message (message, &from, &stamp, - &msgtype, &body, &state, &send_error)) - return LM_HANDLER_RESULT_REMOVE_MESSAGE; - - if (process_muc_invite (fac, message, from, send_error)) - return LM_HANDLER_RESULT_REMOVE_MESSAGE; - - if (process_obsolete_invite (fac, message, from, body, send_error)) - return LM_HANDLER_RESULT_REMOVE_MESSAGE; - - /* check if a room with the jid exists */ - room = gabble_remove_resource (from); - room_handle = tp_handle_lookup (room_repo, room, NULL, NULL); - g_free (room); - - /* the message is nothing to do with MUC, do nothing */ - if (room_handle == 0) - { - return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; - } - - /* find the MUC channel */ - chan = g_hash_table_lookup (priv->channels, GUINT_TO_POINTER (room_handle)); - - if (chan == NULL) - { - NODE_DEBUG (message->node, "ignoring MUC message from known " - "handle with no corresponding channel"); - - return LM_HANDLER_RESULT_REMOVE_MESSAGE; - } - - /* get the handle of the sender, which is either the room - * itself or one of its members */ - if (strchr (from, '/') == NULL) - { - handle_source = room_repo; - handle_type = TP_HANDLE_TYPE_ROOM; - handle = room_handle; - tp_handle_ref (room_repo, handle); - } - else - { - handle_source = contact_repo; - handle_type = TP_HANDLE_TYPE_CONTACT; - handle = tp_handle_ensure (contact_repo, from, - GUINT_TO_POINTER (GABBLE_JID_ROOM_MEMBER), NULL); - - if (handle == 0) - { - NODE_DEBUG (message->node, "MUC message from invalid JID; ignoring"); - return LM_HANDLER_RESULT_REMOVE_MESSAGE; - } - - /* anything other than a type="groupchat" is from the person directly and - * simply relayed by the MUC, so should be left to the normal handlers */ - if (lm_message_get_sub_type (message) != LM_MESSAGE_SUB_TYPE_GROUPCHAT) - { - tp_handle_unref (contact_repo, handle); - - return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; - } - } - - if (send_error != TP_CHANNEL_SEND_NO_ERROR) - { - tp_svc_channel_type_text_emit_send_error ((TpSvcChannelTypeText *) chan, - send_error, stamp, msgtype, body); - goto done; - } - - if (state != -1 && handle_type == TP_HANDLE_TYPE_CONTACT) - _gabble_muc_channel_state_receive (chan, state, handle); - - if (body != NULL) - _gabble_muc_channel_receive (chan, msgtype, handle_type, handle, stamp, - body, message); - -done: - tp_handle_unref (handle_source, handle); - - return LM_HANDLER_RESULT_REMOVE_MESSAGE; -} - - -/** - * connection_presence_muc_cb: - * @handler: #LmMessageHandler for this message - * @connection: #LmConnection that originated the message - * @message: the presence message - * @user_data: callback data - * - * Called by loudmouth when we get an incoming <presence>. - */ -static LmHandlerResult -muc_factory_presence_cb (LmMessageHandler *handler, - LmConnection *lmconn, - LmMessage *msg, - gpointer user_data) -{ - GabbleMucFactory *fac = GABBLE_MUC_FACTORY (user_data); - GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac); - TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( - (TpBaseConnection *)priv->conn, TP_HANDLE_TYPE_CONTACT); - const char *from; - LmMessageSubType sub_type; - GabbleMucChannel *muc_chan; - LmMessageNode *x_node; - - g_assert (lmconn == priv->conn->lmconn); - - from = lm_message_node_get_attribute (msg->node, "from"); - - if (from == NULL) - { - NODE_DEBUG (msg->node, - "presence stanza without from attribute, ignoring"); - return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; - } - - sub_type = lm_message_get_sub_type (msg); - - muc_chan = get_muc_from_jid (fac, from); - - /* is it an error and for a MUC? */ - if (sub_type == LM_MESSAGE_SUB_TYPE_ERROR - && muc_chan != NULL) - { - _gabble_muc_channel_presence_error (muc_chan, from, msg->node); - - return LM_HANDLER_RESULT_REMOVE_MESSAGE; - } - - x_node = lm_message_node_get_child_with_namespace (msg->node, "x", - NS_MUC_USER); - - /* is it a MUC member presence? */ - if (x_node != NULL) - { - if (muc_chan != NULL) - { - TpHandle handle; - - handle = tp_handle_ensure (contact_repo, from, - GUINT_TO_POINTER (GABBLE_JID_ROOM_MEMBER), NULL); - if (handle == 0) - { - NODE_DEBUG (msg->node, - "discarding MUC presence from malformed jid"); - return LM_HANDLER_RESULT_REMOVE_MESSAGE; - } - - _gabble_muc_channel_member_presence_updated (muc_chan, handle, - msg, x_node); - tp_handle_unref (contact_repo, handle); - } - else - { - NODE_DEBUG (msg->node, "discarding unexpected MUC member presence"); - - return LM_HANDLER_RESULT_REMOVE_MESSAGE; - } - } - - return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; - -} - -static void -roomlist_channel_closed_cb (GabbleRoomlistChannel *chan, gpointer data) -{ - GabbleMucFactory *fac = GABBLE_MUC_FACTORY (data); - GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac); - - if (priv->roomlist_channel != NULL) - { - g_object_unref (priv->roomlist_channel); - priv->roomlist_channel = NULL; - } -} - -static gboolean -make_roomlist_channel (GabbleMucFactory *fac, gpointer request) -{ - GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac); - TpBaseConnection *conn = (TpBaseConnection *)priv->conn; - - const gchar *server; - gchar *object_path; - - g_assert (priv->roomlist_channel == NULL); - - server = _gabble_connection_find_conference_server (priv->conn); - - if (server == NULL) - return FALSE; - - object_path = g_strdup_printf ("%s/RoomlistChannel", - conn->object_path); - - priv->roomlist_channel = _gabble_roomlist_channel_new (priv->conn, - object_path, server); - - g_signal_connect (priv->roomlist_channel, "closed", - (GCallback) roomlist_channel_closed_cb, fac); - - tp_channel_factory_iface_emit_new_channel (fac, - (TpChannelIface *)priv->roomlist_channel, request); - - g_free (object_path); - - return TRUE; -} - - - -static void -gabble_muc_factory_iface_close_all (TpChannelFactoryIface *iface) -{ - GabbleMucFactory *fac = GABBLE_MUC_FACTORY (iface); - GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac); - - DEBUG ("closing channels"); - - if (priv->channels != NULL) - { - GHashTable *tmp = priv->channels; - priv->channels = NULL; - g_hash_table_destroy (tmp); - } - - if (priv->roomlist_channel != NULL) - { - GObject *tmp = G_OBJECT (priv->roomlist_channel); - priv->roomlist_channel = NULL; - g_object_unref (tmp); - } -} - -static void -gabble_muc_factory_iface_connecting (TpChannelFactoryIface *iface) -{ - GabbleMucFactory *fac = GABBLE_MUC_FACTORY (iface); - GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac); - - DEBUG ("adding callbacks"); - - g_assert (priv->message_cb == NULL); - g_assert (priv->presence_cb == NULL); - - priv->message_cb = lm_message_handler_new (muc_factory_message_cb, fac, - NULL); - lm_connection_register_message_handler (priv->conn->lmconn, priv->message_cb, - LM_MESSAGE_TYPE_MESSAGE, - LM_HANDLER_PRIORITY_NORMAL); - - priv->presence_cb = lm_message_handler_new (muc_factory_presence_cb, - fac, NULL); - lm_connection_register_message_handler (priv->conn->lmconn, - priv->presence_cb, LM_MESSAGE_TYPE_PRESENCE, LM_HANDLER_PRIORITY_NORMAL); -} - - -static void -gabble_muc_factory_iface_connected (TpChannelFactoryIface *iface) -{ - /* nothing to do */ -} - -static void -gabble_muc_factory_iface_disconnected (TpChannelFactoryIface *iface) -{ - GabbleMucFactory *fac = GABBLE_MUC_FACTORY (iface); - GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac); - - DEBUG ("removing callbacks"); - - g_assert (priv->message_cb != NULL); - g_assert (priv->presence_cb != NULL); - - lm_connection_unregister_message_handler (priv->conn->lmconn, - priv->message_cb, LM_MESSAGE_TYPE_MESSAGE); - lm_message_handler_unref (priv->message_cb); - priv->message_cb = NULL; - - lm_connection_unregister_message_handler (priv->conn->lmconn, - priv->presence_cb, LM_MESSAGE_TYPE_PRESENCE); - lm_message_handler_unref (priv->presence_cb); - priv->presence_cb = NULL; -} - -struct _ForeachData -{ - TpChannelFunc foreach; - gpointer user_data; -}; - -static void -_foreach_slave (gpointer key, gpointer value, gpointer user_data) -{ - struct _ForeachData *data = (struct _ForeachData *) user_data; - TpChannelIface *chan = TP_CHANNEL_IFACE (value); - - data->foreach (chan, data->user_data); -} - -static void -gabble_muc_factory_iface_foreach (TpChannelFactoryIface *iface, - TpChannelFunc foreach, - gpointer user_data) -{ - GabbleMucFactory *fac = GABBLE_MUC_FACTORY (iface); - GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac); - struct _ForeachData data; - - data.user_data = user_data; - data.foreach = foreach; - - g_hash_table_foreach (priv->channels, _foreach_slave, &data); - - if (priv->roomlist_channel != NULL) - foreach (TP_CHANNEL_IFACE (priv->roomlist_channel), user_data); -} - -static TpChannelFactoryRequestStatus -gabble_muc_factory_iface_request (TpChannelFactoryIface *iface, - const gchar *chan_type, - TpHandleType handle_type, - guint handle, - gpointer request, - TpChannelIface **ret, - GError **error) -{ - GabbleMucFactory *fac = GABBLE_MUC_FACTORY (iface); - GabbleMucFactoryPrivate *priv = GABBLE_MUC_FACTORY_GET_PRIVATE (fac); - TpHandleRepoIface *room_repo = tp_base_connection_get_handles ( - (TpBaseConnection *)priv->conn, TP_HANDLE_TYPE_ROOM); - GabbleMucChannel *chan; - - if (!tp_strdiff (chan_type, TP_IFACE_CHANNEL_TYPE_ROOM_LIST)) - { - if (priv->roomlist_channel != NULL) - { - *ret = TP_CHANNEL_IFACE (priv->roomlist_channel); - return TP_CHANNEL_FACTORY_REQUEST_STATUS_EXISTING; - } - - /* FIXME - delay if services aren't discovered yet? */ - if (!make_roomlist_channel (fac, request)) - { - DEBUG ("no conference server available for roomlist request"); - return TP_CHANNEL_FACTORY_REQUEST_STATUS_NOT_AVAILABLE; - } - *ret = TP_CHANNEL_IFACE (priv->roomlist_channel); - return TP_CHANNEL_FACTORY_REQUEST_STATUS_CREATED; - } - - if (strcmp (chan_type, TP_IFACE_CHANNEL_TYPE_TEXT)) - return TP_CHANNEL_FACTORY_REQUEST_STATUS_NOT_IMPLEMENTED; - - if (handle_type != TP_HANDLE_TYPE_ROOM) - return TP_CHANNEL_FACTORY_REQUEST_STATUS_NOT_AVAILABLE; - - if (!tp_handle_is_valid (room_repo, handle, NULL)) - return TP_CHANNEL_FACTORY_REQUEST_STATUS_INVALID_HANDLE; - - chan = g_hash_table_lookup (priv->channels, GINT_TO_POINTER (handle)); - if (!chan) - { - chan = new_muc_channel (fac, handle, TRUE); - return TP_CHANNEL_FACTORY_REQUEST_STATUS_QUEUED; - } - - if (_gabble_muc_channel_is_ready (chan)) - { - *ret = TP_CHANNEL_IFACE (chan); - return TP_CHANNEL_FACTORY_REQUEST_STATUS_EXISTING; - } - else - { - return TP_CHANNEL_FACTORY_REQUEST_STATUS_QUEUED; - } -} - -static void -gabble_muc_factory_iface_init (gpointer g_iface, - gpointer iface_data) -{ - TpChannelFactoryIfaceClass *klass = (TpChannelFactoryIfaceClass *) g_iface; - - klass->close_all = gabble_muc_factory_iface_close_all; - klass->connecting = gabble_muc_factory_iface_connecting; - klass->connected = gabble_muc_factory_iface_connected; - klass->disconnected = gabble_muc_factory_iface_disconnected; - klass->foreach = gabble_muc_factory_iface_foreach; - klass->request = gabble_muc_factory_iface_request; -} - - diff --git a/src/muc-factory.h b/src/muc-factory.h deleted file mode 100644 index 09ec8dbc4..000000000 --- a/src/muc-factory.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * muc-factory.h - Header for GabbleMucFactory - * Copyright (C) 2006 Collabora Ltd. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef __MUC_FACTORY_H__ -#define __MUC_FACTORY_H__ - -#include <glib-object.h> - -G_BEGIN_DECLS - -typedef struct _GabbleMucFactory GabbleMucFactory; -typedef struct _GabbleMucFactoryClass GabbleMucFactoryClass; - -struct _GabbleMucFactoryClass { - GObjectClass parent_class; -}; - -struct _GabbleMucFactory { - GObject parent; -}; - -GType gabble_muc_factory_get_type (void); - -/* TYPE MACROS */ -#define GABBLE_TYPE_MUC_FACTORY \ - (gabble_muc_factory_get_type ()) -#define GABBLE_MUC_FACTORY(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_MUC_FACTORY, \ - GabbleMucFactory)) -#define GABBLE_MUC_FACTORY_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_MUC_FACTORY, \ - GabbleMucFactoryClass)) -#define GABBLE_IS_MUC_FACTORY(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_MUC_FACTORY)) -#define GABBLE_IS_MUC_FACTORY_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_MUC_FACTORY)) -#define GABBLE_MUC_FACTORY_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_MUC_FACTORY, \ - GabbleMucFactoryClass)) - -G_END_DECLS - -#endif /* #ifndef __MUC_FACTORY_H__ */ diff --git a/src/namespaces.h b/src/namespaces.h deleted file mode 100644 index 91bf40813..000000000 --- a/src/namespaces.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * namespaces.h - XMPP namespace constants - * Copyright (C) 2005 Collabora Ltd. - * Copyright (C) 2005 Nokia Corporation - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef __GABBLE_NAMESPACES__H__ -#define __GABBLE_NAMESPACES__H__ - -#define NS_CAPS "http://jabber.org/protocol/caps" -#define NS_CHAT_STATES "http://jabber.org/protocol/chatstates" -#define NS_DISCO_INFO "http://jabber.org/protocol/disco#info" -#define NS_DISCO_ITEMS "http://jabber.org/protocol/disco#items" -#define NS_GABBLE_CAPS "http://telepathy.freedesktop.org/caps" -#define NS_GOOGLE_CAPS "http://www.google.com/xmpp/client/caps" -#define NS_GOOGLE_FEAT_SESSION "http://www.google.com/xmpp/protocol/session" -#define NS_GOOGLE_FEAT_VOICE "http://www.google.com/xmpp/protocol/voice/v1" -#define NS_GOOGLE_JINGLE_INFO "google:jingleinfo" -#define NS_GOOGLE_ROSTER "google:roster" -#define NS_GOOGLE_SESSION "http://www.google.com/session" -#define NS_GOOGLE_SESSION_PHONE "http://www.google.com/session/phone" -#define NS_GOOGLE_TRANSPORT_P2P "http://www.google.com/transport/p2p" -#define NS_JINGLE "http://jabber.org/protocol/jingle" -#define NS_JINGLE_DESCRIPTION_AUDIO \ - "http://jabber.org/protocol/jingle/description/audio" -#define NS_JINGLE_DESCRIPTION_VIDEO \ - "http://jabber.org/protocol/jingle/description/video" -#define NS_JINGLE_ERRORS "http://jabber.org/protocol/jingle#errors" -#define NS_JINGLE_TRANSPORT_ICE \ - "http://jabber.org/protocol/jingle/transport/ice" -#define NS_MUC "http://jabber.org/protocol/muc" -#define NS_MUC_USER "http://jabber.org/protocol/muc#user" -#define NS_MUC_ADMIN "http://jabber.org/protocol/muc#admin" -#define NS_MUC_OWNER "http://jabber.org/protocol/muc#owner" -#define NS_NICK "http://jabber.org/protocol/nick" -#define NS_NOKIA_IV \ - "http://videovoip.tableteer.nokia.com/xmpp/privilege" -#define NS_OLPC_BUDDY_PROPS "http://laptop.org/xmpp/buddy-properties" -#define NS_OLPC_ACTIVITIES "http://laptop.org/xmpp/activities" -#define NS_PUBSUB "http://jabber.org/protocol/pubsub" -#define NS_PRESENCE_INVISIBLE "presence-invisible" -#define NS_PRIVACY "jabber:iq:privacy" -#define NS_REGISTER "jabber:iq:register" -#define NS_ROSTER "jabber:iq:roster" -#define NS_VCARD_TEMP "vcard-temp" -#define NS_VCARD_TEMP_UPDATE "vcard-temp:x:update" -#define NS_X_DATA "jabber:x:data" -#define NS_X_DELAY "jabber:x:delay" -#define NS_X_CONFERENCE "jabber:x:conference" -#define NS_XMPP_STANZAS "urn:ietf:params:xml:ns:xmpp-stanzas" - -#endif /* __GABBLE_NAMESPACES__H__ */ diff --git a/src/presence-cache.c b/src/presence-cache.c deleted file mode 100644 index f663d8d91..000000000 --- a/src/presence-cache.c +++ /dev/null @@ -1,1266 +0,0 @@ -/* - * gabble-presence-cache.c - Gabble's contact presence cache - * Copyright (C) 2005 Collabora Ltd. - * Copyright (C) 2005 Nokia Corporation - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "presence-cache.h" - -#include <stdlib.h> -#include <string.h> - -/* when five DIFFERENT guys report the same caps for a given bundle, it'll -be enough */ -#define CAPABILITY_BUNDLE_ENOUGH_TRUST 5 -#define DEBUG_FLAG GABBLE_DEBUG_PRESENCE - -#include <telepathy-glib/intset.h> - -#include "debug.h" -#include "disco.h" /* \o\ \o/ /o/ */ -#include "namespaces.h" -#include "util.h" - -#include "gabble-signals-marshal.h" - -G_DEFINE_TYPE (GabblePresenceCache, gabble_presence_cache, G_TYPE_OBJECT); - -/* properties */ -enum -{ - PROP_CONNECTION = 1, - LAST_PROPERTY -}; - -/* signal enum */ -enum -{ - PRESENCE_UPDATE, - NICKNAME_UPDATE, - CAPABILITIES_UPDATE, - AVATAR_UPDATE, - LAST_SIGNAL -}; - -static guint signals[LAST_SIGNAL] = { 0 }; - -#define GABBLE_PRESENCE_CACHE_PRIV(account) \ - ((GabblePresenceCachePrivate *)account->priv) - -typedef struct _GabblePresenceCachePrivate GabblePresenceCachePrivate; - -struct _GabblePresenceCachePrivate -{ - GabbleConnection *conn; - - gulong status_changed_cb; - LmMessageHandler *lm_message_cb; - - GHashTable *presence; - TpHandleSet *presence_handles; - - GHashTable *capabilities; - GHashTable *disco_pending; - guint caps_serial; - - gboolean dispose_has_run; -}; - -typedef struct _DiscoWaiter DiscoWaiter; - -struct _DiscoWaiter -{ - TpHandleRepoIface *repo; - TpHandle handle; - gchar *resource; - guint serial; - gboolean disco_requested; -}; - -/** - * disco_waiter_new () - */ -static DiscoWaiter * -disco_waiter_new (TpHandleRepoIface *repo, - TpHandle handle, - const gchar *resource, - guint serial) -{ - DiscoWaiter *waiter; - - g_assert (repo); - tp_handle_ref (repo, handle); - - waiter = g_slice_new0 (DiscoWaiter); - waiter->repo = repo; - waiter->handle = handle; - waiter->resource = g_strdup (resource); - waiter->serial = serial; - - DEBUG ("created waiter %p for handle %u with serial %u", waiter, handle, - serial); - - return waiter; -} - -static void -disco_waiter_free (DiscoWaiter *waiter) -{ - g_assert (NULL != waiter); - - DEBUG ("freeing waiter %p for handle %u with serial %u", waiter, - waiter->handle, waiter->serial); - - tp_handle_unref (waiter->repo, waiter->handle); - - g_free (waiter->resource); - g_slice_free (DiscoWaiter, waiter); -} - -static void -disco_waiter_list_free (GSList *list) -{ - GSList *i; - - DEBUG ("list %p", list); - - for (i = list; NULL != i; i = i->next) - disco_waiter_free ((DiscoWaiter *) i->data); - - g_slist_free (list); -} - -static guint -disco_waiter_list_get_request_count (GSList *list) -{ - guint c = 0; - GSList *i; - - for (i = list; i; i = i->next) - { - DiscoWaiter *waiter = (DiscoWaiter *) i->data; - - if (waiter->disco_requested) - c++; - } - - return c; -} - -typedef struct _CapabilityInfo CapabilityInfo; - -struct _CapabilityInfo -{ - GabblePresenceCapabilities caps; - TpIntSet *guys; - guint trust; -}; - -static CapabilityInfo * -capability_info_get (GabblePresenceCache *cache, const gchar *node, - GabblePresenceCapabilities caps) -{ - GabblePresenceCachePrivate *priv = GABBLE_PRESENCE_CACHE_PRIV (cache); - CapabilityInfo *info = g_hash_table_lookup (priv->capabilities, node); - - if (NULL == info) - { - info = g_slice_new0 (CapabilityInfo); - info->caps = caps; - info->guys = tp_intset_new (); - g_hash_table_insert (priv->capabilities, g_strdup (node), info); - } - - return info; -} - -static void -capability_info_free (CapabilityInfo *info) -{ - tp_intset_destroy (info->guys); - g_slice_free (CapabilityInfo, info); -} - -static guint -capability_info_recvd (GabblePresenceCache *cache, const gchar *node, - TpHandle handle, GabblePresenceCapabilities caps) -{ - CapabilityInfo *info = capability_info_get (cache, node, caps); - - /* Detect inconsistency in reported caps */ - if (info->caps != caps) - { - tp_intset_clear (info->guys); - info->caps = caps; - info->trust = 0; - } - - if (!tp_intset_is_member (info->guys, handle)) - { - tp_intset_add (info->guys, handle); - info->trust++; - } - - return info->trust; -} - -static void gabble_presence_cache_init (GabblePresenceCache *presence_cache); -static GObject * gabble_presence_cache_constructor (GType type, guint n_props, - GObjectConstructParam *props); -static void gabble_presence_cache_dispose (GObject *object); -static void gabble_presence_cache_finalize (GObject *object); -static void gabble_presence_cache_set_property (GObject *object, guint - property_id, const GValue *value, GParamSpec *pspec); -static void gabble_presence_cache_get_property (GObject *object, guint - property_id, GValue *value, GParamSpec *pspec); -static GabblePresence *_cache_insert (GabblePresenceCache *cache, - TpHandle handle); - -static void gabble_presence_cache_status_changed_cb (GabbleConnection *, - TpConnectionStatus, TpConnectionStatusReason, gpointer); -static LmHandlerResult gabble_presence_cache_lm_message_cb (LmMessageHandler*, - LmConnection*, LmMessage*, gpointer); - -static void -gabble_presence_cache_class_init (GabblePresenceCacheClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GParamSpec *param_spec; - - g_type_class_add_private (object_class, sizeof (GabblePresenceCachePrivate)); - - object_class->constructor = gabble_presence_cache_constructor; - - object_class->dispose = gabble_presence_cache_dispose; - object_class->finalize = gabble_presence_cache_finalize; - - object_class->get_property = gabble_presence_cache_get_property; - object_class->set_property = gabble_presence_cache_set_property; - - param_spec = g_param_spec_object ("connection", "GabbleConnection object", - "Gabble connection object that owns this " - "presence cache.", - GABBLE_TYPE_CONNECTION, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | - G_PARAM_STATIC_NICK | - G_PARAM_STATIC_BLURB); - g_object_class_install_property (object_class, - PROP_CONNECTION, - param_spec); - - signals[PRESENCE_UPDATE] = g_signal_new ( - "presence-update", - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, - g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); - signals[NICKNAME_UPDATE] = g_signal_new ( - "nickname-update", - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, - g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); - signals[CAPABILITIES_UPDATE] = g_signal_new ( - "capabilities-update", - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, - gabble_marshal_VOID__UINT_UINT_UINT, G_TYPE_NONE, - 3, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT); - signals[AVATAR_UPDATE] = g_signal_new ( - "avatar-update", - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, - g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); -} - -static void -gabble_presence_cache_init (GabblePresenceCache *cache) -{ - GabblePresenceCachePrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (cache, - GABBLE_TYPE_PRESENCE_CACHE, GabblePresenceCachePrivate); - - cache->priv = priv; - - priv->presence = g_hash_table_new_full (NULL, NULL, NULL, g_object_unref); - priv->capabilities = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, - (GDestroyNotify) capability_info_free); - priv->disco_pending = g_hash_table_new_full (g_str_hash, g_str_equal, - g_free, (GDestroyNotify) disco_waiter_list_free); - priv->caps_serial = 1; -} - -static GObject * -gabble_presence_cache_constructor (GType type, guint n_props, - GObjectConstructParam *props) -{ - GObject *obj; - GabblePresenceCachePrivate *priv; - - obj = G_OBJECT_CLASS (gabble_presence_cache_parent_class)-> - constructor (type, n_props, props); - priv = GABBLE_PRESENCE_CACHE_PRIV (GABBLE_PRESENCE_CACHE (obj)); - - priv->status_changed_cb = g_signal_connect (priv->conn, "status-changed", - G_CALLBACK (gabble_presence_cache_status_changed_cb), obj); - - return obj; -} - -static void -gabble_presence_cache_dispose (GObject *object) -{ - GabblePresenceCache *self = GABBLE_PRESENCE_CACHE (object); - GabblePresenceCachePrivate *priv = GABBLE_PRESENCE_CACHE_PRIV (self); - - if (priv->dispose_has_run) - return; - - DEBUG ("dispose called"); - - priv->dispose_has_run = TRUE; - - g_assert (priv->lm_message_cb == NULL); - - g_signal_handler_disconnect (priv->conn, priv->status_changed_cb); - - g_hash_table_destroy (priv->presence); - priv->presence = NULL; - - g_hash_table_destroy (priv->capabilities); - priv->capabilities = NULL; - - g_hash_table_destroy (priv->disco_pending); - priv->disco_pending = NULL; - - tp_handle_set_destroy (priv->presence_handles); - priv->presence_handles = NULL; - - if (G_OBJECT_CLASS (gabble_presence_cache_parent_class)->dispose) - G_OBJECT_CLASS (gabble_presence_cache_parent_class)->dispose (object); -} - -static void -gabble_presence_cache_finalize (GObject *object) -{ - DEBUG ("called with %p", object); - - G_OBJECT_CLASS (gabble_presence_cache_parent_class)->finalize (object); -} - -static void -gabble_presence_cache_get_property (GObject *object, - guint property_id, - GValue *value, - GParamSpec *pspec) -{ - GabblePresenceCache *cache = GABBLE_PRESENCE_CACHE (object); - GabblePresenceCachePrivate *priv = GABBLE_PRESENCE_CACHE_PRIV (cache); - - switch (property_id) { - case PROP_CONNECTION: - g_value_set_object (value, priv->conn); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } -} - -static void -gabble_presence_cache_set_property (GObject *object, - guint property_id, - const GValue *value, - GParamSpec *pspec) -{ - GabblePresenceCache *cache = GABBLE_PRESENCE_CACHE (object); - GabblePresenceCachePrivate *priv = GABBLE_PRESENCE_CACHE_PRIV (cache); - TpHandleRepoIface *contact_repo; - TpHandleSet *new_presence_handles; - - switch (property_id) { - case PROP_CONNECTION: - priv->conn = g_value_get_object (value); - contact_repo = tp_base_connection_get_handles ( - (TpBaseConnection *)priv->conn, TP_HANDLE_TYPE_CONTACT); - - new_presence_handles = tp_handle_set_new (contact_repo); - - if (priv->presence_handles) - { - const TpIntSet *add; - TpIntSet *tmp; - add = tp_handle_set_peek (priv->presence_handles); - tmp = tp_handle_set_update (new_presence_handles, add); - tp_handle_set_destroy (priv->presence_handles); - tp_intset_destroy (tmp); - } - priv->presence_handles = new_presence_handles; - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } -} - -#if 0 -static gboolean -_presence_node_has_google_voice (LmMessageNode *pres_node) -{ - LmMessageNode *node; - const gchar *cap_ext; - gchar **features, **tmp; - gboolean found = FALSE; - - node = lm_message_node_get_child_with_namespace (pres_node, "c", NS_CAPS); - - if (node == NULL); - return FALSE; - - cap_ext = lm_message_node_get_attribute (node, "ext"); - - if (cap_ext == NULL); - return FALSE; - - features = g_strsplit (cap_ext, " ", 0); - - for (tmp = features; *tmp; tmp++) - { - if (!tp_strdiff (tmp, "voice-v1")) - { - found = TRUE; - break; - } - } - - g_strfreev (features); - - return found; -} -#endif - -static void -gabble_presence_cache_status_changed_cb (GabbleConnection *conn, - TpConnectionStatus status, - TpConnectionStatusReason reason, - gpointer data) -{ - GabblePresenceCache *cache = GABBLE_PRESENCE_CACHE (data); - GabblePresenceCachePrivate *priv = GABBLE_PRESENCE_CACHE_PRIV (cache); - - g_assert (conn == priv->conn); - - switch (status) - { - case TP_CONNECTION_STATUS_CONNECTING: - g_assert (priv->lm_message_cb == NULL); - - priv->lm_message_cb = lm_message_handler_new ( - gabble_presence_cache_lm_message_cb, cache, NULL); - lm_connection_register_message_handler (priv->conn->lmconn, - priv->lm_message_cb, - LM_MESSAGE_TYPE_PRESENCE, - LM_HANDLER_PRIORITY_LAST); - lm_connection_register_message_handler (priv->conn->lmconn, - priv->lm_message_cb, - LM_MESSAGE_TYPE_MESSAGE, - LM_HANDLER_PRIORITY_FIRST); - break; - case TP_CONNECTION_STATUS_CONNECTED: - break; - case TP_CONNECTION_STATUS_DISCONNECTED: - if (priv->lm_message_cb != NULL) - { - lm_connection_unregister_message_handler (conn->lmconn, - priv->lm_message_cb, - LM_MESSAGE_TYPE_PRESENCE); - lm_connection_unregister_message_handler (conn->lmconn, - priv->lm_message_cb, - LM_MESSAGE_TYPE_MESSAGE); - lm_message_handler_unref (priv->lm_message_cb); - priv->lm_message_cb = NULL; - } - break; - default: - g_assert_not_reached (); - } -} - -static GabblePresenceId -_presence_node_get_status (LmMessageNode *pres_node) -{ - const gchar *presence_show; - LmMessageNode *child_node = lm_message_node_get_child (pres_node, "show"); - - if (!child_node) - { - /* - NODE_DEBUG (pres_node, - "<presence> without <show> received from server, " - "setting presence to available"); - */ - return GABBLE_PRESENCE_AVAILABLE; - } - - presence_show = lm_message_node_get_value (child_node); - - if (!presence_show) - { - /* - NODE_DEBUG (pres_node, - "empty <show> tag received from server, " - "setting presence to available"); - */ - return GABBLE_PRESENCE_AVAILABLE; - } - - if (0 == strcmp (presence_show, JABBER_PRESENCE_SHOW_AWAY)) - return GABBLE_PRESENCE_AWAY; - else if (0 == strcmp (presence_show, JABBER_PRESENCE_SHOW_CHAT)) - return GABBLE_PRESENCE_CHAT; - else if (0 == strcmp (presence_show, JABBER_PRESENCE_SHOW_DND)) - return GABBLE_PRESENCE_DND; - else if (0 == strcmp (presence_show, JABBER_PRESENCE_SHOW_XA)) - return GABBLE_PRESENCE_XA; - else - { - NODE_DEBUG (pres_node, - "unrecognised <show/> value received from server, " - "setting presence to available"); - return GABBLE_PRESENCE_AVAILABLE; - } -} - -static void -_grab_nickname (GabblePresenceCache *cache, - TpHandle handle, - const gchar *from, - LmMessageNode *node) -{ - const gchar *nickname; - GabblePresence *presence; - - node = lm_message_node_get_child_with_namespace (node, "nick", NS_NICK); - - if (NULL == node) - return; - - presence = gabble_presence_cache_get (cache, handle); - - if (NULL == presence) - return; - - nickname = lm_message_node_get_value (node); - DEBUG ("got nickname \"%s\" for %s", nickname, from); - - if (tp_strdiff (presence->nickname, nickname)) - { - if (NULL != presence->nickname) - g_free (presence->nickname); - - presence->nickname = g_strdup (nickname); - g_signal_emit (cache, signals[NICKNAME_UPDATE], 0, handle); - } -} - -static void -_grab_avatar_sha1 (GabblePresenceCache *cache, - TpHandle handle, - const gchar *from, - LmMessageNode *node) -{ - const gchar *sha1; - LmMessageNode *x_node, *photo_node; - GabblePresence *presence; - - presence = gabble_presence_cache_get (cache, handle); - - if (NULL == presence) - return; - - x_node = lm_message_node_get_child_with_namespace (node, "x", - NS_VCARD_TEMP_UPDATE); - - if (NULL == x_node) - { -#if 0 - if (handle == priv->conn->parent.self_handle) - { - /* One of my other resources does not support XEP-0153. As per that - * XEP, I MUST stop advertising the image hash, at least until all - * instances of non-conforming resources have gone offline. - * However, we're going to ignore this requirement and hope that - * non-conforming clients won't alter the <PHOTO>, which should - * in practice be true. - */ - presence->avatar_sha1 = NULL; - } -#endif - return; - } - - photo_node = lm_message_node_get_child (x_node, "photo"); - - /* If there is no photo node, the resource supports XEP-0153, but has - * nothing in particular to say about the avatar. */ - if (NULL == photo_node) - return; - - sha1 = lm_message_node_get_value (photo_node); - - if (tp_strdiff (presence->avatar_sha1, sha1)) - { - g_free (presence->avatar_sha1); - presence->avatar_sha1 = g_strdup (sha1); - -#if 0 - if (handle == priv->conn->parent.self_handle) - { - /* that would be us, then. According to XEP-0153, we MUST - * immediately send a presence update with an empty update child - * element (no photo node), then re-download our own vCard; - * when that arrives, we may start setting the photo node in our - * presence again. - * - * For the moment I'm going to ignore that requirement and - * trust that our other resource is getting its sha1 right! - */ - /* TODO: I don't trust anyone to get XMPP right, so let's do - * this. :D */ - } -#endif - - g_signal_emit (cache, signals[AVATAR_UPDATE], 0, handle); - } -} - -static GSList * -_extract_cap_bundles (LmMessageNode *lm_node) -{ - const gchar *node, *ver, *ext; - GSList *uris = NULL; - LmMessageNode *cap_node; - - cap_node = lm_message_node_get_child_with_namespace (lm_node, "c", NS_CAPS); - - if (NULL == cap_node) - return NULL; - - node = lm_message_node_get_attribute (cap_node, "node"); - - if (NULL == node) - return NULL; - - ver = lm_message_node_get_attribute (cap_node, "ver"); - - if (NULL != ver) - uris = g_slist_prepend (uris, g_strdup_printf ("%s#%s", node, ver)); - - ext = lm_message_node_get_attribute (cap_node, "ext"); - - if (NULL != ext) - { - gchar **exts, **i; - - exts = g_strsplit (ext, " ", 0); - - for (i = exts; NULL != *i; i++) - uris = g_slist_prepend (uris, g_strdup_printf ("%s#%s", node, *i)); - - g_strfreev (exts); - } - - return uris; -} - -static void -_caps_disco_cb (GabbleDisco *disco, - GabbleDiscoRequest *request, - const gchar *jid, - const gchar *node, - LmMessageNode *query_result, - GError *error, - gpointer user_data) -{ - GSList *waiters, *i; - LmMessageNode *child; - GabblePresenceCache *cache; - GabblePresenceCachePrivate *priv; - TpHandleRepoIface *contact_repo; - gchar *full_jid = NULL; - GabblePresenceCapabilities caps = 0; - guint trust; - TpHandle handle = 0; - - cache = GABBLE_PRESENCE_CACHE (user_data); - priv = GABBLE_PRESENCE_CACHE_PRIV (cache); - contact_repo = tp_base_connection_get_handles ( - (TpBaseConnection *)priv->conn, TP_HANDLE_TYPE_CONTACT); - - if (NULL == node) - { - g_warning ("got disco response with NULL node, ignoring"); - return; - } - - waiters = g_hash_table_lookup (priv->disco_pending, node); - - if (NULL != error) - { - DiscoWaiter *waiter = NULL; - - DEBUG ("disco query failed: %s", error->message); - - for (i = waiters; NULL != i; i = i->next) - { - waiter = (DiscoWaiter *) i->data; - - if (!waiter->disco_requested) - { - const gchar *jid; - - jid = tp_handle_inspect (contact_repo, waiter->handle); - full_jid = g_strdup_printf ("%s/%s", jid, waiter->resource); - - gabble_disco_request (disco, GABBLE_DISCO_TYPE_INFO, full_jid, - node, _caps_disco_cb, cache, G_OBJECT(cache), NULL); - waiter->disco_requested = TRUE; - break; - } - } - - if (NULL != i) - { - DEBUG ("sent a retry disco request to %s for URI %s", full_jid, - node); - } - else - { - DEBUG ("failed to find a suitable candidate to retry disco " - "request for URI %s", node); - /* FIXME do something very clever here? */ - g_hash_table_remove (priv->disco_pending, node); - } - - goto OUT; - } - - for (child = query_result->children; NULL != child; child = child->next) - { - const gchar *var; - - if (0 != strcmp (child->name, "feature")) - continue; - - var = lm_message_node_get_attribute (child, "var"); - - if (NULL == var) - continue; - - /* TODO: use a table that equates disco features to caps */ - if (0 == strcmp (var, NS_GOOGLE_TRANSPORT_P2P)) - caps |= PRESENCE_CAP_GOOGLE_TRANSPORT_P2P; - else if (0 == strcmp (var, NS_GOOGLE_FEAT_VOICE)) - caps |= PRESENCE_CAP_GOOGLE_VOICE; - else if (0 == strcmp (var, NS_JINGLE)) - caps |= PRESENCE_CAP_JINGLE; - else if (0 == strcmp (var, NS_JINGLE_DESCRIPTION_AUDIO)) - caps |= PRESENCE_CAP_JINGLE_DESCRIPTION_AUDIO; - else if (0 == strcmp (var, NS_JINGLE_DESCRIPTION_VIDEO)) - caps |= PRESENCE_CAP_JINGLE_DESCRIPTION_VIDEO; - else if (0 == strcmp (var, NS_CHAT_STATES)) - caps |= PRESENCE_CAP_CHAT_STATES; - } - - handle = tp_handle_ensure (contact_repo, jid, NULL, NULL); - if (handle == 0) - { - DEBUG ("Ignoring presence from invalid JID %s", jid); - goto OUT; - } - trust = capability_info_recvd (cache, node, handle, caps); - - for (i = waiters; NULL != i;) - { - DiscoWaiter *waiter; - GabblePresence *presence; - - waiter = (DiscoWaiter *) i->data; - - if (trust >= CAPABILITY_BUNDLE_ENOUGH_TRUST || waiter->handle == handle) - { - GSList *tmp; - gpointer key; - gpointer value; - - /* trusted reply */ - presence = gabble_presence_cache_get (cache, waiter->handle); - - if (presence) - { - GabblePresenceCapabilities save_caps = presence->caps; - DEBUG ("setting caps for %d (%s) to %d", handle, jid, caps); - gabble_presence_set_capabilities (presence, waiter->resource,caps, - waiter->serial); - DEBUG ("caps for %d (%s) now %d", handle, jid, presence->caps); - g_signal_emit (cache, signals[CAPABILITIES_UPDATE], 0, - waiter->handle, save_caps, presence->caps); - } - - tmp = i; - i = i->next; - - waiters = g_slist_delete_link (waiters, tmp); - - if (!g_hash_table_lookup_extended (priv->disco_pending, node, &key, - &value)) - g_assert_not_reached (); - - g_hash_table_steal (priv->disco_pending, node); - g_hash_table_insert (priv->disco_pending, key, waiters); - - disco_waiter_free (waiter); - } - else if (trust + disco_waiter_list_get_request_count (waiters) - 1 - < CAPABILITY_BUNDLE_ENOUGH_TRUST) - { - /* if the possible trust, not counting this guy, is too low, - * we have been poisoned and reset our trust meters - disco - * anybody we still haven't to be able to get more trusted replies */ - - if (!waiter->disco_requested) - { - const gchar *jid; - - jid = tp_handle_inspect (contact_repo, waiter->handle); - full_jid = g_strdup_printf ("%s/%s", jid, waiter->resource); - - gabble_disco_request (disco, GABBLE_DISCO_TYPE_INFO, full_jid, - node, _caps_disco_cb, cache, G_OBJECT(cache), NULL); - waiter->disco_requested = TRUE; - - g_free (full_jid); - full_jid = NULL; - } - - i = i->next; - } - else - { - /* trust level still uncertain, don't do nothing */ - i = i->next; - } - } - - if (trust >= CAPABILITY_BUNDLE_ENOUGH_TRUST) - g_hash_table_remove (priv->disco_pending, node); - -OUT: - - if (handle) - tp_handle_unref (contact_repo, handle); - g_free (full_jid); -} - -static void -_process_caps_uri (GabblePresenceCache *cache, - const gchar *from, - const gchar *uri, - TpHandle handle, - const gchar *resource, - guint serial) -{ - CapabilityInfo *info; - GabblePresenceCachePrivate *priv; - TpHandleRepoIface *contact_repo; - - priv = GABBLE_PRESENCE_CACHE_PRIV (cache); - contact_repo = tp_base_connection_get_handles ( - (TpBaseConnection *)priv->conn, TP_HANDLE_TYPE_CONTACT); - info = capability_info_get (cache, uri, 0); - - if (info->trust >= CAPABILITY_BUNDLE_ENOUGH_TRUST - || tp_intset_is_member (info->guys, handle)) - { - /* we already have enough trust for this node; apply the cached value to - * the (handle, resource) */ - - GabblePresence *presence = gabble_presence_cache_get (cache, handle); - DEBUG ("enough trust for URI %s, setting caps for %u (%s) to %u", - uri, handle, from, info->caps); - - if (presence) - { - GabblePresenceCapabilities save_caps = presence->caps; - gabble_presence_set_capabilities (presence, resource, info->caps, - serial); - g_signal_emit (cache, signals[CAPABILITIES_UPDATE], 0, - handle, save_caps, presence->caps); - DEBUG ("caps for %d (%s) now %d", handle, from, presence->caps); - } - else - { - DEBUG ("presence not found"); - } - } - else - { - /* Append the (handle, resource) pair to the list of such pairs - * waiting for capabilities for this uri, and send a disco request - * if we don't have enough possible trust yet */ - - GSList *waiters; - DiscoWaiter *waiter; - guint possible_trust; - gpointer key; - gpointer value = NULL; - - DEBUG ("not enough trust for URI %s", uri); - - /* If the URI is in the hash table, steal it and its value; we can - * reuse the same URI for the following insertion. Otherwise, make a - * copy of the URI for use as a key. - */ - - if (g_hash_table_lookup_extended (priv->disco_pending, uri, &key, - &value)) - { - g_hash_table_steal (priv->disco_pending, key); - } - else - { - key = g_strdup (uri); - } - - waiters = (GSList *) value; - waiter = disco_waiter_new (contact_repo, handle, resource, serial); - waiters = g_slist_prepend (waiters, waiter); - g_hash_table_insert (priv->disco_pending, key, waiters); - - possible_trust = disco_waiter_list_get_request_count (waiters); - - if (!value - || info->trust + possible_trust < CAPABILITY_BUNDLE_ENOUGH_TRUST) - { - /* DISCO */ - DEBUG ("only %u trust out of %u possible thus far, sending " - "disco for URI %s", info->trust + possible_trust, - CAPABILITY_BUNDLE_ENOUGH_TRUST, uri); - gabble_disco_request (priv->conn->disco, GABBLE_DISCO_TYPE_INFO, - from, uri, _caps_disco_cb, cache, G_OBJECT (cache), NULL); - /* enough DISCO for you, buddy */ - waiter->disco_requested = TRUE; - } - } -} - -static void -_process_caps (GabblePresenceCache *cache, - TpHandle handle, - const gchar *from, - LmMessageNode *lm_node) -{ - const gchar *resource; - GSList *uris, *i; - GabblePresenceCachePrivate *priv; - guint serial; - - priv = GABBLE_PRESENCE_CACHE_PRIV (cache); - serial = priv->caps_serial++; - - resource = strchr (from, '/'); - - uris = _extract_cap_bundles (lm_node); - - for (i = uris; NULL != i; i = i->next) - { - _process_caps_uri (cache, from, (gchar *) i->data, handle, resource, - serial); - g_free (i->data); - } - - g_slist_free (uris); -} - -static LmHandlerResult -_parse_presence_message (GabblePresenceCache *cache, - TpHandle handle, - const gchar *from, - LmMessage *message) -{ - gint8 priority = 0; - const gchar *resource, *status_message = NULL; - LmMessageNode *presence_node, *child_node; - LmHandlerResult ret = LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; - GabblePresenceId presence_id; - GabblePresence *presence; - - presence_node = message->node; - g_assert (0 == strcmp (presence_node->name, "presence")); - - resource = strchr (from, '/'); - - presence = gabble_presence_cache_get (cache, handle); - - if (NULL != presence) - /* Once we've received presence from somebody, we don't need to keep the - * presence around when it's unavailable. */ - presence->keep_unavailable = FALSE; - - child_node = lm_message_node_get_child (presence_node, "status"); - - if (child_node) - status_message = lm_message_node_get_value (child_node); - - child_node = lm_message_node_get_child (presence_node, "priority"); - - if (child_node) - { - const gchar *prio = lm_message_node_get_value (child_node); - - if (prio != NULL) - priority = CLAMP (atoi (prio), G_MININT8, G_MAXINT8); - } - - switch (lm_message_get_sub_type (message)) - { - case LM_MESSAGE_SUB_TYPE_NOT_SET: - case LM_MESSAGE_SUB_TYPE_AVAILABLE: - presence_id = _presence_node_get_status (presence_node); - gabble_presence_cache_update (cache, handle, resource, presence_id, - status_message, priority); - - _grab_nickname (cache, handle, from, presence_node); - _grab_avatar_sha1 (cache, handle, from, presence_node); - _process_caps (cache, handle, from, presence_node); - - ret = LM_HANDLER_RESULT_REMOVE_MESSAGE; - break; - - case LM_MESSAGE_SUB_TYPE_ERROR: - NODE_DEBUG (presence_node, "setting contact offline due to error"); - /* fall through */ - - case LM_MESSAGE_SUB_TYPE_UNAVAILABLE: - gabble_presence_cache_update (cache, handle, resource, - GABBLE_PRESENCE_OFFLINE, status_message, priority); - - ret = LM_HANDLER_RESULT_REMOVE_MESSAGE; - break; - - default: - break; - } - - return ret; -} - -static LmHandlerResult -_parse_message_message (GabblePresenceCache *cache, - TpHandle handle, - const gchar *from, - LmMessage *message) -{ - LmMessageNode *node; - GabblePresence *presence; - - presence = gabble_presence_cache_get (cache, handle); - - if (NULL == presence) - { - presence = _cache_insert (cache, handle); - presence->keep_unavailable = TRUE; - } - - node = lm_message_get_node (message); - - _grab_nickname (cache, handle, from, node); - _grab_avatar_sha1 (cache, handle, from, node); - _process_caps (cache, handle, from, node); - - return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; -} - - -/** - * gabble_presence_cache_lm_message_cb: - * @handler: #LmMessageHandler for this message - * @connection: #LmConnection that originated the message - * @message: the presence message - * @user_data: callback data - * - * Called by loudmouth when we get an incoming <presence>. - */ -static LmHandlerResult -gabble_presence_cache_lm_message_cb (LmMessageHandler *handler, - LmConnection *lmconn, - LmMessage *message, - gpointer user_data) -{ - GabblePresenceCache *cache = GABBLE_PRESENCE_CACHE (user_data); - GabblePresenceCachePrivate *priv = GABBLE_PRESENCE_CACHE_PRIV (cache); - TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( - (TpBaseConnection *)priv->conn, TP_HANDLE_TYPE_CONTACT); - const char *from; - LmHandlerResult ret; - TpHandle handle; - - g_assert (lmconn == priv->conn->lmconn); - - from = lm_message_node_get_attribute (message->node, "from"); - - if (NULL == from) - { - NODE_DEBUG (message->node, "message without from attribute, ignoring"); - return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; - } - - handle = tp_handle_ensure (contact_repo, from, NULL, NULL); - - if (0 == handle) - { - NODE_DEBUG (message->node, "ignoring message from malformed jid"); - return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; - } - - switch (lm_message_get_type (message)) - { - case LM_MESSAGE_TYPE_PRESENCE: - ret = _parse_presence_message (cache, handle, from, message); - break; - case LM_MESSAGE_TYPE_MESSAGE: - ret = _parse_message_message (cache, handle, from, message); - break; - default: - ret = LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; - break; - } - - tp_handle_unref (contact_repo, handle); - return ret; -} - - -GabblePresenceCache * -gabble_presence_cache_new (GabbleConnection *conn) -{ - return g_object_new (GABBLE_TYPE_PRESENCE_CACHE, - "connection", conn, - NULL); -} - -GabblePresence * -gabble_presence_cache_get (GabblePresenceCache *cache, TpHandle handle) -{ - GabblePresenceCachePrivate *priv = GABBLE_PRESENCE_CACHE_PRIV (cache); - TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( - (TpBaseConnection *)priv->conn, TP_HANDLE_TYPE_CONTACT); - - g_assert (tp_handle_is_valid (contact_repo, handle, NULL)); - - return g_hash_table_lookup (priv->presence, GINT_TO_POINTER (handle)); -} - -void -gabble_presence_cache_maybe_remove ( - GabblePresenceCache *cache, - TpHandle handle) -{ - GabblePresenceCachePrivate *priv = GABBLE_PRESENCE_CACHE_PRIV (cache); - TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( - (TpBaseConnection *)priv->conn, TP_HANDLE_TYPE_CONTACT); - GabblePresence *presence; - - presence = gabble_presence_cache_get (cache, handle); - - if (NULL == presence) - return; - - if (presence->status == GABBLE_PRESENCE_OFFLINE && - presence->status_message == NULL && - !presence->keep_unavailable) - { - const gchar *jid; - - jid = tp_handle_inspect (contact_repo, handle); - DEBUG ("discarding cached presence for unavailable jid %s", jid); - g_hash_table_remove (priv->presence, GINT_TO_POINTER (handle)); - tp_handle_set_remove (priv->presence_handles, handle); - } -} - -static GabblePresence * -_cache_insert ( - GabblePresenceCache *cache, - TpHandle handle) -{ - GabblePresenceCachePrivate *priv = GABBLE_PRESENCE_CACHE_PRIV (cache); - GabblePresence *presence; - - presence = gabble_presence_new (); - g_hash_table_insert (priv->presence, GINT_TO_POINTER (handle), presence); - tp_handle_set_add (priv->presence_handles, handle); - return presence; -} - -void -gabble_presence_cache_update ( - GabblePresenceCache *cache, - TpHandle handle, - const gchar *resource, - GabblePresenceId presence_id, - const gchar *status_message, - gint8 priority) -{ - GabblePresenceCachePrivate *priv = GABBLE_PRESENCE_CACHE_PRIV (cache); - TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( - (TpBaseConnection *)priv->conn, TP_HANDLE_TYPE_CONTACT); - const gchar *jid; - GabblePresence *presence; - - jid = tp_handle_inspect (contact_repo, handle); - DEBUG ("%s (%d) resource %s prio %d presence %d message \"%s\"", - jid, handle, resource, priority, presence_id, status_message); - - presence = gabble_presence_cache_get (cache, handle); - - if (presence == NULL) - presence = _cache_insert (cache, handle); - - if (gabble_presence_update (presence, resource, presence_id, status_message, - priority)) - g_signal_emit (cache, signals[PRESENCE_UPDATE], 0, handle); - - gabble_presence_cache_maybe_remove (cache, handle); -} - -void gabble_presence_cache_add_bundle_caps (GabblePresenceCache *cache, - const gchar *node, GabblePresenceCapabilities new_caps) -{ - CapabilityInfo *info; - - info = capability_info_get (cache, node, 0); - info->trust = CAPABILITY_BUNDLE_ENOUGH_TRUST; - info->caps |= new_caps; -} - diff --git a/src/presence-cache.h b/src/presence-cache.h deleted file mode 100644 index 9b9b5225e..000000000 --- a/src/presence-cache.h +++ /dev/null @@ -1,84 +0,0 @@ -/* - * gabble-presence-cache.h - Headers for Gabble's contact presence cache - * Copyright (C) 2005 Collabora Ltd. - * Copyright (C) 2005 Nokia Corporation - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef __GABBLE_PRESENCE_CACHE_H__ -#define __GABBLE_PRESENCE_CACHE_H__ - -#include <glib-object.h> - -#include "presence.h" - -G_BEGIN_DECLS - -#define GABBLE_TYPE_PRESENCE_CACHE gabble_presence_cache_get_type () - -#define GABBLE_PRESENCE_CACHE(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ - GABBLE_TYPE_PRESENCE_CACHE, GabblePresenceCache)) - -#define GABBLE_PRESENCE_CACHE_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST ((klass), \ - GABBLE_TYPE_PRESENCE_CACHE, GabblePresenceCacheClass)) - -#define GABBLE_IS_PRESENCE_CACHE(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ - GABBLE_TYPE_PRESENCE_CACHE)) - -#define GABBLE_IS_PRESENCE_CACHE_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE ((klass), \ - GABBLE_TYPE_PRESENCE_CACHE)) - -#define GABBLE_PRESENCE_CACHE_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS ((obj), \ - GABBLE_TYPE_PRESENCE_CACHE, GabblePresenceCacheClass)) - -#define JABBER_PRESENCE_SHOW_AWAY "away" -#define JABBER_PRESENCE_SHOW_CHAT "chat" -#define JABBER_PRESENCE_SHOW_DND "dnd" -#define JABBER_PRESENCE_SHOW_XA "xa" - -struct _GabblePresenceCache { - GObject parent; - gpointer priv; -}; - -typedef struct _GabblePresenceCacheClass GabblePresenceCacheClass; - -struct _GabblePresenceCacheClass { - GObjectClass parent_class; -}; - -GType gabble_presence_cache_get_type (void); - -GabblePresenceCache *gabble_presence_cache_new (GabbleConnection *conn); -GabblePresence *gabble_presence_cache_get (GabblePresenceCache *cache, - TpHandle handle); -void gabble_presence_cache_update (GabblePresenceCache *cache, - TpHandle handle, const gchar *resource, GabblePresenceId presence_id, - const gchar *status_message, gint8 priority); -void gabble_presence_cache_maybe_remove (GabblePresenceCache *cache, - TpHandle handle); -void gabble_presence_cache_add_bundle_caps (GabblePresenceCache *cache, - const gchar *node, GabblePresenceCapabilities caps); - -G_END_DECLS - -#endif /* __GABBLE_PRESENCE_CACHE_H__ */ - diff --git a/src/presence.c b/src/presence.c deleted file mode 100644 index 35e1d7e29..000000000 --- a/src/presence.c +++ /dev/null @@ -1,429 +0,0 @@ -/* - * gabble-presence.c - Gabble's per-contact presence structure - * Copyright (C) 2005 Collabora Ltd. - * Copyright (C) 2005 Nokia Corporation - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "presence.h" - -#include <string.h> -#include <glib.h> - -#include "presence-cache.h" -#include "namespaces.h" -#include "util.h" - -#include "config.h" -#define DEBUG_FLAG GABBLE_DEBUG_PRESENCE -#include "debug.h" - -G_DEFINE_TYPE (GabblePresence, gabble_presence, G_TYPE_OBJECT); - -#define GABBLE_PRESENCE_PRIV(account) ((GabblePresencePrivate *)account->priv) - -typedef struct _Resource Resource; - -struct _Resource { - gchar *name; - GabblePresenceCapabilities caps; - guint caps_serial; - GabblePresenceId status; - gchar *status_message; - gint8 priority; -}; - -typedef struct _GabblePresencePrivate GabblePresencePrivate; - -struct _GabblePresencePrivate { - gchar *no_resource_status_message; - GSList *resources; -}; - -static Resource * -_resource_new (gchar *name) -{ - Resource *new = g_slice_new (Resource); - new->name = name; - new->caps = PRESENCE_CAP_NONE; - new->status = GABBLE_PRESENCE_OFFLINE; - new->status_message = NULL; - new->priority = 0; - new->caps_serial = 0; - - return new; -} - -static void -_resource_free (Resource *resource) -{ - g_free (resource->name); - g_free (resource->status_message); - g_slice_free (Resource, resource); -} - -static void -gabble_presence_finalize (GObject *object) -{ - GSList *i; - GabblePresence *presence = GABBLE_PRESENCE (object); - GabblePresencePrivate *priv = GABBLE_PRESENCE_PRIV (presence); - - for (i = priv->resources; NULL != i; i = i->next) - _resource_free (i->data); - - g_slist_free (priv->resources); - g_free (presence->nickname); - g_free (presence->avatar_sha1); - g_free (priv->no_resource_status_message); -} - -static void -gabble_presence_class_init (GabblePresenceClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - g_type_class_add_private (object_class, sizeof (GabblePresencePrivate)); - object_class->finalize = gabble_presence_finalize; -} - -static void -gabble_presence_init (GabblePresence *self) -{ - self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, - GABBLE_TYPE_PRESENCE, GabblePresencePrivate); - ((GabblePresencePrivate *)self->priv)->resources = NULL; -} - -GabblePresence* -gabble_presence_new (void) -{ - return g_object_new (GABBLE_TYPE_PRESENCE, NULL); -} - -const gchar * -gabble_presence_pick_resource_by_caps ( - GabblePresence *presence, - GabblePresenceCapabilities caps) -{ - GabblePresencePrivate *priv = GABBLE_PRESENCE_PRIV (presence); - GSList *i; - Resource *chosen = NULL; - - for (i = priv->resources; NULL != i; i = i->next) - { - Resource *res = (Resource *) i->data; - - if ((res->priority >= 0) && - ((res->caps & caps) == caps) && - (NULL == chosen || res->priority > chosen->priority)) - chosen = res; - } - - if (chosen) - return chosen->name; - else - return NULL; -} - -gboolean -gabble_presence_resource_has_caps (GabblePresence *presence, - const gchar *resource, - GabblePresenceCapabilities caps) -{ - GabblePresencePrivate *priv = GABBLE_PRESENCE_PRIV (presence); - GSList *i; - - for (i = priv->resources; NULL != i; i = i->next) - { - Resource *res = (Resource *) i->data; - - if (!tp_strdiff (res->name, resource) && (res->caps & caps)) - return TRUE; - } - - return FALSE; -} - -void -gabble_presence_set_capabilities (GabblePresence *presence, - const gchar *resource, - GabblePresenceCapabilities caps, - guint serial) -{ - GabblePresencePrivate *priv = GABBLE_PRESENCE_PRIV (presence); - GSList *i; - - presence->caps = 0; - - DEBUG ("about to add caps %u to resource %s with serial %u", caps, resource, - serial); - - for (i = priv->resources; NULL != i; i = i->next) - { - Resource *tmp = (Resource *) i->data; - - if (0 == strcmp (tmp->name, resource)) - { - DEBUG ("found resource %s", resource); - - if (serial > tmp->caps_serial) - { - DEBUG ("new serial %u, old %u, clearing caps", serial, - tmp->caps_serial); - tmp->caps = 0; - tmp->caps_serial = serial; - } - - if (serial >= tmp->caps_serial) - { - DEBUG ("adding caps %u to resource %s", caps, resource); - tmp->caps |= caps; - DEBUG ("resource %s caps now %u", resource, tmp->caps); - } - } - - presence->caps |= tmp->caps; - } - - DEBUG ("total caps now %u", presence->caps); -} - -static Resource * -_find_resource (GabblePresence *presence, const gchar *resource) -{ - GSList *i; - GabblePresencePrivate *priv = GABBLE_PRESENCE_PRIV (presence); - - for (i = priv->resources; NULL != i; i = i->next) - { - Resource *res = (Resource *) i->data; - - if (0 == strcmp (res->name, resource)) - return res; - } - - return NULL; -} - -gboolean -gabble_presence_update (GabblePresence *presence, - const gchar *resource, - GabblePresenceId status, - const gchar *status_message, - gint8 priority) -{ - GabblePresencePrivate *priv = GABBLE_PRESENCE_PRIV (presence); - Resource *res; - GabblePresenceId old_status; - gchar *old_status_message; - GSList *i; - guint8 prio; - gboolean ret = FALSE; - - /* save our current state */ - old_status = presence->status; - old_status_message = g_strdup (presence->status_message); - - if (NULL == resource) - { - /* presence from a JID with no resource: free all resources and set - * presence directly */ - - for (i = priv->resources; i; i = i->next) - _resource_free (i->data); - - g_slist_free (priv->resources); - priv->resources = NULL; - - if (tp_strdiff (priv->no_resource_status_message, status_message)) - { - g_free (priv->no_resource_status_message); - priv->no_resource_status_message = g_strdup (status_message); - } - - presence->status = status; - presence->status_message = priv->no_resource_status_message; - goto OUT; - } - - res = _find_resource (presence, resource); - - /* remove, create or update a Resource as appropriate */ - if (GABBLE_PRESENCE_OFFLINE == status && - NULL == status_message) - { - if (NULL != res) - { - priv->resources = g_slist_remove (priv->resources, res); - _resource_free (res); - res = NULL; - } - } - else - { - if (NULL == res) - { - res = _resource_new (g_strdup (resource)); - priv->resources = g_slist_append (priv->resources, res); - } - - res->status = status; - - if (tp_strdiff (res->status_message, status_message)) - { - g_free (res->status_message); - res->status_message = g_strdup (status_message); - } - - res->priority = priority; - } - - /* select the most preferable Resource and update presence->* based on our - * choice */ - presence->caps = 0; - presence->status = GABBLE_PRESENCE_OFFLINE; - - /* use the status message from any offline Resource we're - * keeping around just because it has a message on it */ - presence->status_message = res ? res->status_message : NULL; - - prio = -128; - - for (i = priv->resources; NULL != i; i = i->next) - { - Resource *res = (Resource *) i->data; - - presence->caps |= res->caps; - - /* trump existing status & message if it's more present - * or has the same presence and a higher priority */ - if (res->status > presence->status || - (res->status == presence->status && res->priority > prio)) - { - presence->status = res->status; - presence->status_message = res->status_message; - prio = res->priority; - } - } - -OUT: - /* detect changes */ - if (presence->status != old_status || - tp_strdiff (presence->status_message, old_status_message)) - ret = TRUE; - - g_free (old_status_message); - return ret; -} - -LmMessage * -gabble_presence_as_message (GabblePresence *presence, const gchar *resource) -{ - LmMessage *message; - LmMessageNode *node, *subnode; - LmMessageSubType subtype; - Resource *res = _find_resource (presence, resource); - - g_assert (NULL != res); - - if (presence->status == GABBLE_PRESENCE_OFFLINE) - subtype = LM_MESSAGE_SUB_TYPE_UNAVAILABLE; - else - subtype = LM_MESSAGE_SUB_TYPE_AVAILABLE; - - message = lm_message_new_with_sub_type (NULL, LM_MESSAGE_TYPE_PRESENCE, - subtype); - node = lm_message_get_node (message); - - switch (presence->status) - { - case GABBLE_PRESENCE_AVAILABLE: - case GABBLE_PRESENCE_OFFLINE: - case GABBLE_PRESENCE_HIDDEN: - break; - case GABBLE_PRESENCE_AWAY: - lm_message_node_add_child (node, "show", JABBER_PRESENCE_SHOW_AWAY); - break; - case GABBLE_PRESENCE_CHAT: - lm_message_node_add_child (node, "show", JABBER_PRESENCE_SHOW_CHAT); - break; - case GABBLE_PRESENCE_DND: - lm_message_node_add_child (node, "show", JABBER_PRESENCE_SHOW_DND); - break; - case GABBLE_PRESENCE_XA: - lm_message_node_add_child (node, "show", JABBER_PRESENCE_SHOW_XA); - break; - default: - g_critical ("%s: Unexpected Telepathy presence type", G_STRFUNC); - break; - } - - if (presence->status_message) - lm_message_node_add_child (node, "status", presence->status_message); - - if (res->priority) - { - gchar *priority = g_strdup_printf ("%d", res->priority); - lm_message_node_add_child (node, "priority", priority); - g_free (priority); - } - - subnode = lm_message_node_add_child (node, "x", ""); - lm_message_node_set_attribute (subnode, "xmlns", NS_VCARD_TEMP_UPDATE); - /* NULL means we make no particular assertion about the avatar. */ - if (presence->avatar_sha1 != NULL) - { - lm_message_node_add_child (subnode, "photo", presence->avatar_sha1); - } - - return message; -} - -gchar * -gabble_presence_dump (GabblePresence *presence) -{ - GSList *i; - GString *ret = g_string_new (""); - GabblePresencePrivate *priv = GABBLE_PRESENCE_PRIV (presence); - - g_string_append_printf (ret, - "nickname: %s\n" - "accumulated status: %d\n" - "accumulated status msg: %s\n" - "accumulated capabilities: %d\n" - "kept while unavailable: %d\n" - "resources:\n", presence->nickname, presence->status, - presence->status_message, presence->caps, - presence->keep_unavailable); - - for (i = priv->resources; i; i = i->next) - { - Resource *res = (Resource *) i->data; - - g_string_append_printf (ret, - " %s\n" - " capabilities: %d\n" - " status: %d\n" - " status msg: %s\n" - " priority: %d\n", res->name, res->caps, res->status, - res->status_message, res->priority); - } - - if (priv->resources == NULL) - g_string_append_printf (ret, " (none)\n"); - - return g_string_free (ret, FALSE); -} diff --git a/src/presence.h b/src/presence.h deleted file mode 100644 index 5dedcb26c..000000000 --- a/src/presence.h +++ /dev/null @@ -1,106 +0,0 @@ -/* - * gabble-presence.h - Headers for Gabble's per-contact presence structure - * Copyright (C) 2005 Collabora Ltd. - * Copyright (C) 2005 Nokia Corporation - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - - -#ifndef __GABBLE_PRESENCE_H__ -#define __GABBLE_PRESENCE_H__ - -#include <glib-object.h> - -#include "gabble-connection.h" -#include "gabble-types.h" - -G_BEGIN_DECLS - -#define GABBLE_TYPE_PRESENCE gabble_presence_get_type () - -#define GABBLE_PRESENCE(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ - GABBLE_TYPE_PRESENCE, GabblePresence)) - -#define GABBLE_PRESENCE_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST ((klass), \ - GABBLE_TYPE_PRESENCE, GabblePresenceClass)) - -#define GABBLE_IS_PRESENCE(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ - GABBLE_TYPE_PRESENCE)) - -#define GABBLE_IS_PRESENCE_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE ((klass), \ - GABBLE_TYPE_PRESENCE)) - -#define GABBLE_PRESENCE_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS ((obj), \ - GABBLE_TYPE_PRESENCE, GabblePresenceClass)) - -typedef enum { - PRESENCE_CAP_NONE = 0, - PRESENCE_CAP_GOOGLE_TRANSPORT_P2P = 1 << 0, - PRESENCE_CAP_GOOGLE_VOICE = 1 << 1, - PRESENCE_CAP_JINGLE = 1 << 2, - PRESENCE_CAP_JINGLE_DESCRIPTION_AUDIO = 1 << 3, - PRESENCE_CAP_JINGLE_DESCRIPTION_VIDEO = 1 << 4, - PRESENCE_CAP_CHAT_STATES = 1 << 5, -} GabblePresenceCapabilities; - -struct _GabblePresence { - GObject parent; - GabblePresenceCapabilities caps; - GabblePresenceId status; - gchar *status_message; - gchar *nickname; - gchar *avatar_sha1; - gboolean keep_unavailable; - gpointer priv; -}; - -typedef struct _GabblePresenceClass GabblePresenceClass; - -struct _GabblePresenceClass { - GObjectClass parent_class; -}; - -GType gabble_presence_get_type (void); - -GabblePresence* gabble_presence_new (void); - -gboolean gabble_presence_update (GabblePresence *presence, - const gchar *resource, GabblePresenceId status, - const gchar *status_message, gint8 priority); - -void gabble_presence_set_capabilities (GabblePresence *presence, - const gchar *resource, GabblePresenceCapabilities caps, guint serial); - -const gchar *gabble_presence_pick_resource_by_caps (GabblePresence *presence, - GabblePresenceCapabilities caps); - -gboolean gabble_presence_resource_has_caps (GabblePresence *presence, - const gchar *resource, - GabblePresenceCapabilities caps); - -LmMessage *gabble_presence_as_message (GabblePresence *presence, - const gchar *resource); -gchar *gabble_presence_dump (GabblePresence *presence); - -G_END_DECLS - -#endif /* __GABBLE_PRESENCE_H__ */ - diff --git a/src/roster.c b/src/roster.c deleted file mode 100644 index 99de617bf..000000000 --- a/src/roster.c +++ /dev/null @@ -1,2345 +0,0 @@ -/* - * roster.c - Source for Gabble roster helper - * - * Copyright (C) 2006 Collabora Ltd. - * Copyright (C) 2006 Nokia Corporation - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "roster.h" - -#define DBUS_API_SUBJECT_TO_CHANGE - -#include <dbus/dbus-glib.h> -#include <string.h> - -#include <telepathy-glib/interfaces.h> -#include <telepathy-glib/channel-factory-iface.h> - -#define DEBUG_FLAG GABBLE_DEBUG_ROSTER - -#include "debug.h" -#include "gabble-connection.h" -#include "gabble-roster-channel.h" -#include "namespaces.h" -#include "util.h" - -#define GOOGLE_ROSTER_VERSION "2" - -/* Properties */ -enum -{ - PROP_CONNECTION = 1, - LAST_PROPERTY -}; - -/* signal enum */ -enum -{ - NICKNAME_UPDATE, - LAST_SIGNAL -}; - -static guint signals[LAST_SIGNAL] = { 0 }; - -typedef struct _GabbleRosterPrivate GabbleRosterPrivate; -struct _GabbleRosterPrivate -{ - GabbleConnection *conn; - - LmMessageHandler *iq_cb; - LmMessageHandler *presence_cb; - - GHashTable *list_channels; - GHashTable *group_channels; - GHashTable *items; - - gboolean roster_received; - gboolean dispose_has_run; -}; - -typedef enum -{ - GOOGLE_ITEM_TYPE_INVALID = -1, - GOOGLE_ITEM_TYPE_NORMAL = 0, - GOOGLE_ITEM_TYPE_BLOCKED, - GOOGLE_ITEM_TYPE_HIDDEN, - GOOGLE_ITEM_TYPE_PINNED, -} GoogleItemType; - -typedef struct _GabbleRosterItemEdit GabbleRosterItemEdit; -struct _GabbleRosterItemEdit -{ - /* if these are ..._INVALID, that means don't edit */ - GabbleRosterSubscription new_subscription; - GoogleItemType new_google_type; - /* owned by the item; if NULL, that means don't edit */ - gchar *new_name; - TpHandleSet *add_to_groups; - TpHandleSet *remove_from_groups; -}; - -typedef struct _GabbleRosterItem GabbleRosterItem; -struct _GabbleRosterItem -{ - GabbleRosterSubscription subscription; - gboolean ask_subscribe; - GoogleItemType google_type; - gchar *name; - TpHandleSet *groups; - /* if not NULL, an edit attempt is already "in-flight" so instead of - * sending off another, store required edits here until the one we - * already sent is acknowledged - this prevents some race conditions - */ - GabbleRosterItemEdit *unsent_edits; -}; - -static void gabble_roster_factory_iface_init (); -static void gabble_roster_init (GabbleRoster *roster); -static GObject * gabble_roster_constructor (GType type, guint n_props, - GObjectConstructParam *props); -static void gabble_roster_dispose (GObject *object); -static void gabble_roster_finalize (GObject *object); -static void gabble_roster_set_property (GObject *object, guint property_id, - const GValue *value, GParamSpec *pspec); -static void gabble_roster_get_property (GObject *object, guint property_id, - GValue *value, GParamSpec *pspec); - -static void _gabble_roster_item_free (GabbleRosterItem *item); -static void item_edit_free (GabbleRosterItemEdit *edits); - -G_DEFINE_TYPE_WITH_CODE (GabbleRoster, gabble_roster, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (TP_TYPE_CHANNEL_FACTORY_IFACE, - gabble_roster_factory_iface_init)); - -#define GABBLE_ROSTER_GET_PRIVATE(o) ((GabbleRosterPrivate*) ((o)->priv)); - -static void -gabble_roster_class_init (GabbleRosterClass *gabble_roster_class) -{ - GObjectClass *object_class = G_OBJECT_CLASS (gabble_roster_class); - GParamSpec *param_spec; - - g_type_class_add_private (gabble_roster_class, sizeof (GabbleRosterPrivate)); - - object_class->constructor = gabble_roster_constructor; - - object_class->dispose = gabble_roster_dispose; - object_class->finalize = gabble_roster_finalize; - - object_class->get_property = gabble_roster_get_property; - object_class->set_property = gabble_roster_set_property; - - param_spec = g_param_spec_object ("connection", "GabbleConnection object", - "Gabble connection object that owns this " - "XMPP roster object.", - GABBLE_TYPE_CONNECTION, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | - G_PARAM_STATIC_NICK | - G_PARAM_STATIC_BLURB); - g_object_class_install_property (object_class, - PROP_CONNECTION, - param_spec); - - signals[NICKNAME_UPDATE] = g_signal_new ( - "nickname-update", - G_TYPE_FROM_CLASS (gabble_roster_class), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, - g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); -} - -static void -gabble_roster_init (GabbleRoster *obj) -{ - GabbleRosterPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (obj, - GABBLE_TYPE_ROSTER, GabbleRosterPrivate); - - obj->priv = priv; - - priv->list_channels = g_hash_table_new_full (g_direct_hash, g_direct_equal, - NULL, g_object_unref); - - priv->group_channels = g_hash_table_new_full (g_direct_hash, g_direct_equal, - NULL, g_object_unref); - - priv->items = g_hash_table_new_full (g_direct_hash, g_direct_equal, - NULL, (GDestroyNotify) _gabble_roster_item_free); -} - -static GObject * -gabble_roster_constructor (GType type, guint n_props, - GObjectConstructParam *props) -{ - GObject *obj; - /* GabbleRosterPrivate *priv; */ - - obj = G_OBJECT_CLASS (gabble_roster_parent_class)-> - constructor (type, n_props, props); - /* priv = GABBLE_ROSTER_GET_PRIVATE (GABBLE_ROSTER (obj)); */ - - return obj; -} - -void -gabble_roster_dispose (GObject *object) -{ - GabbleRoster *self = GABBLE_ROSTER (object); - GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (self); - - if (priv->dispose_has_run) - return; - - DEBUG ("dispose called"); - - priv->dispose_has_run = TRUE; - - g_assert (priv->iq_cb == NULL); - g_assert (priv->presence_cb == NULL); - - tp_channel_factory_iface_close_all (TP_CHANNEL_FACTORY_IFACE (object)); - g_assert (priv->group_channels == NULL); - g_assert (priv->list_channels == NULL); - - if (G_OBJECT_CLASS (gabble_roster_parent_class)->dispose) - G_OBJECT_CLASS (gabble_roster_parent_class)->dispose (object); -} - -static void -item_handle_unref_foreach (gpointer key, gpointer data, gpointer user_data) -{ - TpHandle handle = (TpHandle) key; - GabbleRosterPrivate *priv = (GabbleRosterPrivate *) user_data; - TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( - (TpBaseConnection *)priv->conn, TP_HANDLE_TYPE_CONTACT); - - tp_handle_unref (contact_repo, handle); -} - -void -gabble_roster_finalize (GObject *object) -{ - GabbleRoster *self = GABBLE_ROSTER (object); - GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (self); - - DEBUG ("called with %p", object); - - g_hash_table_foreach (priv->items, item_handle_unref_foreach, priv); - g_hash_table_destroy (priv->items); - - G_OBJECT_CLASS (gabble_roster_parent_class)->finalize (object); -} - -static void -gabble_roster_get_property (GObject *object, - guint property_id, - GValue *value, - GParamSpec *pspec) -{ - GabbleRoster *roster = GABBLE_ROSTER (object); - GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster); - - switch (property_id) { - case PROP_CONNECTION: - g_value_set_object (value, priv->conn); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } -} - -static void -gabble_roster_set_property (GObject *object, - guint property_id, - const GValue *value, - GParamSpec *pspec) -{ - GabbleRoster *roster = GABBLE_ROSTER (object); - GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster); - - switch (property_id) { - case PROP_CONNECTION: - priv->conn = g_value_get_object (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } -} - -static void -_gabble_roster_item_free (GabbleRosterItem *item) -{ - g_assert (item != NULL); - - tp_handle_set_destroy (item->groups); - item_edit_free (item->unsent_edits); - g_free (item->name); - g_slice_free (GabbleRosterItem, item); -} - -static const gchar * -_subscription_to_string (GabbleRosterSubscription subscription) -{ - switch (subscription) - { - case GABBLE_ROSTER_SUBSCRIPTION_NONE: - return "none"; - case GABBLE_ROSTER_SUBSCRIPTION_FROM: - return "from"; - case GABBLE_ROSTER_SUBSCRIPTION_TO: - return "to"; - case GABBLE_ROSTER_SUBSCRIPTION_BOTH: - return "both"; - case GABBLE_ROSTER_SUBSCRIPTION_REMOVE: - return "remove"; - default: - g_assert_not_reached (); - return NULL; - } -} - -static GabbleRosterSubscription -_parse_item_subscription (LmMessageNode *item_node) -{ - const gchar *subscription; - - g_assert (item_node != NULL); - - subscription = lm_message_node_get_attribute (item_node, "subscription"); - - if (NULL == subscription || 0 == strcmp (subscription, "none")) - return GABBLE_ROSTER_SUBSCRIPTION_NONE; - else if (0 == strcmp (subscription, "from")) - return GABBLE_ROSTER_SUBSCRIPTION_FROM; - else if (0 == strcmp (subscription, "to")) - return GABBLE_ROSTER_SUBSCRIPTION_TO; - else if (0 == strcmp (subscription, "both")) - return GABBLE_ROSTER_SUBSCRIPTION_BOTH; - else if (0 == strcmp (subscription, "remove")) - return GABBLE_ROSTER_SUBSCRIPTION_REMOVE; - else - { - NODE_DEBUG (item_node, "got unexpected subscription value"); - return GABBLE_ROSTER_SUBSCRIPTION_NONE; - } -} - -static TpHandleSet * -_parse_item_groups (LmMessageNode *item_node, TpBaseConnection *conn) -{ - LmMessageNode *group_node; - TpHandleRepoIface *group_repo = tp_base_connection_get_handles ( - conn, TP_HANDLE_TYPE_GROUP); - TpHandleSet *groups = tp_handle_set_new (group_repo); - TpHandle handle; - - for (group_node = item_node->children; - NULL != group_node; - group_node = group_node->next) - { - if (0 != strcmp (group_node->name, "group")) - continue; - - if (NULL == group_node->value) - continue; - - handle = tp_handle_ensure (group_repo, group_node->value, NULL, NULL); - if (!handle) - continue; - tp_handle_set_add (groups, handle); - tp_handle_unref (group_repo, handle); - } - - return groups; -} - -static const gchar * -_google_item_type_to_string (GoogleItemType google_type) -{ - switch (google_type) - { - case GOOGLE_ITEM_TYPE_NORMAL: - return NULL; - case GOOGLE_ITEM_TYPE_BLOCKED: - return "B"; - case GOOGLE_ITEM_TYPE_HIDDEN: - return "H"; - case GOOGLE_ITEM_TYPE_PINNED: - return "P"; - case GOOGLE_ITEM_TYPE_INVALID: - g_assert_not_reached (); - return NULL; - } - - g_assert_not_reached (); - - return NULL; -} - -static GoogleItemType -_parse_google_item_type (LmMessageNode *item_node) -{ - const gchar *google_type; - - g_assert (item_node != NULL); - - google_type = lm_message_node_get_attribute (item_node, "gr:t"); - - if (NULL == google_type) - return GOOGLE_ITEM_TYPE_NORMAL; - else if (!tp_strdiff (google_type, "B")) - return GOOGLE_ITEM_TYPE_BLOCKED; - else if (!tp_strdiff (google_type, "H")) - return GOOGLE_ITEM_TYPE_HIDDEN; - else if (!tp_strdiff (google_type, "P")) - return GOOGLE_ITEM_TYPE_PINNED; - - NODE_DEBUG (item_node, "got unexpected google contact type value"); - - return GOOGLE_ITEM_TYPE_NORMAL; -} - -static gboolean -_google_roster_item_should_keep (LmMessageNode *item_node, - GabbleRosterItem *item) -{ - const gchar *attr; - - /* skip automatically subscribed Google roster items */ - attr = lm_message_node_get_attribute (item_node, "gr:autosub"); - - if (!tp_strdiff (attr, "true")) - return FALSE; - - /* skip email addresses that replied to an invite */ - attr = lm_message_node_get_attribute (item_node, "gr:alias-for"); - - if (attr != NULL) - return FALSE; - - /* allow items that we've requested a subscription from */ - if (item->ask_subscribe) - return TRUE; - - if (item->subscription != GABBLE_ROSTER_SUBSCRIPTION_NONE) - return TRUE; - - /* discard anything else */ - return FALSE; -} - -static GabbleRosterItem * -_gabble_roster_item_get (GabbleRoster *roster, - TpHandle handle) -{ - GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster); - TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( - (TpBaseConnection *)priv->conn, TP_HANDLE_TYPE_CONTACT); - TpHandleRepoIface *group_repo = tp_base_connection_get_handles ( - (TpBaseConnection *)priv->conn, TP_HANDLE_TYPE_GROUP); - GabbleRosterItem *item; - - g_assert (roster != NULL); - g_assert (GABBLE_IS_ROSTER (roster)); - g_assert (tp_handle_is_valid (contact_repo, handle, NULL)); - - item = g_hash_table_lookup (priv->items, GINT_TO_POINTER (handle)); - - if (NULL == item) - { - item = g_slice_new0 (GabbleRosterItem); - item->groups = tp_handle_set_new (group_repo); - tp_handle_ref (contact_repo, handle); - g_hash_table_insert (priv->items, GINT_TO_POINTER (handle), item); - } - - return item; -} - -static void -_gabble_roster_item_remove (GabbleRoster *roster, - TpHandle handle) -{ - GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster); - TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( - (TpBaseConnection *)priv->conn, TP_HANDLE_TYPE_CONTACT); - - g_assert (roster != NULL); - g_assert (GABBLE_IS_ROSTER (roster)); - g_assert (tp_handle_is_valid (contact_repo, handle, NULL)); - - g_hash_table_remove (priv->items, GINT_TO_POINTER (handle)); - tp_handle_unref (contact_repo, handle); -} - -/* the TpHandleType must be GROUP or LIST */ -static GabbleRosterChannel *_gabble_roster_get_channel (GabbleRoster *, - TpHandleType, TpHandle, gboolean *created); - -typedef struct -{ - GHashTable *group_mem_updates; - guint contact_handle; -} GroupsUpdateContext; - -typedef struct -{ - TpIntSet *contacts_added; - TpIntSet *contacts_removed; -#ifdef ENABLE_DEBUG - guint group_handle; -#endif -} GroupMembershipUpdate; - -static GroupMembershipUpdate * -group_mem_update_ensure (GroupsUpdateContext *ctx, TpHandle group_handle) -{ - GroupMembershipUpdate *update = g_hash_table_lookup (ctx->group_mem_updates, - GUINT_TO_POINTER (group_handle)); - - if (update) return update; - - DEBUG ("Creating new hash table entry for group#%u", group_handle); - update = g_slice_new0 (GroupMembershipUpdate); -#ifdef ENABLE_DEBUG - update->group_handle = group_handle; -#endif - update->contacts_added = tp_intset_new (); - update->contacts_removed = tp_intset_new (); - g_hash_table_insert (ctx->group_mem_updates, - GUINT_TO_POINTER (group_handle), - update); - return update; -} - -static void -_update_add_to_group (guint group_handle, gpointer user_data) -{ - GroupsUpdateContext *ctx = (GroupsUpdateContext *)user_data; - GroupMembershipUpdate *update = group_mem_update_ensure (ctx, group_handle); - - DEBUG ("- contact#%u added to group#%u", ctx->contact_handle, - group_handle); - tp_intset_add (update->contacts_added, ctx->contact_handle); -} - -static void -_update_remove_from_group (guint group_handle, gpointer user_data) -{ - GroupsUpdateContext *ctx = (GroupsUpdateContext *)user_data; - GroupMembershipUpdate *update = group_mem_update_ensure (ctx, group_handle); - - DEBUG ("- contact#%u removed from group#%u", ctx->contact_handle, - group_handle); - tp_intset_add (update->contacts_removed, ctx->contact_handle); -} - -static GabbleRosterItem * -_gabble_roster_item_update (GabbleRoster *roster, - TpHandle contact_handle, - LmMessageNode *node, - GHashTable *group_updates, - gboolean google_roster_mode) -{ - GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster); - TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( - (TpBaseConnection *)priv->conn, TP_HANDLE_TYPE_CONTACT); - GabbleRosterItem *item; - const gchar *ask, *name; - TpIntSet *old_groups, *new_groups, *added_to, *removed_from, *removed_from2; - TpHandleSet *new_groups_handle_set; - GroupsUpdateContext ctx = { group_updates, contact_handle }; - - g_assert (roster != NULL); - g_assert (GABBLE_IS_ROSTER (roster)); - g_assert (tp_handle_is_valid (contact_repo, contact_handle, NULL)); - g_assert (node != NULL); - - item = _gabble_roster_item_get (roster, contact_handle); - - item->subscription = _parse_item_subscription (node); - - ask = lm_message_node_get_attribute (node, "ask"); - if (NULL != ask && 0 == strcmp (ask, "subscribe")) - item->ask_subscribe = TRUE; - else - item->ask_subscribe = FALSE; - - if (google_roster_mode) - { - item->google_type = _parse_google_item_type (node); - - /* discard roster item if strange, just hide it if it's hidden */ - if (item->google_type == GOOGLE_ITEM_TYPE_HIDDEN) - { - DEBUG ("Google roster: caching hidden contact %d (%s)", - contact_handle, - lm_message_node_get_attribute (node, "jid")); - item->subscription = GABBLE_ROSTER_SUBSCRIPTION_NONE; - } - else if (!_google_roster_item_should_keep (node, item)) - { - DEBUG ("Google roster: discarding odd contact %d (%s)", - contact_handle, - lm_message_node_get_attribute (node, "jid")); - item->subscription = GABBLE_ROSTER_SUBSCRIPTION_REMOVE; - } - } - - if (item->subscription == GABBLE_ROSTER_SUBSCRIPTION_REMOVE) - name = NULL; - else - name = lm_message_node_get_attribute (node, "name"); - - if (tp_strdiff (item->name, name)) - { - g_free (item->name); - item->name = g_strdup (name); - - DEBUG ("name for contact#%u changed to %s", contact_handle, - name); - g_signal_emit (G_OBJECT (roster), signals[NICKNAME_UPDATE], 0, - contact_handle); - } - - old_groups = tp_handle_set_peek (item->groups); /* borrowed */ - new_groups_handle_set = _parse_item_groups (node, - (TpBaseConnection *)priv->conn); - new_groups = tp_handle_set_peek (new_groups_handle_set); - - removed_from = tp_intset_difference (old_groups, new_groups); - added_to = tp_handle_set_update (item->groups, new_groups); - removed_from2 = tp_handle_set_difference_update (item->groups, removed_from); - - DEBUG ("Checking which groups contact#%u was just added to:", - contact_handle); - tp_intset_foreach (added_to, _update_add_to_group, &ctx); - DEBUG ("Checking which groups contact#%u was just removed from:", - contact_handle); - tp_intset_foreach (removed_from, _update_remove_from_group, &ctx); - - tp_intset_destroy (added_to); - tp_intset_destroy (removed_from); - tp_intset_destroy (removed_from2); - new_groups = NULL; - tp_handle_set_destroy (new_groups_handle_set); - - return item; -} - - -#ifdef ENABLE_DEBUG -static void -_gabble_roster_item_dump_group (guint handle, gpointer user_data) -{ - g_string_append_printf ((GString *)user_data, "group#%u ", handle); -} - -static gchar * -_gabble_roster_item_dump (GabbleRosterItem *item) -{ - GString *str; - - g_assert (item != NULL); - - str = g_string_new ("subscription: "); - - g_string_append (str, _subscription_to_string (item->subscription)); - - if (item->ask_subscribe) - g_string_append (str, ", ask: subscribe"); - - if (item->google_type != GOOGLE_ITEM_TYPE_NORMAL) - g_string_append_printf (str, ", google_type: %s", - _google_item_type_to_string (item->google_type)); - - if (item->name) - g_string_append_printf (str, ", name: %s", item->name); - - if (item->groups) - { - tp_intset_foreach (tp_handle_set_peek (item->groups), - _gabble_roster_item_dump_group, str); - } - - return g_string_free (str, FALSE); -} -#endif /* ENABLE_DEBUG */ - - -static LmMessage * -_gabble_roster_message_new (GabbleRoster *roster, - LmMessageSubType sub_type, - LmMessageNode **query_return) -{ - GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster); - LmMessage *message; - LmMessageNode *query_node; - - g_assert (roster != NULL); - g_assert (GABBLE_IS_ROSTER (roster)); - - message = lm_message_new_with_sub_type (NULL, - LM_MESSAGE_TYPE_IQ, - sub_type); - - query_node = lm_message_node_add_child (message->node, "query", NULL); - - if (NULL != query_return) - *query_return = query_node; - - lm_message_node_set_attribute (query_node, "xmlns", NS_ROSTER); - - if (priv->conn->features & GABBLE_CONNECTION_FEATURES_GOOGLE_ROSTER) - { - lm_message_node_set_attributes (query_node, - "xmlns:gr", NS_GOOGLE_ROSTER, - "gr:ext", GOOGLE_ROSTER_VERSION, - "gr:include", "all", - NULL); - } - - return message; -} - - -struct _ItemToMessageContext { - TpBaseConnection *conn; - LmMessageNode *item_node; -}; - -static void -_gabble_roster_item_put_group_in_message (guint handle, gpointer user_data) -{ - struct _ItemToMessageContext *ctx = - (struct _ItemToMessageContext *)user_data; - TpHandleRepoIface *group_repo = tp_base_connection_get_handles ( - ctx->conn, TP_HANDLE_TYPE_GROUP); - const char *name = tp_handle_inspect (group_repo, handle); - - lm_message_node_add_child (ctx->item_node, "group", name); -} - -/* Return a message representing the current state of the item for contact - * @handle on the roster @roster. - * - * If item_return is not NULL, populate it with the <item/> node. - * - * If item is not NULL, it represents the state we would like the contact's - * roster item to have - use it instead of the contact's actual roster item - * when composing the message. - */ -static LmMessage * -_gabble_roster_item_to_message (GabbleRoster *roster, - TpHandle handle, - LmMessageNode **item_return, - GabbleRosterItem *item) -{ - GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster); - TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( - (TpBaseConnection *)priv->conn, TP_HANDLE_TYPE_CONTACT); - LmMessage *message; - LmMessageNode *query_node, *item_node; - const gchar *jid; - struct _ItemToMessageContext ctx = { - (TpBaseConnection *)priv->conn, - }; - - g_assert (roster != NULL); - g_assert (GABBLE_IS_ROSTER (roster)); - g_assert (tp_handle_is_valid (contact_repo, handle, NULL)); - - if (!item) - item = _gabble_roster_item_get (roster, handle); - - message = _gabble_roster_message_new (roster, LM_MESSAGE_SUB_TYPE_SET, - &query_node); - - item_node = lm_message_node_add_child (query_node, "item", NULL); - ctx.item_node = item_node; - - if (NULL != item_return) - *item_return = item_node; - - jid = tp_handle_inspect (contact_repo, handle); - lm_message_node_set_attribute (item_node, "jid", jid); - - if (item->subscription != GABBLE_ROSTER_SUBSCRIPTION_NONE) - { - const gchar *subscription = _subscription_to_string (item->subscription); - lm_message_node_set_attribute (item_node, "subscription", subscription); - } - - if (item->subscription == GABBLE_ROSTER_SUBSCRIPTION_REMOVE) - goto DONE; - - if ((priv->conn->features & GABBLE_CONNECTION_FEATURES_GOOGLE_ROSTER) && - item->google_type != GOOGLE_ITEM_TYPE_NORMAL) - lm_message_node_set_attribute (item_node, "gr:t", - _google_item_type_to_string (item->google_type)); - - if (item->ask_subscribe) - lm_message_node_set_attribute (item_node, "ask", "subscribe"); - - if (item->name) - lm_message_node_set_attribute (item_node, "name", item->name); - - if (item->groups) - { - tp_intset_foreach (tp_handle_set_peek (item->groups), - _gabble_roster_item_put_group_in_message, - (void *)&ctx); - } - -DONE: - return message; -} - -static GabbleRosterChannel * -_gabble_roster_create_channel (GabbleRoster *roster, - guint handle_type, - TpHandle handle) -{ - GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster); - TpBaseConnection *conn = (TpBaseConnection *)priv->conn; - TpHandleRepoIface *handle_repo = tp_base_connection_get_handles (conn, - handle_type); - GabbleRosterChannel *chan; - const char *name; - char *mangled_name; - char *object_path; - GHashTable *channels = (handle_type == TP_HANDLE_TYPE_LIST - ? priv->list_channels - : priv->group_channels); - - /* if this assertion succeeds, we know we have the right handle repo */ - g_assert (handle_type == TP_HANDLE_TYPE_LIST || - handle_type == TP_HANDLE_TYPE_GROUP); - g_assert (channels != NULL); - g_assert (g_hash_table_lookup (channels, GINT_TO_POINTER (handle)) == NULL); - - name = tp_handle_inspect (handle_repo, handle); - DEBUG ("Instantiating channel %u:%u \"%s\"", handle_type, handle, name); - mangled_name = tp_escape_as_identifier (name); - object_path = g_strdup_printf ("%s/RosterChannel/%s/%s", - conn->object_path, - handle_type == TP_HANDLE_TYPE_LIST ? "List" - : "Group", - mangled_name); - g_free (mangled_name); - mangled_name = NULL; - - chan = g_object_new (GABBLE_TYPE_ROSTER_CHANNEL, - "connection", priv->conn, - "object-path", object_path, - "handle", handle, - "handle-type", handle_type, - NULL); - - DEBUG ("created %s", object_path); - - g_hash_table_insert (channels, GINT_TO_POINTER (handle), chan); - - if (priv->roster_received) - { - DEBUG ("roster already received, emitting signal for %s", - object_path); - - tp_channel_factory_iface_emit_new_channel (roster, - (TpChannelIface *)chan, NULL); - } - else - { - DEBUG ("roster not yet received, not emitting signal for %s list " - "channel", name); - } - g_free (object_path); - - return chan; -} - -static GabbleRosterChannel * -_gabble_roster_get_channel (GabbleRoster *roster, - guint handle_type, - TpHandle handle, - gboolean *created) -{ - GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster); - TpHandleRepoIface *handle_repo = tp_base_connection_get_handles ( - (TpBaseConnection *)priv->conn, handle_type); - GabbleRosterChannel *chan; - GHashTable *channels = (handle_type == TP_HANDLE_TYPE_LIST - ? priv->list_channels - : priv->group_channels); - - /* if this assertion succeeds, we know we have the right handle repos */ - g_assert (handle_type == TP_HANDLE_TYPE_LIST || - handle_type == TP_HANDLE_TYPE_GROUP); - g_assert (channels != NULL); - g_assert (tp_handle_is_valid (handle_repo, handle, NULL)); - - DEBUG ("Looking up channel %u:%u \"%s\"", handle_type, handle, - tp_handle_inspect (handle_repo, handle)); - chan = g_hash_table_lookup (channels, GINT_TO_POINTER (handle)); - - if (chan == NULL) - { - if (created) - *created = TRUE; - chan = _gabble_roster_create_channel (roster, handle_type, handle); - } - else - { - if (created) - *created = FALSE; - } - - return chan; -} - -struct _EmitOneData { - GabbleRoster *roster; - guint handle_type; /* must be GROUP or LIST */ -}; - -static void -_gabble_roster_emit_one (gpointer key, - gpointer value, - gpointer data) -{ - struct _EmitOneData *data_struct = (struct _EmitOneData *)data; - GabbleRoster *roster = data_struct->roster; - GabbleRosterChannel *chan = GABBLE_ROSTER_CHANNEL (value); -#ifdef ENABLE_DEBUG - GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster); - TpHandleRepoIface *handle_repo = tp_base_connection_get_handles ( - (TpBaseConnection *)priv->conn, data_struct->handle_type); - TpHandle handle = GPOINTER_TO_INT (key); - const gchar *name; - - g_assert (data_struct->handle_type == TP_HANDLE_TYPE_GROUP || - data_struct->handle_type == TP_HANDLE_TYPE_LIST); - g_assert (handle_repo != NULL); - name = tp_handle_inspect (handle_repo, handle); - - DEBUG ("roster now received, emitting signal signal for %s list channel", - name); -#endif - - tp_channel_factory_iface_emit_new_channel (roster, (TpChannelIface *)chan, - NULL); -} - -static void -_gabble_roster_received (GabbleRoster *roster) -{ - GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster); - - g_assert (priv->list_channels != NULL); - - if (!priv->roster_received) - { - struct _EmitOneData data = { roster, TP_HANDLE_TYPE_LIST }; - - priv->roster_received = TRUE; - - g_hash_table_foreach (priv->list_channels, _gabble_roster_emit_one, - &data); - data.handle_type = TP_HANDLE_TYPE_GROUP; - g_hash_table_foreach (priv->group_channels, _gabble_roster_emit_one, - &data); - } -} - -static void -_group_mem_update_destroy (GroupMembershipUpdate *update) -{ - tp_intset_destroy (update->contacts_added); - tp_intset_destroy (update->contacts_removed); - g_slice_free (GroupMembershipUpdate, update); -} - -static gboolean -_update_group (gpointer key, gpointer value, gpointer user_data) -{ - guint group_handle = GPOINTER_TO_UINT (key); - GabbleRoster *roster = GABBLE_ROSTER (user_data); - GroupMembershipUpdate *update = (GroupMembershipUpdate *)value; - GabbleRosterChannel *group_channel = _gabble_roster_get_channel ( - roster, TP_HANDLE_TYPE_GROUP, group_handle, NULL); - TpIntSet *empty = tp_intset_new (); - -#ifdef ENABLE_DEBUG - g_assert (group_handle == update->group_handle); -#endif - - DEBUG ("Updating group channel %u now message has been received", - group_handle); - tp_group_mixin_change_members ((GObject *)group_channel, - "", update->contacts_added, update->contacts_removed, empty, empty, - 0, 0); - - tp_intset_destroy (empty); - - return TRUE; -} - -/** - * gabble_roster_iq_cb - * - * Called by loudmouth when we get an incoming <iq>. This handler - * is concerned only with roster queries, and allows other handlers - * if queries other than rosters are received. - */ -static LmHandlerResult -gabble_roster_iq_cb (LmMessageHandler *handler, - LmConnection *lmconn, - LmMessage *message, - gpointer user_data) -{ - GabbleRoster *roster = GABBLE_ROSTER (user_data); - GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster); - TpBaseConnection *conn = (TpBaseConnection *)priv->conn; - TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (conn, - TP_HANDLE_TYPE_CONTACT); - LmMessageNode *iq_node, *query_node; - LmMessageSubType sub_type; - const gchar *from; - gboolean google_roster = FALSE; - - g_assert (lmconn == priv->conn->lmconn); - - if (priv->list_channels == NULL) - return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; - - iq_node = lm_message_get_node (message); - query_node = lm_message_node_get_child_with_namespace (iq_node, "query", - NS_ROSTER); - - if (query_node == NULL) - return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; - - from = lm_message_node_get_attribute (message->node, "from"); - - if (from != NULL) - { - TpHandle sender; - - sender = tp_handle_lookup (contact_repo, from, NULL, NULL); - - if (sender != conn->self_handle) - { - NODE_DEBUG (iq_node, "discarding roster IQ which is not from " - "ourselves or the server"); - return LM_HANDLER_RESULT_REMOVE_MESSAGE; - } - } - - if (priv->conn->features & GABBLE_CONNECTION_FEATURES_GOOGLE_ROSTER) - { - const char *gr_ext; - - gr_ext = lm_message_node_get_attribute (query_node, "gr:ext"); - - if (!tp_strdiff (gr_ext, GOOGLE_ROSTER_VERSION)) - google_roster = TRUE; - } - - sub_type = lm_message_get_sub_type (message); - - /* if this is a result, it's from our initial query. if it's a set, - * it's a roster push. either way, parse the items. */ - switch (sub_type) - { - LmMessageNode *item_node; - TpIntSet *pub_add, *pub_rem, - *sub_add, *sub_rem, *sub_rp, - *known_add, *known_rem, - *deny_add, *deny_rem; - TpHandleSet *referenced_handles; - GArray *removed; - TpHandle handle; - GabbleRosterChannel *chan; - GHashTable *group_update_table; - guint i; - - case LM_MESSAGE_SUB_TYPE_RESULT: - case LM_MESSAGE_SUB_TYPE_SET: - /* asymmetry is because we don't get locally pending subscription - * requests via <roster>, we get it via <presence> */ - pub_add = tp_intset_new (); - pub_rem = tp_intset_new (); - sub_add = tp_intset_new (); - sub_rem = tp_intset_new (); - sub_rp = tp_intset_new (); - known_add = tp_intset_new (); - known_rem = tp_intset_new (); - group_update_table = g_hash_table_new_full (NULL, NULL, NULL, - (GDestroyNotify)_group_mem_update_destroy); - removed = g_array_new (FALSE, FALSE, sizeof (TpHandle)); - referenced_handles = tp_handle_set_new (contact_repo); - - if (google_roster) - { - deny_add = tp_intset_new (); - deny_rem = tp_intset_new (); - } - else - { - deny_add = NULL; - deny_rem = NULL; - } - - /* get the publish channel first because we need it when processing */ - handle = GABBLE_LIST_HANDLE_PUBLISH; - chan = _gabble_roster_get_channel (roster, TP_HANDLE_TYPE_LIST, handle, - NULL); - - /* iterate every sub-node, which we expect to be <item>s */ - for (item_node = query_node->children; - item_node; - item_node = item_node->next) - { - const char *jid; - GabbleRosterItem *item; - - if (strcmp (item_node->name, "item")) - { - NODE_DEBUG (item_node, "query sub-node is not item, skipping"); - continue; - } - - jid = lm_message_node_get_attribute (item_node, "jid"); - if (!jid) - { - NODE_DEBUG (item_node, "item node has no jid, skipping"); - continue; - } - - handle = tp_handle_ensure (contact_repo, jid, NULL, NULL); - if (handle == 0) - { - NODE_DEBUG (item_node, "item jid is malformed, skipping"); - continue; - } - /* transfer ownership of the reference to referenced_handles */ - tp_handle_set_add (referenced_handles, handle); - tp_handle_unref (contact_repo, handle); - - item = _gabble_roster_item_update (roster, handle, item_node, - group_update_table, - google_roster); -#ifdef ENABLE_DEBUG - if (DEBUGGING) - { - gchar *dump = _gabble_roster_item_dump (item); - DEBUG ("jid: %s, %s", jid, dump); - g_free (dump); - } -#endif - - /* handle publish list changes */ - switch (item->subscription) - { - case GABBLE_ROSTER_SUBSCRIPTION_FROM: - case GABBLE_ROSTER_SUBSCRIPTION_BOTH: - tp_intset_add (pub_add, handle); - break; - case GABBLE_ROSTER_SUBSCRIPTION_NONE: - case GABBLE_ROSTER_SUBSCRIPTION_TO: - case GABBLE_ROSTER_SUBSCRIPTION_REMOVE: - /* publish channel is a bit odd, the roster item doesn't tell us - * if someone is awaiting our approval - we get this via presence - * type=subscribe, so we have to not remove them if they're - * already local_pending in our publish channel */ - if (!tp_handle_set_is_member (chan->group.local_pending, handle)) - { - tp_intset_add (pub_rem, handle); - } - break; - default: - g_assert_not_reached (); - } - - /* handle subscribe list changes */ - switch (item->subscription) - { - case GABBLE_ROSTER_SUBSCRIPTION_TO: - case GABBLE_ROSTER_SUBSCRIPTION_BOTH: - tp_intset_add (sub_add, handle); - break; - case GABBLE_ROSTER_SUBSCRIPTION_NONE: - case GABBLE_ROSTER_SUBSCRIPTION_FROM: - if (item->ask_subscribe) - tp_intset_add (sub_rp, handle); - else - tp_intset_add (sub_rem, handle); - break; - case GABBLE_ROSTER_SUBSCRIPTION_REMOVE: - tp_intset_add (sub_rem, handle); - break; - default: - g_assert_not_reached (); - } - - /* handle known list changes */ - switch (item->subscription) - { - case GABBLE_ROSTER_SUBSCRIPTION_NONE: - case GABBLE_ROSTER_SUBSCRIPTION_TO: - case GABBLE_ROSTER_SUBSCRIPTION_FROM: - case GABBLE_ROSTER_SUBSCRIPTION_BOTH: - if (item->google_type == GOOGLE_ITEM_TYPE_HIDDEN) - tp_intset_add (known_rem, handle); - else - tp_intset_add (known_add, handle); - break; - case GABBLE_ROSTER_SUBSCRIPTION_REMOVE: - tp_intset_add (known_rem, handle); - break; - default: - g_assert_not_reached (); - } - - /* handle deny list changes */ - if (google_roster) - { - switch (item->subscription) - { - case GABBLE_ROSTER_SUBSCRIPTION_NONE: - case GABBLE_ROSTER_SUBSCRIPTION_TO: - case GABBLE_ROSTER_SUBSCRIPTION_FROM: - case GABBLE_ROSTER_SUBSCRIPTION_BOTH: - if (item->google_type == GOOGLE_ITEM_TYPE_BLOCKED) - tp_intset_add (deny_add, handle); - else - tp_intset_add (deny_rem, handle); - break; - case GABBLE_ROSTER_SUBSCRIPTION_REMOVE: - tp_intset_add (deny_rem, handle); - break; - default: - g_assert_not_reached (); - } - } - - /* delay removing items from roster until signals have been emitted; - * otherwise handles go out of scope! - * FIXME: this probably isn't true any more because of - * referenced_handles */ - if (GABBLE_ROSTER_SUBSCRIPTION_REMOVE == item->subscription) - g_array_append_val (removed, handle); - } - - /* chan was initialised to the publish channel before the for loop */ - - DEBUG ("calling change members on publish channel"); - tp_group_mixin_change_members ((GObject *)chan, - "", pub_add, pub_rem, NULL, NULL, 0, 0); - - handle = GABBLE_LIST_HANDLE_SUBSCRIBE; - chan = _gabble_roster_get_channel (roster, TP_HANDLE_TYPE_LIST, handle, - NULL); - - DEBUG ("calling change members on subscribe channel"); - tp_group_mixin_change_members ((GObject *)chan, - "", sub_add, sub_rem, NULL, sub_rp, 0, 0); - - handle = GABBLE_LIST_HANDLE_KNOWN; - chan = _gabble_roster_get_channel (roster, TP_HANDLE_TYPE_LIST, handle, - NULL); - - DEBUG ("calling change members on known channel"); - tp_group_mixin_change_members ((GObject *)chan, - "", known_add, known_rem, NULL, NULL, 0, 0); - - DEBUG ("calling change members on any group channels"); - g_hash_table_foreach_remove (group_update_table, _update_group, roster); - - if (google_roster) - { - handle = GABBLE_LIST_HANDLE_DENY; - chan = _gabble_roster_get_channel (roster, TP_HANDLE_TYPE_LIST, - handle, NULL); - - DEBUG ("calling change members on deny channel"); - tp_group_mixin_change_members ((GObject *)chan, - "", deny_add, deny_rem, NULL, NULL, 0, 0); - - tp_intset_destroy (deny_add); - tp_intset_destroy (deny_rem); - } - - for (i = 0; i < removed->len; i++) - _gabble_roster_item_remove (roster, - g_array_index (removed, TpHandle, i)); - - tp_intset_destroy (pub_add); - tp_intset_destroy (pub_rem); - tp_intset_destroy (sub_add); - tp_intset_destroy (sub_rem); - tp_intset_destroy (sub_rp); - tp_intset_destroy (known_add); - tp_intset_destroy (known_rem); - g_array_free (removed, TRUE); - g_hash_table_destroy (group_update_table); - tp_handle_set_destroy (referenced_handles); - break; - default: - NODE_DEBUG (iq_node, "unhandled roster IQ"); - return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; - } - - switch (sub_type) - { - case LM_MESSAGE_SUB_TYPE_RESULT: - /* result means it's a roster push, so the roster is now complete and we - * can emit signals */ - _gabble_roster_received (roster); - break; - case LM_MESSAGE_SUB_TYPE_SET: - /* acknowledge roster */ - _gabble_connection_acknowledge_set_iq (priv->conn, message); - break; - default: - break; - } - - return LM_HANDLER_RESULT_REMOVE_MESSAGE; -} - - -static void -_gabble_roster_send_presence_ack (GabbleRoster *roster, - const gchar *from, - LmMessageSubType sub_type, - gboolean changed) -{ - GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster); - LmMessage *reply; - - if (!changed) - { - DEBUG ("not sending ack to avoid loop with buggy server"); - return; - } - - switch (sub_type) - { - case LM_MESSAGE_SUB_TYPE_UNSUBSCRIBE: - sub_type = LM_MESSAGE_SUB_TYPE_UNSUBSCRIBED; - break; - case LM_MESSAGE_SUB_TYPE_SUBSCRIBED: - sub_type = LM_MESSAGE_SUB_TYPE_SUBSCRIBE; - break; - case LM_MESSAGE_SUB_TYPE_UNSUBSCRIBED: - sub_type = LM_MESSAGE_SUB_TYPE_UNSUBSCRIBE; - break; - default: - g_assert_not_reached (); - return; - } - - reply = lm_message_new_with_sub_type (from, - LM_MESSAGE_TYPE_PRESENCE, - sub_type); - - _gabble_connection_send (priv->conn, reply, NULL); - - lm_message_unref (reply); -} - - -/** - * connection_presence_roster_cb: - * @handler: #LmMessageHandler for this message - * @connection: #LmConnection that originated the message - * @message: the presence message - * @user_data: callback data - * - * Called by loudmouth when we get an incoming <presence>. - */ -static LmHandlerResult -gabble_roster_presence_cb (LmMessageHandler *handler, - LmConnection *lmconn, - LmMessage *message, - gpointer user_data) -{ - GabbleRoster *roster = GABBLE_ROSTER (user_data); - GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster); - TpBaseConnection *conn = (TpBaseConnection *)priv->conn; - TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (conn, - TP_HANDLE_TYPE_CONTACT); - LmMessageNode *pres_node, *child_node; - const char *from; - LmMessageSubType sub_type; - TpIntSet *tmp; - TpHandle handle, list_handle; - const gchar *status_message = NULL; - GabbleRosterChannel *chan = NULL; - gboolean changed; - LmHandlerResult ret; - - g_assert (lmconn == priv->conn->lmconn); - - if (priv->list_channels == NULL) - return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; - - pres_node = lm_message_get_node (message); - - from = lm_message_node_get_attribute (pres_node, "from"); - - if (from == NULL) - { - NODE_DEBUG (pres_node, "presence stanza without from attribute, " - "ignoring"); - return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; - } - - sub_type = lm_message_get_sub_type (message); - - handle = tp_handle_ensure (contact_repo, from, NULL, NULL); - - if (handle == 0) - { - NODE_DEBUG (pres_node, "ignoring presence from malformed jid"); - return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; - } - - if (handle == conn->self_handle) - { - NODE_DEBUG (pres_node, "ignoring presence from ourselves on another " - "resource"); - ret = LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; - goto OUT; - } - - g_assert (handle != 0); - - child_node = lm_message_node_get_child (pres_node, "status"); - if (child_node) - status_message = lm_message_node_get_value (child_node); - - switch (sub_type) - { - case LM_MESSAGE_SUB_TYPE_SUBSCRIBE: - DEBUG ("making %s (handle %u) local pending on the publish channel", - from, handle); - - tmp = tp_intset_new (); - tp_intset_add (tmp, handle); - - list_handle = GABBLE_LIST_HANDLE_PUBLISH; - chan = _gabble_roster_get_channel (roster, TP_HANDLE_TYPE_LIST, - list_handle, NULL); - tp_group_mixin_change_members ((GObject *)chan, status_message, - NULL, NULL, tmp, NULL, 0, 0); - - tp_intset_destroy (tmp); - - ret = LM_HANDLER_RESULT_REMOVE_MESSAGE; - break; - case LM_MESSAGE_SUB_TYPE_UNSUBSCRIBE: - DEBUG ("removing %s (handle %u) from the publish channel", - from, handle); - - tmp = tp_intset_new (); - tp_intset_add (tmp, handle); - - list_handle = GABBLE_LIST_HANDLE_PUBLISH; - chan = _gabble_roster_get_channel (roster, TP_HANDLE_TYPE_LIST, - list_handle, NULL); - changed = tp_group_mixin_change_members ((GObject *)chan, - status_message, NULL, tmp, NULL, NULL, 0, 0); - - _gabble_roster_send_presence_ack (roster, from, sub_type, changed); - - tp_intset_destroy (tmp); - - ret = LM_HANDLER_RESULT_REMOVE_MESSAGE; - break; - case LM_MESSAGE_SUB_TYPE_SUBSCRIBED: - DEBUG ("adding %s (handle %u) to the subscribe channel", - from, handle); - - tmp = tp_intset_new (); - tp_intset_add (tmp, handle); - - list_handle = GABBLE_LIST_HANDLE_SUBSCRIBE; - chan = _gabble_roster_get_channel (roster, TP_HANDLE_TYPE_LIST, - list_handle, NULL); - changed = tp_group_mixin_change_members ((GObject *)chan, - status_message, tmp, NULL, NULL, NULL, 0, 0); - - _gabble_roster_send_presence_ack (roster, from, sub_type, changed); - - tp_intset_destroy (tmp); - - ret = LM_HANDLER_RESULT_REMOVE_MESSAGE; - break; - case LM_MESSAGE_SUB_TYPE_UNSUBSCRIBED: - DEBUG ("removing %s (handle %u) from the subscribe channel", - from, handle); - - tmp = tp_intset_new (); - tp_intset_add (tmp, handle); - - list_handle = GABBLE_LIST_HANDLE_SUBSCRIBE; - chan = _gabble_roster_get_channel (roster, TP_HANDLE_TYPE_LIST, - list_handle, NULL); - changed = tp_group_mixin_change_members ((GObject *)chan, - status_message, NULL, tmp, NULL, NULL, 0, 0); - - _gabble_roster_send_presence_ack (roster, from, sub_type, changed); - - tp_intset_destroy (tmp); - - ret = LM_HANDLER_RESULT_REMOVE_MESSAGE; - break; - default: - ret = LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; - } - -OUT: - tp_handle_unref (contact_repo, handle); - return ret; -} - -static void -gabble_roster_factory_iface_close_all (TpChannelFactoryIface *iface) -{ - GabbleRoster *roster = GABBLE_ROSTER (iface); - GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster); - - DEBUG ("closing channels"); - - if (priv->group_channels) - { - g_hash_table_destroy (priv->group_channels); - priv->group_channels = NULL; - } - - if (priv->list_channels) - { - g_hash_table_destroy (priv->list_channels); - priv->list_channels = NULL; - } -} - -static void -gabble_roster_factory_iface_connecting (TpChannelFactoryIface *iface) -{ - GabbleRoster *roster = GABBLE_ROSTER (iface); - GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster); - - DEBUG ("adding callbacks"); - - g_assert (priv->iq_cb == NULL); - g_assert (priv->presence_cb == NULL); - - priv->iq_cb = lm_message_handler_new (gabble_roster_iq_cb, - roster, NULL); - lm_connection_register_message_handler (priv->conn->lmconn, - priv->iq_cb, - LM_MESSAGE_TYPE_IQ, - LM_HANDLER_PRIORITY_NORMAL); - - priv->presence_cb = lm_message_handler_new (gabble_roster_presence_cb, - roster, NULL); - lm_connection_register_message_handler (priv->conn->lmconn, - priv->presence_cb, - LM_MESSAGE_TYPE_PRESENCE, - LM_HANDLER_PRIORITY_LAST); -} - -static void -gabble_roster_factory_iface_connected (TpChannelFactoryIface *iface) -{ - GabbleRoster *roster = GABBLE_ROSTER (iface); - GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster); - LmMessage *message; - - DEBUG ("requesting roster"); - - message = _gabble_roster_message_new (roster, LM_MESSAGE_SUB_TYPE_GET, NULL); - - _gabble_connection_send (priv->conn, message, NULL); - - lm_message_unref (message); -} - -static void -gabble_roster_factory_iface_disconnected (TpChannelFactoryIface *iface) -{ - GabbleRoster *roster = GABBLE_ROSTER (iface); - GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster); - - DEBUG ("removing callbacks"); - - g_assert (priv->iq_cb != NULL); - g_assert (priv->presence_cb != NULL); - - lm_connection_unregister_message_handler (priv->conn->lmconn, - priv->iq_cb, - LM_MESSAGE_TYPE_IQ); - lm_message_handler_unref (priv->iq_cb); - priv->iq_cb = NULL; - - lm_connection_unregister_message_handler (priv->conn->lmconn, - priv->presence_cb, - LM_MESSAGE_TYPE_PRESENCE); - lm_message_handler_unref (priv->presence_cb); - priv->presence_cb = NULL; -} - -struct foreach_data { - TpChannelFunc func; - gpointer data; -}; - -static void -_gabble_roster_factory_iface_foreach_one (gpointer key, - gpointer value, - gpointer data) -{ - TpChannelIface *chan = TP_CHANNEL_IFACE (value); - struct foreach_data *foreach = (struct foreach_data *) data; - - foreach->func (chan, foreach->data); -} - -static void -gabble_roster_factory_iface_foreach (TpChannelFactoryIface *iface, - TpChannelFunc func, - gpointer data) -{ - GabbleRoster *roster = GABBLE_ROSTER (iface); - GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster); - struct foreach_data foreach; - - foreach.func = func; - foreach.data = data; - - g_hash_table_foreach (priv->group_channels, - _gabble_roster_factory_iface_foreach_one, &foreach); - g_hash_table_foreach (priv->list_channels, - _gabble_roster_factory_iface_foreach_one, &foreach); -} - -static TpChannelFactoryRequestStatus -gabble_roster_factory_iface_request (TpChannelFactoryIface *iface, - const gchar *chan_type, - TpHandleType handle_type, - guint handle, - gpointer request, - TpChannelIface **ret, - GError **error) -{ - GabbleRoster *roster = GABBLE_ROSTER (iface); - GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster); - TpHandleRepoIface *handle_repo = tp_base_connection_get_handles ( - (TpBaseConnection *)priv->conn, handle_type); - gboolean created; - - if (strcmp (chan_type, TP_IFACE_CHANNEL_TYPE_CONTACT_LIST)) - return TP_CHANNEL_FACTORY_REQUEST_STATUS_NOT_IMPLEMENTED; - - if (handle_type != TP_HANDLE_TYPE_LIST && - handle_type != TP_HANDLE_TYPE_GROUP) - return TP_CHANNEL_FACTORY_REQUEST_STATUS_NOT_AVAILABLE; - - if (!tp_handle_is_valid (handle_repo, handle, NULL)) - return TP_CHANNEL_FACTORY_REQUEST_STATUS_INVALID_HANDLE; - - /* disallow "deny" channels if we don't have google:roster support */ - if (handle == GABBLE_LIST_HANDLE_DENY && - handle_type == TP_HANDLE_TYPE_LIST && - !(priv->conn->features & GABBLE_CONNECTION_FEATURES_GOOGLE_ROSTER)) - return TP_CHANNEL_FACTORY_REQUEST_STATUS_NOT_AVAILABLE; - - if (priv->roster_received) - { - GabbleRosterChannel *chan; - chan = _gabble_roster_get_channel (roster, handle_type, handle, - &created); - *ret = TP_CHANNEL_IFACE (chan); - return created ? TP_CHANNEL_FACTORY_REQUEST_STATUS_CREATED - : TP_CHANNEL_FACTORY_REQUEST_STATUS_EXISTING; - } - else - { - return TP_CHANNEL_FACTORY_REQUEST_STATUS_QUEUED; - } -} - -static void -gabble_roster_factory_iface_init (gpointer g_iface, - gpointer iface_data) -{ - TpChannelFactoryIfaceClass *klass = (TpChannelFactoryIfaceClass *) g_iface; - - klass->close_all = gabble_roster_factory_iface_close_all; - klass->connecting = gabble_roster_factory_iface_connecting; - klass->connected = gabble_roster_factory_iface_connected; - klass->disconnected = gabble_roster_factory_iface_disconnected; - klass->foreach = gabble_roster_factory_iface_foreach; - klass->request = gabble_roster_factory_iface_request; -} - -GabbleRoster * -gabble_roster_new (GabbleConnection *conn) -{ - g_return_val_if_fail (conn != NULL, NULL); - - return g_object_new (GABBLE_TYPE_ROSTER, - "connection", conn, - NULL); -} - -static GabbleRosterItemEdit * -item_edit_new (void) -{ - GabbleRosterItemEdit *self = g_slice_new0 (GabbleRosterItemEdit); - self->new_subscription = GABBLE_ROSTER_SUBSCRIPTION_INVALID; - self->new_google_type = GOOGLE_ITEM_TYPE_INVALID; - return self; -} - -static void -item_edit_free (GabbleRosterItemEdit *edits) -{ - if (!edits) - return; - - if (edits->add_to_groups) - tp_handle_set_destroy (edits->add_to_groups); - if (edits->remove_from_groups) - tp_handle_set_destroy (edits->remove_from_groups); - g_free (edits->new_name); - g_slice_free (GabbleRosterItemEdit, edits); -} - -static LmHandlerResult roster_edited_cb (GabbleConnection *conn, - LmMessage *sent_msg, - LmMessage *reply_msg, - GObject *roster_obj, - gpointer user_data); - -/* Apply the unsent edits to the given roster item. - * - * \param roster The roster - * \param contact The contact handle - * \param item contact's roster item on roster - */ -static void -roster_item_apply_edits (GabbleRoster *roster, - TpHandle contact, - GabbleRosterItem *item) -{ - gboolean altered = FALSE, ret; - GabbleRosterItem edited_item; - TpIntSet *intset; - GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster); - TpHandleRepoIface *group_repo = tp_base_connection_get_handles ( - (TpBaseConnection *)priv->conn, TP_HANDLE_TYPE_GROUP); - GabbleRosterItemEdit *edits = item->unsent_edits; - LmMessage *message; - - DEBUG ("Applying edits to contact#%u", contact); - - g_return_if_fail (item->unsent_edits); - - memcpy (&edited_item, item, sizeof (GabbleRosterItem)); - -#ifdef ENABLE_DEBUG - if (DEBUGGING) - { - gchar *dump = _gabble_roster_item_dump (&edited_item); - DEBUG ("Before, contact#%u: %s", contact, dump); - g_free (dump); - } -#endif - - if (edits->new_subscription != GABBLE_ROSTER_SUBSCRIPTION_INVALID - && edits->new_subscription != item->subscription) - { - DEBUG ("Changing subscription from %d to %d", - item->subscription, edits->new_subscription); - altered = TRUE; - edited_item.subscription = edits->new_subscription; - } - - if (edits->new_name != NULL && tp_strdiff (item->name, edits->new_name)) - { - DEBUG ("Changing name from %s to %s", item->name, edits->new_name); - altered = TRUE; - edited_item.name = edits->new_name; - } - - if (edits->new_google_type != GOOGLE_ITEM_TYPE_INVALID - && edits->new_google_type != item->google_type) - { - DEBUG ("Changing Google type from %d to %d", item->google_type, - edits->new_google_type); - altered = TRUE; - edited_item.google_type = edits->new_google_type; - } - - if (edits->add_to_groups || edits->remove_from_groups) - { -#ifdef ENABLE_DEBUG - if (DEBUGGING) - { - if (edits->add_to_groups) - { - GString *str = g_string_new ("Adding to groups: "); - tp_intset_foreach (tp_handle_set_peek (edits->add_to_groups), - _gabble_roster_item_dump_group, str); - DEBUG("%s", g_string_free (str, FALSE)); - } - else - { - DEBUG ("Not adding to any groups"); - } - if (edits->remove_from_groups) - { - GString *str = g_string_new ("Removing from groups: "); - tp_intset_foreach (tp_handle_set_peek (edits->remove_from_groups), - _gabble_roster_item_dump_group, str); - DEBUG("%s", g_string_free (str, FALSE)); - } - else - { - DEBUG ("Not removing from any groups"); - } - } -#endif - edited_item.groups = tp_handle_set_new (group_repo); - intset = tp_handle_set_update (edited_item.groups, - tp_handle_set_peek (item->groups)); - tp_intset_destroy (intset); - - if (edits->add_to_groups) - { - intset = tp_handle_set_update (edited_item.groups, - tp_handle_set_peek (edits->add_to_groups)); - if (tp_intset_size (intset) > 0) - { - altered = TRUE; - } - tp_intset_destroy (intset); - } - - if (edits->remove_from_groups) - { - intset = tp_handle_set_difference_update (edited_item.groups, - tp_handle_set_peek (edits->remove_from_groups)); - if (tp_intset_size (intset) > 0) - { - altered = TRUE; - } - tp_intset_destroy (intset); - } - } - -#ifdef ENABLE_DEBUG - if (DEBUGGING) - { - gchar *dump = _gabble_roster_item_dump (&edited_item); - DEBUG ("After, contact#%u: %s", contact, dump); - g_free (dump); - } -#endif - - if (!altered) - { - DEBUG ("Contact#%u not actually changed - nothing to do", contact); - item_edit_free (item->unsent_edits); - item->unsent_edits = NULL; - return; - } - - DEBUG ("Contact#%u did change, sending message", contact); - message = _gabble_roster_item_to_message (roster, contact, NULL, - &edited_item); - ret = _gabble_connection_send_with_reply (priv->conn, - message, roster_edited_cb, G_OBJECT (roster), - GUINT_TO_POINTER(contact), NULL); - if (ret) - { - /* assume everything will be OK */ - item_edit_free (item->unsent_edits); - item->unsent_edits = NULL; - } - else - { - /* FIXME: somehow have another try at it later? leave the - * edits in unsent_edits for this purpose, anyway - */ - } - - if (edited_item.groups != item->groups) - { - tp_handle_set_destroy (edited_item.groups); - } -} - -/* Called when an edit to the roster item has either succeeded or failed. */ -static LmHandlerResult -roster_edited_cb (GabbleConnection *conn, - LmMessage *sent_msg, - LmMessage *reply_msg, - GObject *roster_obj, - gpointer user_data) -{ - GabbleRoster *roster = GABBLE_ROSTER (roster_obj); - TpHandle contact = GPOINTER_TO_UINT(user_data); - GabbleRosterItem *item = _gabble_roster_item_get (roster, contact); - - if (item->unsent_edits) - { - /* more edits have been queued since we sent this batch */ - roster_item_apply_edits (roster, contact, item); - } - - return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; -} - -GabbleRosterSubscription -gabble_roster_handle_get_subscription (GabbleRoster *roster, - TpHandle handle) -{ - GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster); - TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( - (TpBaseConnection *)priv->conn, TP_HANDLE_TYPE_CONTACT); - GabbleRosterItem *item; - - g_return_val_if_fail (roster != NULL, GABBLE_ROSTER_SUBSCRIPTION_NONE); - g_return_val_if_fail (GABBLE_IS_ROSTER (roster), - GABBLE_ROSTER_SUBSCRIPTION_NONE); - g_return_val_if_fail (tp_handle_is_valid (contact_repo, handle, NULL), - GABBLE_ROSTER_SUBSCRIPTION_NONE); - - item = g_hash_table_lookup (priv->items, GINT_TO_POINTER (handle)); - - if (NULL == item) - return GABBLE_ROSTER_SUBSCRIPTION_NONE; - - return item->subscription; -} - -gboolean -gabble_roster_handle_set_blocked (GabbleRoster *roster, - TpHandle handle, - gboolean blocked, - GError **error) -{ - GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster); - TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( - (TpBaseConnection *)priv->conn, TP_HANDLE_TYPE_CONTACT); - GabbleRosterItem *item; - GoogleItemType orig_type; - LmMessage *message; - gboolean ret; - - g_return_val_if_fail (roster != NULL, FALSE); - g_return_val_if_fail (GABBLE_IS_ROSTER (roster), FALSE); - g_return_val_if_fail (tp_handle_is_valid (contact_repo, handle, NULL), - FALSE); - g_return_val_if_fail (priv->conn->features & - GABBLE_CONNECTION_FEATURES_GOOGLE_ROSTER, FALSE); - - item = _gabble_roster_item_get (roster, handle); - orig_type = item->google_type; - - if (item->unsent_edits) - { - DEBUG ("queue edit to contact#%u - change subscription to blocked=%d", - handle, blocked); - /* an edit is pending - make the change afterwards and - * assume it'll be OK - */ - if (blocked) - { - item->unsent_edits->new_google_type = GOOGLE_ITEM_TYPE_BLOCKED; - } - else - { - item->unsent_edits->new_google_type = GOOGLE_ITEM_TYPE_NORMAL; - } - return TRUE; - } - else - { - item->unsent_edits = item_edit_new (); - } - - if (blocked == (orig_type == GOOGLE_ITEM_TYPE_BLOCKED)) - return TRUE; - - /* temporarily set the desired block state and generate a message */ - if (blocked) - item->google_type = GOOGLE_ITEM_TYPE_BLOCKED; - else - item->google_type = GOOGLE_ITEM_TYPE_NORMAL; - message = _gabble_roster_item_to_message (roster, handle, NULL, NULL); - item->google_type = orig_type; - - ret = _gabble_connection_send_with_reply (priv->conn, - message, roster_edited_cb, G_OBJECT (roster), - GUINT_TO_POINTER(handle), error); - - lm_message_unref (message); - - return ret; -} - -gboolean -gabble_roster_handle_has_entry (GabbleRoster *roster, - TpHandle handle) -{ - GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster); - TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( - (TpBaseConnection *)priv->conn, TP_HANDLE_TYPE_CONTACT); - GabbleRosterItem *item; - - g_return_val_if_fail (roster != NULL, FALSE); - g_return_val_if_fail (GABBLE_IS_ROSTER (roster), FALSE); - g_return_val_if_fail (tp_handle_is_valid (contact_repo, handle, NULL), - FALSE); - - item = g_hash_table_lookup (priv->items, GINT_TO_POINTER (handle)); - - return (NULL != item); -} - -const gchar * -gabble_roster_handle_get_name (GabbleRoster *roster, - TpHandle handle) -{ - GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster); - TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( - (TpBaseConnection *)priv->conn, TP_HANDLE_TYPE_CONTACT); - GabbleRosterItem *item; - - g_return_val_if_fail (roster != NULL, NULL); - g_return_val_if_fail (GABBLE_IS_ROSTER (roster), NULL); - g_return_val_if_fail (tp_handle_is_valid (contact_repo, handle, NULL), - NULL); - - item = g_hash_table_lookup (priv->items, GINT_TO_POINTER (handle)); - - if (NULL == item) - return NULL; - - return item->name; -} - -gboolean -gabble_roster_handle_set_name (GabbleRoster *roster, - TpHandle handle, - const gchar *name, - GError **error) -{ - GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster); - TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( - (TpBaseConnection *)priv->conn, TP_HANDLE_TYPE_CONTACT); - GabbleRosterItem *item; - LmMessage *message; - LmMessageNode *item_node; - gboolean ret; - - g_return_val_if_fail (roster != NULL, FALSE); - g_return_val_if_fail (GABBLE_IS_ROSTER (roster), FALSE); - g_return_val_if_fail (tp_handle_is_valid (contact_repo, handle, NULL), - FALSE); - g_return_val_if_fail (name != NULL, FALSE); - - item = _gabble_roster_item_get (roster, handle); - g_return_val_if_fail (item != NULL, FALSE); - - if (item->unsent_edits) - { - DEBUG ("queue edit to contact#%u - change name to \"%s\"", - handle, name); - /* an edit is pending - make the change afterwards and - * assume it'll be OK - */ - g_free (item->unsent_edits->new_name); - item->unsent_edits->new_name = g_strdup (name); - return TRUE; - } - else - { - DEBUG ("immediate edit to contact#%u - change name to \"%s\"", - handle, name); - item->unsent_edits = item_edit_new (); - } - - message = _gabble_roster_item_to_message (roster, handle, &item_node, NULL); - - lm_message_node_set_attribute (item_node, "name", name); - - ret = _gabble_connection_send_with_reply (priv->conn, - message, roster_edited_cb, G_OBJECT (roster), - GUINT_TO_POINTER(handle), error); - - lm_message_unref (message); - - return ret; -} - -gboolean -gabble_roster_handle_remove (GabbleRoster *roster, - TpHandle handle, - GError **error) -{ - GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster); - TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( - (TpBaseConnection *)priv->conn, TP_HANDLE_TYPE_CONTACT); - GabbleRosterItem *item; - GabbleRosterSubscription subscription; - LmMessage *message; - gboolean ret; - - g_return_val_if_fail (roster != NULL, FALSE); - g_return_val_if_fail (GABBLE_IS_ROSTER (roster), FALSE); - g_return_val_if_fail (tp_handle_is_valid (contact_repo, handle, NULL), - FALSE); - - item = _gabble_roster_item_get (roster, handle); - - if (item->unsent_edits) - { - DEBUG ("queue edit to contact#%u - change subscription to REMOVE", - handle); - /* an edit is pending - make the change afterwards and - * assume it'll be OK - */ - item->unsent_edits->new_subscription = GABBLE_ROSTER_SUBSCRIPTION_REMOVE; - return TRUE; - } - else - { - DEBUG ("immediate edit to contact#%u - change subscription to REMOVE", - handle); - item->unsent_edits = item_edit_new (); - } - - subscription = item->subscription; - item->subscription = GABBLE_ROSTER_SUBSCRIPTION_REMOVE; - - message = _gabble_roster_item_to_message (roster, handle, NULL, NULL); - ret = _gabble_connection_send_with_reply (priv->conn, - message, roster_edited_cb, G_OBJECT (roster), - GUINT_TO_POINTER(handle), error); - lm_message_unref (message); - - item->subscription = subscription; - - return ret; -} - -gboolean -gabble_roster_handle_add (GabbleRoster *roster, - TpHandle handle, - GError **error) -{ - GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster); - TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( - (TpBaseConnection *)priv->conn, TP_HANDLE_TYPE_CONTACT); - GabbleRosterItem *item; - LmMessage *message; - gboolean do_add = FALSE; - gboolean ret; - - g_return_val_if_fail (roster != NULL, FALSE); - g_return_val_if_fail (GABBLE_IS_ROSTER (roster), FALSE); - g_return_val_if_fail (tp_handle_is_valid (contact_repo, handle, NULL), - FALSE); - - if (!gabble_roster_handle_has_entry (roster, handle)) - do_add = TRUE; - - item = _gabble_roster_item_get (roster, handle); - - if (item->google_type == GOOGLE_ITEM_TYPE_HIDDEN) - do_add = TRUE; - - if (!do_add) - return TRUE; - - if (item->unsent_edits) - { - DEBUG ("queue edit to contact#%u - change google type to NORMAL", - handle); - /* an edit is pending - make the change afterwards and - * assume it'll be OK. - */ - item->unsent_edits->new_google_type = GOOGLE_ITEM_TYPE_NORMAL; - return TRUE; - } - else - { - DEBUG ("immediate edit to contact#%u - change google type to NORMAL", - handle); - if (item->google_type == GOOGLE_ITEM_TYPE_HIDDEN) - item->google_type = GOOGLE_ITEM_TYPE_NORMAL; - item->unsent_edits = item_edit_new (); - } - - message = _gabble_roster_item_to_message (roster, handle, NULL, NULL); - ret = _gabble_connection_send_with_reply (priv->conn, - message, roster_edited_cb, G_OBJECT (roster), - GUINT_TO_POINTER(handle), error); - lm_message_unref (message); - - return ret; -} - -gboolean -gabble_roster_handle_add_to_group (GabbleRoster *roster, - TpHandle handle, - TpHandle group, - GError **error) -{ - GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster); - TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( - (TpBaseConnection *)priv->conn, TP_HANDLE_TYPE_CONTACT); - TpHandleRepoIface *group_repo = tp_base_connection_get_handles ( - (TpBaseConnection *)priv->conn, TP_HANDLE_TYPE_GROUP); - GabbleRosterItem *item; - LmMessage *message; - gboolean ret; - - g_return_val_if_fail (roster != NULL, FALSE); - g_return_val_if_fail (GABBLE_IS_ROSTER (roster), FALSE); - g_return_val_if_fail (tp_handle_is_valid (contact_repo, handle, NULL), - FALSE); - g_return_val_if_fail (tp_handle_is_valid (group_repo, group, NULL), - FALSE); - - item = _gabble_roster_item_get (roster, handle); - - if (item->unsent_edits) - { - DEBUG ("queue edit to contact#%u - add to group#%u", handle, group); - /* an edit is pending - make the change afterwards and - * assume it'll be OK - */ - if (!item->unsent_edits->add_to_groups) - { - item->unsent_edits->add_to_groups = tp_handle_set_new (group_repo); - } - tp_handle_set_add (item->unsent_edits->add_to_groups, group); - if (item->unsent_edits->remove_from_groups) - { - tp_handle_set_remove (item->unsent_edits->remove_from_groups, group); - } - return TRUE; - } - else - { - DEBUG ("immediate edit to contact#%u - add to group#%u", handle, group); - item->unsent_edits = item_edit_new (); - } - - tp_handle_set_add (item->groups, group); - message = _gabble_roster_item_to_message (roster, handle, NULL, NULL); - NODE_DEBUG (message->node, "Roster item as message"); - tp_handle_set_remove (item->groups, group); - - ret = _gabble_connection_send_with_reply (priv->conn, - message, roster_edited_cb, G_OBJECT (roster), - GUINT_TO_POINTER(handle), error); - lm_message_unref (message); - - return ret; -} - -gboolean -gabble_roster_handle_remove_from_group (GabbleRoster *roster, - TpHandle handle, - TpHandle group, - GError **error) -{ - GabbleRosterPrivate *priv = GABBLE_ROSTER_GET_PRIVATE (roster); - TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( - (TpBaseConnection *)priv->conn, TP_HANDLE_TYPE_CONTACT); - TpHandleRepoIface *group_repo = tp_base_connection_get_handles ( - (TpBaseConnection *)priv->conn, TP_HANDLE_TYPE_GROUP); - GabbleRosterItem *item; - LmMessage *message; - gboolean ret, was_in_group; - const gchar *name; - - g_return_val_if_fail (roster != NULL, FALSE); - g_return_val_if_fail (GABBLE_IS_ROSTER (roster), FALSE); - g_return_val_if_fail (tp_handle_is_valid (contact_repo, handle, NULL), - FALSE); - g_return_val_if_fail (tp_handle_is_valid (group_repo, group, NULL), - FALSE); - - item = _gabble_roster_item_get (roster, handle); - - if (item->unsent_edits) - { - DEBUG ("queue edit to contact#%u - remove from group#%u", handle, group); - /* an edit is pending - make the change afterwards and - * assume it'll be OK - */ - if (!item->unsent_edits->remove_from_groups) - { - item->unsent_edits->remove_from_groups = tp_handle_set_new ( - group_repo); - } - tp_handle_set_add (item->unsent_edits->remove_from_groups, group); - if (item->unsent_edits->add_to_groups) - { - tp_handle_set_remove (item->unsent_edits->add_to_groups, group); - } - return TRUE; - } - else - { - DEBUG ("immediate edit to contact#%u - remove from group#%u", handle, - group); - item->unsent_edits = item_edit_new (); - } - - name = tp_handle_inspect (group_repo, group); - - /* temporarily remove the handle from the set (taking a reference), - * make the message, and put it back afterwards - */ - tp_handle_ref (group_repo, group); - was_in_group = tp_handle_set_remove (item->groups, group); - message = _gabble_roster_item_to_message (roster, handle, NULL, NULL); - if (was_in_group) - tp_handle_set_add (item->groups, group); - tp_handle_unref (group_repo, group); - - ret = _gabble_connection_send_with_reply (priv->conn, - message, roster_edited_cb, G_OBJECT (roster), - GUINT_TO_POINTER(handle), error); - lm_message_unref (message); - - return ret; -} diff --git a/src/roster.h b/src/roster.h deleted file mode 100644 index a5749adf1..000000000 --- a/src/roster.h +++ /dev/null @@ -1,92 +0,0 @@ -/* - * roster.h - Headers for Gabble roster helper - * - * Copyright (C) 2006 Collabora Ltd. - * Copyright (C) 2006 Nokia Corporation - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef __ROSTER_H__ -#define __ROSTER_H__ - -#include <glib-object.h> - -#include "gabble-types.h" - -G_BEGIN_DECLS - -typedef struct _GabbleRosterClass GabbleRosterClass; - -GType gabble_roster_get_type (void); - -/* TYPE MACROS */ -#define GABBLE_TYPE_ROSTER \ - (gabble_roster_get_type ()) -#define GABBLE_ROSTER(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_ROSTER, GabbleRoster)) -#define GABBLE_ROSTER_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_ROSTER, GabbleRosterClass)) -#define GABBLE_IS_ROSTER(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_ROSTER)) -#define GABBLE_IS_ROSTER_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_ROSTER)) -#define GABBLE_ROSTER_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_ROSTER, GabbleRosterClass)) - -struct _GabbleRosterClass { - GObjectClass parent_class; -}; - -struct _GabbleRoster { - GObject parent; - gpointer priv; -}; - -typedef enum -{ - GABBLE_ROSTER_SUBSCRIPTION_NONE = 0, - GABBLE_ROSTER_SUBSCRIPTION_FROM = 1 << 0, - GABBLE_ROSTER_SUBSCRIPTION_TO = 1 << 1, - GABBLE_ROSTER_SUBSCRIPTION_BOTH = GABBLE_ROSTER_SUBSCRIPTION_FROM | - GABBLE_ROSTER_SUBSCRIPTION_TO, - GABBLE_ROSTER_SUBSCRIPTION_REMOVE = 1 << 2, - GABBLE_ROSTER_SUBSCRIPTION_INVALID = 1 << 3, -} GabbleRosterSubscription; - -GabbleRoster *gabble_roster_new (GabbleConnection *); - -GabbleRosterSubscription gabble_roster_handle_get_subscription (GabbleRoster *, - TpHandle); -gboolean gabble_roster_handle_set_blocked (GabbleRoster *, TpHandle, gboolean, - GError **); -const gchar *gabble_roster_handle_get_name (GabbleRoster *, TpHandle); -gboolean gabble_roster_handle_set_name (GabbleRoster *, TpHandle, - const gchar *, GError **); -gboolean gabble_roster_handle_remove (GabbleRoster *, TpHandle, GError **); -gboolean gabble_roster_handle_add (GabbleRoster *, TpHandle, GError **); -gboolean gabble_roster_handle_has_entry (GabbleRoster *, TpHandle); -gboolean gabble_roster_handle_add_to_group (GabbleRoster *roster, - TpHandle handle, - TpHandle group, - GError **error); -gboolean gabble_roster_handle_remove_from_group (GabbleRoster *roster, - TpHandle handle, - TpHandle group, - GError **error); - -G_END_DECLS - -#endif /* __ROSTER_H__ */ diff --git a/src/sha1/.git-darcs-dir b/src/sha1/.git-darcs-dir deleted file mode 100644 index e69de29bb..000000000 --- a/src/sha1/.git-darcs-dir +++ /dev/null diff --git a/src/sha1/sha1.c b/src/sha1/sha1.c deleted file mode 100644 index 513e4e0b7..000000000 --- a/src/sha1/sha1.c +++ /dev/null @@ -1,626 +0,0 @@ -/*- - * Copyright (c) 2001-2003 Allan Saddi <allan@saddi.com> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY ALLAN SADDI AND HIS CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL ALLAN SADDI OR HIS CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * $Id: sha1.c 680 2003-07-25 21:57:38Z asaddi $ - */ - -/* - * Define WORDS_BIGENDIAN if compiling on a big-endian architecture. - * - * Define SHA1_TEST to test the implementation using the NIST's - * sample messages. The output should be: - * - * a9993e36 4706816a ba3e2571 7850c26c 9cd0d89d - * 84983e44 1c3bd26e baae4aa1 f95129e5 e54670f1 - * 34aa973c d4c4daa4 f61eeb2b dbad2731 6534016f - */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif /* HAVE_CONFIG_H */ - -#if HAVE_INTTYPES_H -# include <inttypes.h> -#else -# if HAVE_STDINT_H -# include <stdint.h> -# endif -#endif - -#include <string.h> - -#include "sha1.h" - -#ifndef lint -static const char rcsid[] = - "$Id: sha1.c 680 2003-07-25 21:57:38Z asaddi $"; -#endif /* !lint */ - -#define ROTL(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) -#define ROTR(x, n) (((x) >> (n)) | ((x) << (32 - (n)))) - -#define F_0_19(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) -#define F_20_39(x, y, z) ((x) ^ (y) ^ (z)) -#define F_40_59(x, y, z) (((x) & ((y) | (z))) | ((y) & (z))) -#define F_60_79(x, y, z) ((x) ^ (y) ^ (z)) - -#define DO_ROUND(F, K) { \ - temp = ROTL(a, 5) + F(b, c, d) + e + *(W++) + K; \ - e = d; \ - d = c; \ - c = ROTL(b, 30); \ - b = a; \ - a = temp; \ -} - -#define K_0_19 0x5a827999L -#define K_20_39 0x6ed9eba1L -#define K_40_59 0x8f1bbcdcL -#define K_60_79 0xca62c1d6L - -#ifndef RUNTIME_ENDIAN - -#ifdef WORDS_BIGENDIAN - -#define BYTESWAP(x) (x) -#define BYTESWAP64(x) (x) - -#else /* WORDS_BIGENDIAN */ - -#define BYTESWAP(x) ((ROTR((x), 8) & 0xff00ff00L) | \ - (ROTL((x), 8) & 0x00ff00ffL)) -#define BYTESWAP64(x) _byteswap64(x) - -static inline uint64_t _byteswap64(uint64_t x) -{ - uint32_t a = x >> 32; - uint32_t b = (uint32_t) x; - return ((uint64_t) BYTESWAP(b) << 32) | (uint64_t) BYTESWAP(a); -} - -#endif /* WORDS_BIGENDIAN */ - -#else /* !RUNTIME_ENDIAN */ - -#define BYTESWAP(x) _byteswap(sc->littleEndian, x) -#define BYTESWAP64(x) _byteswap64(sc->littleEndian, x) - -#define _BYTESWAP(x) ((ROTR((x), 8) & 0xff00ff00L) | \ - (ROTL((x), 8) & 0x00ff00ffL)) -#define _BYTESWAP64(x) __byteswap64(x) - -static inline uint64_t __byteswap64(uint64_t x) -{ - uint32_t a = x >> 32; - uint32_t b = (uint32_t) x; - return ((uint64_t) _BYTESWAP(b) << 32) | (uint64_t) _BYTESWAP(a); -} - -static inline uint32_t _byteswap(int littleEndian, uint32_t x) -{ - if (!littleEndian) - return x; - else - return _BYTESWAP(x); -} - -static inline uint64_t _byteswap64(int littleEndian, uint64_t x) -{ - if (!littleEndian) - return x; - else - return _BYTESWAP64(x); -} - -static inline void setEndian(int *littleEndianp) -{ - union { - uint32_t w; - uint8_t b[4]; - } endian; - - endian.w = 1L; - *littleEndianp = endian.b[0] != 0; -} - -#endif /* !RUNTIME_ENDIAN */ - -static const uint8_t padding[64] = { - 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; - -void -SHA1Init (SHA1Context *sc) -{ -#ifdef RUNTIME_ENDIAN - setEndian (&sc->littleEndian); -#endif /* RUNTIME_ENDIAN */ - - sc->totalLength = 0LL; - sc->hash[0] = 0x67452301L; - sc->hash[1] = 0xefcdab89L; - sc->hash[2] = 0x98badcfeL; - sc->hash[3] = 0x10325476L; - sc->hash[4] = 0xc3d2e1f0L; - sc->bufferLength = 0L; -} - -static void -burnStack (int size) -{ - char buf[128]; - - memset (buf, 0, sizeof (buf)); - size -= sizeof (buf); - if (size > 0) - burnStack (size); -} - -static void -SHA1Guts (SHA1Context *sc, const uint32_t *cbuf) -{ - uint32_t buf[80]; - uint32_t *W, *W3, *W8, *W14, *W16; - uint32_t a, b, c, d, e, temp; - int i; - - W = buf; - - for (i = 15; i >= 0; i--) { - *(W++) = BYTESWAP(*cbuf); - cbuf++; - } - - W16 = &buf[0]; - W14 = &buf[2]; - W8 = &buf[8]; - W3 = &buf[13]; - - for (i = 63; i >= 0; i--) { - *W = *(W3++) ^ *(W8++) ^ *(W14++) ^ *(W16++); - *W = ROTL(*W, 1); - W++; - } - - a = sc->hash[0]; - b = sc->hash[1]; - c = sc->hash[2]; - d = sc->hash[3]; - e = sc->hash[4]; - - W = buf; - -#ifndef SHA1_UNROLL -#define SHA1_UNROLL 20 -#endif /* !SHA1_UNROLL */ - -#if SHA1_UNROLL == 1 - for (i = 19; i >= 0; i--) - DO_ROUND(F_0_19, K_0_19); - - for (i = 19; i >= 0; i--) - DO_ROUND(F_20_39, K_20_39); - - for (i = 19; i >= 0; i--) - DO_ROUND(F_40_59, K_40_59); - - for (i = 19; i >= 0; i--) - DO_ROUND(F_60_79, K_60_79); -#elif SHA1_UNROLL == 2 - for (i = 9; i >= 0; i--) { - DO_ROUND(F_0_19, K_0_19); - DO_ROUND(F_0_19, K_0_19); - } - - for (i = 9; i >= 0; i--) { - DO_ROUND(F_20_39, K_20_39); - DO_ROUND(F_20_39, K_20_39); - } - - for (i = 9; i >= 0; i--) { - DO_ROUND(F_40_59, K_40_59); - DO_ROUND(F_40_59, K_40_59); - } - - for (i = 9; i >= 0; i--) { - DO_ROUND(F_60_79, K_60_79); - DO_ROUND(F_60_79, K_60_79); - } -#elif SHA1_UNROLL == 4 - for (i = 4; i >= 0; i--) { - DO_ROUND(F_0_19, K_0_19); - DO_ROUND(F_0_19, K_0_19); - DO_ROUND(F_0_19, K_0_19); - DO_ROUND(F_0_19, K_0_19); - } - - for (i = 4; i >= 0; i--) { - DO_ROUND(F_20_39, K_20_39); - DO_ROUND(F_20_39, K_20_39); - DO_ROUND(F_20_39, K_20_39); - DO_ROUND(F_20_39, K_20_39); - } - - for (i = 4; i >= 0; i--) { - DO_ROUND(F_40_59, K_40_59); - DO_ROUND(F_40_59, K_40_59); - DO_ROUND(F_40_59, K_40_59); - DO_ROUND(F_40_59, K_40_59); - } - - for (i = 4; i >= 0; i--) { - DO_ROUND(F_60_79, K_60_79); - DO_ROUND(F_60_79, K_60_79); - DO_ROUND(F_60_79, K_60_79); - DO_ROUND(F_60_79, K_60_79); - } -#elif SHA1_UNROLL == 5 - for (i = 3; i >= 0; i--) { - DO_ROUND(F_0_19, K_0_19); - DO_ROUND(F_0_19, K_0_19); - DO_ROUND(F_0_19, K_0_19); - DO_ROUND(F_0_19, K_0_19); - DO_ROUND(F_0_19, K_0_19); - } - - for (i = 3; i >= 0; i--) { - DO_ROUND(F_20_39, K_20_39); - DO_ROUND(F_20_39, K_20_39); - DO_ROUND(F_20_39, K_20_39); - DO_ROUND(F_20_39, K_20_39); - DO_ROUND(F_20_39, K_20_39); - } - - for (i = 3; i >= 0; i--) { - DO_ROUND(F_40_59, K_40_59); - DO_ROUND(F_40_59, K_40_59); - DO_ROUND(F_40_59, K_40_59); - DO_ROUND(F_40_59, K_40_59); - DO_ROUND(F_40_59, K_40_59); - } - - for (i = 3; i >= 0; i--) { - DO_ROUND(F_60_79, K_60_79); - DO_ROUND(F_60_79, K_60_79); - DO_ROUND(F_60_79, K_60_79); - DO_ROUND(F_60_79, K_60_79); - DO_ROUND(F_60_79, K_60_79); - } -#elif SHA1_UNROLL == 10 - for (i = 1; i >= 0; i--) { - DO_ROUND(F_0_19, K_0_19); - DO_ROUND(F_0_19, K_0_19); - DO_ROUND(F_0_19, K_0_19); - DO_ROUND(F_0_19, K_0_19); - DO_ROUND(F_0_19, K_0_19); - DO_ROUND(F_0_19, K_0_19); - DO_ROUND(F_0_19, K_0_19); - DO_ROUND(F_0_19, K_0_19); - DO_ROUND(F_0_19, K_0_19); - DO_ROUND(F_0_19, K_0_19); - } - - for (i = 1; i >= 0; i--) { - DO_ROUND(F_20_39, K_20_39); - DO_ROUND(F_20_39, K_20_39); - DO_ROUND(F_20_39, K_20_39); - DO_ROUND(F_20_39, K_20_39); - DO_ROUND(F_20_39, K_20_39); - DO_ROUND(F_20_39, K_20_39); - DO_ROUND(F_20_39, K_20_39); - DO_ROUND(F_20_39, K_20_39); - DO_ROUND(F_20_39, K_20_39); - DO_ROUND(F_20_39, K_20_39); - } - - for (i = 1; i >= 0; i--) { - DO_ROUND(F_40_59, K_40_59); - DO_ROUND(F_40_59, K_40_59); - DO_ROUND(F_40_59, K_40_59); - DO_ROUND(F_40_59, K_40_59); - DO_ROUND(F_40_59, K_40_59); - DO_ROUND(F_40_59, K_40_59); - DO_ROUND(F_40_59, K_40_59); - DO_ROUND(F_40_59, K_40_59); - DO_ROUND(F_40_59, K_40_59); - DO_ROUND(F_40_59, K_40_59); - } - - for (i = 1; i >= 0; i--) { - DO_ROUND(F_60_79, K_60_79); - DO_ROUND(F_60_79, K_60_79); - DO_ROUND(F_60_79, K_60_79); - DO_ROUND(F_60_79, K_60_79); - DO_ROUND(F_60_79, K_60_79); - DO_ROUND(F_60_79, K_60_79); - DO_ROUND(F_60_79, K_60_79); - DO_ROUND(F_60_79, K_60_79); - DO_ROUND(F_60_79, K_60_79); - DO_ROUND(F_60_79, K_60_79); - } -#elif SHA1_UNROLL == 20 - DO_ROUND(F_0_19, K_0_19); - DO_ROUND(F_0_19, K_0_19); - DO_ROUND(F_0_19, K_0_19); - DO_ROUND(F_0_19, K_0_19); - DO_ROUND(F_0_19, K_0_19); - DO_ROUND(F_0_19, K_0_19); - DO_ROUND(F_0_19, K_0_19); - DO_ROUND(F_0_19, K_0_19); - DO_ROUND(F_0_19, K_0_19); - DO_ROUND(F_0_19, K_0_19); - DO_ROUND(F_0_19, K_0_19); - DO_ROUND(F_0_19, K_0_19); - DO_ROUND(F_0_19, K_0_19); - DO_ROUND(F_0_19, K_0_19); - DO_ROUND(F_0_19, K_0_19); - DO_ROUND(F_0_19, K_0_19); - DO_ROUND(F_0_19, K_0_19); - DO_ROUND(F_0_19, K_0_19); - DO_ROUND(F_0_19, K_0_19); - DO_ROUND(F_0_19, K_0_19); - - DO_ROUND(F_20_39, K_20_39); - DO_ROUND(F_20_39, K_20_39); - DO_ROUND(F_20_39, K_20_39); - DO_ROUND(F_20_39, K_20_39); - DO_ROUND(F_20_39, K_20_39); - DO_ROUND(F_20_39, K_20_39); - DO_ROUND(F_20_39, K_20_39); - DO_ROUND(F_20_39, K_20_39); - DO_ROUND(F_20_39, K_20_39); - DO_ROUND(F_20_39, K_20_39); - DO_ROUND(F_20_39, K_20_39); - DO_ROUND(F_20_39, K_20_39); - DO_ROUND(F_20_39, K_20_39); - DO_ROUND(F_20_39, K_20_39); - DO_ROUND(F_20_39, K_20_39); - DO_ROUND(F_20_39, K_20_39); - DO_ROUND(F_20_39, K_20_39); - DO_ROUND(F_20_39, K_20_39); - DO_ROUND(F_20_39, K_20_39); - DO_ROUND(F_20_39, K_20_39); - - DO_ROUND(F_40_59, K_40_59); - DO_ROUND(F_40_59, K_40_59); - DO_ROUND(F_40_59, K_40_59); - DO_ROUND(F_40_59, K_40_59); - DO_ROUND(F_40_59, K_40_59); - DO_ROUND(F_40_59, K_40_59); - DO_ROUND(F_40_59, K_40_59); - DO_ROUND(F_40_59, K_40_59); - DO_ROUND(F_40_59, K_40_59); - DO_ROUND(F_40_59, K_40_59); - DO_ROUND(F_40_59, K_40_59); - DO_ROUND(F_40_59, K_40_59); - DO_ROUND(F_40_59, K_40_59); - DO_ROUND(F_40_59, K_40_59); - DO_ROUND(F_40_59, K_40_59); - DO_ROUND(F_40_59, K_40_59); - DO_ROUND(F_40_59, K_40_59); - DO_ROUND(F_40_59, K_40_59); - DO_ROUND(F_40_59, K_40_59); - DO_ROUND(F_40_59, K_40_59); - - DO_ROUND(F_60_79, K_60_79); - DO_ROUND(F_60_79, K_60_79); - DO_ROUND(F_60_79, K_60_79); - DO_ROUND(F_60_79, K_60_79); - DO_ROUND(F_60_79, K_60_79); - DO_ROUND(F_60_79, K_60_79); - DO_ROUND(F_60_79, K_60_79); - DO_ROUND(F_60_79, K_60_79); - DO_ROUND(F_60_79, K_60_79); - DO_ROUND(F_60_79, K_60_79); - DO_ROUND(F_60_79, K_60_79); - DO_ROUND(F_60_79, K_60_79); - DO_ROUND(F_60_79, K_60_79); - DO_ROUND(F_60_79, K_60_79); - DO_ROUND(F_60_79, K_60_79); - DO_ROUND(F_60_79, K_60_79); - DO_ROUND(F_60_79, K_60_79); - DO_ROUND(F_60_79, K_60_79); - DO_ROUND(F_60_79, K_60_79); - DO_ROUND(F_60_79, K_60_79); -#else /* SHA1_UNROLL */ -#error SHA1_UNROLL must be 1, 2, 4, 5, 10 or 20! -#endif - - sc->hash[0] += a; - sc->hash[1] += b; - sc->hash[2] += c; - sc->hash[3] += d; - sc->hash[4] += e; -} - -void -SHA1Update (SHA1Context *sc, const void *vdata, uint32_t len) -{ - const uint8_t *data = vdata; - uint32_t bufferBytesLeft; - uint32_t bytesToCopy; - int needBurn = 0; - -#ifdef SHA1_FAST_COPY - if (sc->bufferLength) { - bufferBytesLeft = 64L - sc->bufferLength; - - bytesToCopy = bufferBytesLeft; - if (bytesToCopy > len) - bytesToCopy = len; - - memcpy (&sc->buffer.bytes[sc->bufferLength], data, bytesToCopy); - - sc->totalLength += bytesToCopy * 8L; - - sc->bufferLength += bytesToCopy; - data += bytesToCopy; - len -= bytesToCopy; - - if (sc->bufferLength == 64L) { - SHA1Guts (sc, sc->buffer.words); - needBurn = 1; - sc->bufferLength = 0L; - } - } - - while (len > 63) { - sc->totalLength += 512L; - - SHA1Guts (sc, data); - needBurn = 1; - - data += 64L; - len -= 64L; - } - - if (len) { - memcpy (&sc->buffer.bytes[sc->bufferLength], data, len); - - sc->totalLength += len * 8L; - - sc->bufferLength += len; - } -#else /* SHA1_FAST_COPY */ - while (len) { - bufferBytesLeft = 64L - sc->bufferLength; - - bytesToCopy = bufferBytesLeft; - if (bytesToCopy > len) - bytesToCopy = len; - - memcpy (&sc->buffer.bytes[sc->bufferLength], data, bytesToCopy); - - sc->totalLength += bytesToCopy * 8L; - - sc->bufferLength += bytesToCopy; - data += bytesToCopy; - len -= bytesToCopy; - - if (sc->bufferLength == 64L) { - SHA1Guts (sc, sc->buffer.words); - needBurn = 1; - sc->bufferLength = 0L; - } - } -#endif /* SHA1_FAST_COPY */ - - if (needBurn) - burnStack (sizeof (uint32_t[86]) + sizeof (uint32_t *[5]) + sizeof (int)); -} - -void -SHA1Final (SHA1Context *sc, uint8_t hash[SHA1_HASH_SIZE]) -{ - uint32_t bytesToPad; - uint64_t lengthPad; - int i; - - bytesToPad = 120L - sc->bufferLength; - if (bytesToPad > 64L) - bytesToPad -= 64L; - - lengthPad = BYTESWAP64(sc->totalLength); - - SHA1Update (sc, padding, bytesToPad); - SHA1Update (sc, &lengthPad, 8L); - - if (hash) { - for (i = 0; i < SHA1_HASH_WORDS; i++) { -#ifdef SHA1_FAST_COPY - *((uint32_t *) hash) = BYTESWAP(sc->hash[i]); -#else /* SHA1_FAST_COPY */ - hash[0] = (uint8_t) (sc->hash[i] >> 24); - hash[1] = (uint8_t) (sc->hash[i] >> 16); - hash[2] = (uint8_t) (sc->hash[i] >> 8); - hash[3] = (uint8_t) sc->hash[i]; -#endif /* SHA1_FAST_COPY */ - hash += 4; - } - } -} - -#ifdef SHA1_TEST - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -int -main (int argc, char *argv[]) -{ - SHA1Context foo; - uint8_t hash[SHA1_HASH_SIZE]; - char buf[1000]; - int i; - - SHA1Init (&foo); - SHA1Update (&foo, "abc", 3); - SHA1Final (&foo, hash); - - for (i = 0; i < SHA1_HASH_SIZE;) { - printf ("%02x", hash[i++]); - if (!(i % 4)) - printf (" "); - } - printf ("\n"); - - SHA1Init (&foo); - SHA1Update (&foo, - "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", - 56); - SHA1Final (&foo, hash); - - for (i = 0; i < SHA1_HASH_SIZE;) { - printf ("%02x", hash[i++]); - if (!(i % 4)) - printf (" "); - } - printf ("\n"); - - SHA1Init (&foo); - memset (buf, 'a', sizeof (buf)); - for (i = 0; i < 1000; i++) - SHA1Update (&foo, buf, sizeof (buf)); - SHA1Final (&foo, hash); - - for (i = 0; i < SHA1_HASH_SIZE;) { - printf ("%02x", hash[i++]); - if (!(i % 4)) - printf (" "); - } - printf ("\n"); - - exit (0); -} - -#endif /* SHA1_TEST */ diff --git a/src/sha1/sha1.h b/src/sha1/sha1.h deleted file mode 100644 index 7a6beb7f5..000000000 --- a/src/sha1/sha1.h +++ /dev/null @@ -1,72 +0,0 @@ -/*- - * Copyright (c) 2001-2003 Allan Saddi <allan@saddi.com> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY ALLAN SADDI AND HIS CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL ALLAN SADDI OR HIS CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * $Id: sha1.h 347 2003-02-23 22:11:49Z asaddi $ - */ - -#ifndef _SHA1_H -#define _SHA1_H - -#if HAVE_INTTYPES_H -# include <inttypes.h> -#else -# if HAVE_STDINT_H -# include <stdint.h> -# endif -#endif - -#define SHA1_HASH_SIZE 20 - -/* Hash size in 32-bit words */ -#define SHA1_HASH_WORDS 5 - -struct _SHA1Context { - uint64_t totalLength; - uint32_t hash[SHA1_HASH_WORDS]; - uint32_t bufferLength; - union { - uint32_t words[16]; - uint8_t bytes[64]; - } buffer; -#ifdef RUNTIME_ENDIAN - int littleEndian; -#endif /* RUNTIME_ENDIAN */ -}; - -typedef struct _SHA1Context SHA1Context; - -#ifdef __cplusplus -extern "C" { -#endif - -void SHA1Init (SHA1Context *sc); -void SHA1Update (SHA1Context *sc, const void *data, uint32_t len); -void SHA1Final (SHA1Context *sc, uint8_t hash[SHA1_HASH_SIZE]); - -#ifdef __cplusplus -} -#endif - -#endif /* _SHA1_H */ diff --git a/src/text-mixin.c b/src/text-mixin.c deleted file mode 100644 index 730628bb9..000000000 --- a/src/text-mixin.c +++ /dev/null @@ -1,378 +0,0 @@ -/* - * text-mixin.c - Gabble-specific bits for TpTextMixin - * Copyright (C) 2006, 2007 Collabora Ltd. - * Copyright (C) 2006, 2007 Nokia Corporation - * @author Ole Andre Vadla Ravnaas <ole.andre.ravnaas@collabora.co.uk> - * @author Robert McQueen <robert.mcqueen@collabora.co.uk> - * @author Senko Rasic <senko@senko.net> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#define _GNU_SOURCE /* Needed for strptime (_XOPEN_SOURCE can also be used). */ - -#include "text-mixin.h" - -#include <loudmouth/loudmouth.h> -#include <dbus/dbus-glib.h> -#include <string.h> -#include <time.h> - -#include <telepathy-glib/text-mixin.h> -#include <telepathy-glib/enums.h> -#include <telepathy-glib/errors.h> - -#define DEBUG_FLAG GABBLE_DEBUG_IM - -#include "debug.h" -#include "gabble-connection.h" -#include "namespaces.h" -#include "roster.h" -#include "util.h" - -/** - * gabble_text_mixin_init: - * @obj_cls: The class of the implementation that uses this mixin - * @offset: The offset of the GabbleTextMixinClass within the class structure - * @send_nick: %TRUE if the user's nick should be included in messages - * sent through this channel - * - * Initialize the text mixin. Should be called instead of #tp_text_mixin_init - * from the implementation's init function. - */ -void -gabble_text_mixin_init (GObject *obj, - glong offset, - TpHandleRepoIface *contacts_repo, - gboolean send_nick) -{ - GabbleTextMixin *mixin; - - tp_text_mixin_init (obj, offset, contacts_repo); - - mixin = GABBLE_TEXT_MIXIN (obj); - - mixin->send_nick = send_nick; -} - -/** - * gabble_text_mixin_send - * - * Indirectly, implements D-Bus method Send - * on interface org.freedesktop.Telepathy.Channel.Type.Text. - * - * @param type The Telepathy message type - * @param subtype The Loudmouth message subtype - * @param state The Telepathy chat state type - * @param recipient The recipient's JID - * @param text The text of the message (if NULL, the message won't have body) - * @param conn The Connection - * @param emit_signal If true, emit Sent; if false, assume we'll get an - * echo of the message and will emit Sent at that point - * @param error The GError - */ -gboolean -gabble_text_mixin_send (GObject *obj, - guint type, - guint subtype, - gint state, - const char *recipient, - const gchar *text, - GabbleConnection *conn, - gboolean emit_signal, - GError **error) -{ - GabbleTextMixin *mixin = GABBLE_TEXT_MIXIN (obj); - LmMessage *msg; - LmMessageNode *node; - gboolean result; - time_t timestamp; - - if (type >= NUM_TP_CHANNEL_TEXT_MESSAGE_TYPES) - { - DEBUG ("invalid message type %u", type); - - g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, - "invalid message type: %u", type); - - return FALSE; - } - - if (!subtype) - { - switch (type) - { - case TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL: - case TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION: - subtype = LM_MESSAGE_SUB_TYPE_CHAT; - break; - case TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE: - subtype = LM_MESSAGE_SUB_TYPE_NORMAL; - break; - } - } - - msg = lm_message_new_with_sub_type (recipient, LM_MESSAGE_TYPE_MESSAGE, - subtype); - - if (mixin->send_nick) - { - lm_message_node_add_own_nick (msg->node, conn); - mixin->send_nick = FALSE; - } - - if (text != NULL) - { - if (type == TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION) - { - gchar *tmp; - tmp = g_strconcat ("/me ", text, NULL); - lm_message_node_add_child (msg->node, "body", tmp); - g_free (tmp); - } - else - { - lm_message_node_add_child (msg->node, "body", text); - } - } - - node = NULL; - - switch (state) - { - case TP_CHANNEL_CHAT_STATE_GONE: - node = lm_message_node_add_child (msg->node, "gone", NULL); - break; - case TP_CHANNEL_CHAT_STATE_INACTIVE: - node = lm_message_node_add_child (msg->node, "inactive", NULL); - break; - case TP_CHANNEL_CHAT_STATE_ACTIVE: - node = lm_message_node_add_child (msg->node, "active", NULL); - break; - case TP_CHANNEL_CHAT_STATE_PAUSED: - node = lm_message_node_add_child (msg->node, "paused", NULL); - break; - case TP_CHANNEL_CHAT_STATE_COMPOSING: - node = lm_message_node_add_child (msg->node, "composing", NULL); - break; - } - - if (node != NULL) - { - lm_message_node_set_attributes (node, "xmlns", NS_CHAT_STATES, NULL); - } - - result = _gabble_connection_send (conn, msg, error); - lm_message_unref (msg); - - if (!result) - { - return FALSE; - } - - if (emit_signal && text != NULL) - { - timestamp = time (NULL); - - tp_svc_channel_type_text_emit_sent (obj, timestamp, type, text); - } - return TRUE; -} - -gboolean -gabble_text_mixin_parse_incoming_message (LmMessage *message, - const gchar **from, - time_t *stamp, - TpChannelTextMessageType *msgtype, - const gchar **body_ret, - gint *state, - TpChannelTextSendError *send_error) -{ - const gchar *type, *body; - LmMessageNode *node; - - *send_error = TP_CHANNEL_SEND_NO_ERROR; - - if (lm_message_get_sub_type (message) == LM_MESSAGE_SUB_TYPE_ERROR) - { - LmMessageNode *error_node; - - error_node = lm_message_node_get_child (message->node, "error"); - if (error_node) - { - GabbleXmppError err = gabble_xmpp_error_from_node (error_node); - DEBUG ("got xmpp error: %s: %s", gabble_xmpp_error_string (err), - gabble_xmpp_error_description (err)); - - /* these are based on descriptions of errors, and some testing */ - switch (err) - { - case XMPP_ERROR_SERVICE_UNAVAILABLE: - case XMPP_ERROR_RECIPIENT_UNAVAILABLE: - *send_error = TP_CHANNEL_TEXT_SEND_ERROR_OFFLINE; - break; - - case XMPP_ERROR_ITEM_NOT_FOUND: - case XMPP_ERROR_JID_MALFORMED: - case XMPP_ERROR_REMOTE_SERVER_TIMEOUT: - *send_error = TP_CHANNEL_TEXT_SEND_ERROR_INVALID_CONTACT; - break; - - case XMPP_ERROR_FORBIDDEN: - *send_error = TP_CHANNEL_TEXT_SEND_ERROR_PERMISSION_DENIED; - break; - - case XMPP_ERROR_RESOURCE_CONSTRAINT: - *send_error = TP_CHANNEL_TEXT_SEND_ERROR_TOO_LONG; - break; - - case XMPP_ERROR_FEATURE_NOT_IMPLEMENTED: - *send_error = TP_CHANNEL_TEXT_SEND_ERROR_NOT_IMPLEMENTED; - break; - - default: - *send_error = TP_CHANNEL_TEXT_SEND_ERROR_UNKNOWN; - } - } - else - { - *send_error = TP_CHANNEL_TEXT_SEND_ERROR_UNKNOWN; - } - } - - *from = lm_message_node_get_attribute (message->node, "from"); - if (*from == NULL) - { - NODE_DEBUG (message->node, "got a message without a from field"); - return FALSE; - } - - type = lm_message_node_get_attribute (message->node, "type"); - - /* - * Parse timestamp of delayed messages. For non-delayed, it's - * 0 and the channel code should set the current timestamp. - */ - *stamp = 0; - - node = lm_message_node_get_child_with_namespace (message->node, "x", - NS_X_DELAY); - if (node != NULL) - { - const gchar *stamp_str, *p; - struct tm stamp_tm = { 0, }; - - stamp_str = lm_message_node_get_attribute (node, "stamp"); - if (stamp_str != NULL) - { - p = strptime (stamp_str, "%Y%m%dT%T", &stamp_tm); - if (p == NULL || *p != '\0') - { - g_warning ("%s: malformed date string '%s' for jabber:x:delay", - G_STRFUNC, stamp_str); - } - else - { - *stamp = timegm (&stamp_tm); - } - } - } - - /* - * Parse body if it exists. - */ - node = lm_message_node_get_child (message->node, "body"); - - if (node) - { - body = lm_message_node_get_value (node); - } - else - { - body = NULL; - } - - /* Messages starting with /me are ACTION messages, and the /me should be - * removed. type="chat" messages are NORMAL. everything else is - * something that doesn't necessarily expect a reply or ongoing - * conversation ("normal") or has been auto-sent, so we make it NOTICE in - * all other cases. */ - - *msgtype = TP_CHANNEL_TEXT_MESSAGE_TYPE_NOTICE; - *body_ret = body; - - if (body != NULL) - { - if (0 == strncmp (body, "/me ", 4)) - { - *msgtype = TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION; - *body_ret = body + 4; - } - else if (type != NULL && (0 == strcmp (type, "chat") || - 0 == strcmp (type, "groupchat"))) - { - *msgtype = TP_CHANNEL_TEXT_MESSAGE_TYPE_NORMAL; - *body_ret = body; - } - } - - /* - * Parse chat state if it exists. - */ - - node = lm_message_node_get_child_with_namespace (message->node, "active", - NS_CHAT_STATES); - if (node) - { - *state = TP_CHANNEL_CHAT_STATE_ACTIVE; - return TRUE; - } - - node = lm_message_node_get_child_with_namespace (message->node, "composing", - NS_CHAT_STATES); - if (node) - { - *state = TP_CHANNEL_CHAT_STATE_COMPOSING; - return TRUE; - } - - node = lm_message_node_get_child_with_namespace (message->node, "inactive", - NS_CHAT_STATES); - if (node) - { - *state = TP_CHANNEL_CHAT_STATE_INACTIVE; - return TRUE; - } - - node = lm_message_node_get_child_with_namespace (message->node, "paused", - NS_CHAT_STATES); - if (node) - { - *state = TP_CHANNEL_CHAT_STATE_PAUSED; - return TRUE; - } - - node = lm_message_node_get_child_with_namespace (message->node, "gone", - NS_CHAT_STATES); - if (node) - { - *state = TP_CHANNEL_CHAT_STATE_GONE; - return TRUE; - } - - *state = -1; - - return TRUE; -} diff --git a/src/text-mixin.h b/src/text-mixin.h deleted file mode 100644 index 336982fee..000000000 --- a/src/text-mixin.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * text-mixin.h - Header for GabbleTextMixin - * Copyright (C) 2006 Collabora Ltd. - * Copyright (C) 2006 Nokia Corporation - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef __GABBLE_TEXT_MIXIN_H__ -#define __GABBLE_TEXT_MIXIN_H__ - -#include <telepathy-glib/text-mixin.h> -#include "util.h" - -G_BEGIN_DECLS - -typedef struct _GabbleTextMixinClass GabbleTextMixinClass; -struct _GabbleTextMixinClass -{ - TpTextMixinClass parent_class; -}; - -typedef struct _GabbleTextMixin GabbleTextMixin; -struct _GabbleTextMixin -{ - TpTextMixin parent; - gboolean send_nick; -}; - -#define GABBLE_TEXT_MIXIN_CLASS(o) \ - ((GabbleTextMixinClass *) tp_mixin_offset_cast (o,\ - TP_TEXT_MIXIN_CLASS_OFFSET (o))) -#define GABBLE_TEXT_MIXIN(o) \ - ((GabbleTextMixin *) tp_mixin_offset_cast (o, TP_TEXT_MIXIN_OFFSET (o))) - -#define TP_CHANNEL_SEND_NO_ERROR ((TpChannelTextSendError)-1) - -void gabble_text_mixin_init (GObject *obj, glong offset, - TpHandleRepoIface *contacts_repo, gboolean send_nick); - -gboolean gabble_text_mixin_send (GObject *obj, guint type, guint subtype, - gint state, const char *recipient, const gchar *text, - GabbleConnection *conn, gboolean emit_signal, GError **error); - -gboolean gabble_text_mixin_parse_incoming_message (LmMessage *message, - const gchar **from, time_t *stamp, TpChannelTextMessageType *msgtype, - const gchar **body_ret, gint *state, TpChannelTextSendError *send_error); - -G_END_DECLS - -#endif /* #ifndef __GABBLE_TEXT_MIXIN_H__ */ - diff --git a/src/util.c b/src/util.c deleted file mode 100644 index ec5c4b537..000000000 --- a/src/util.c +++ /dev/null @@ -1,491 +0,0 @@ -/* - * util.c - Source for Gabble utility functions - * Copyright (C) 2006-2007 Collabora Ltd. - * Copyright (C) 2006-2007 Nokia Corporation - * @author Robert McQueen <robert.mcqueen@collabora.co.uk> - * @author Simon McVittie <simon.mcvittie@collabora.co.uk> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "config.h" - -#include "util.h" - -#include <glib.h> -#include <stdio.h> -#include <string.h> - -#include <telepathy-glib/handle-repo-dynamic.c> - -#include "sha1/sha1.h" -#include "namespaces.h" -#include "gabble-connection.h" - -#define DEBUG_FLAG GABBLE_DEBUG_JID -#include "debug.h" - -gchar * -sha1_hex (const gchar *bytes, guint len) -{ - SHA1Context sc; - uint8_t hash[SHA1_HASH_SIZE]; - gchar *hex_hash = g_malloc (SHA1_HASH_SIZE*2 + 1); - int i; - - SHA1Init (&sc); - SHA1Update (&sc, bytes, len); - SHA1Final (&sc, hash); - - for (i = 0; i < SHA1_HASH_SIZE; i++) - { - sprintf (hex_hash + 2 * i, "%02x", (unsigned int) hash[i]); - } - - return hex_hash; -} - -static void -lm_message_node_add_nick (LmMessageNode *node, const gchar *nick) -{ - LmMessageNode *nick_node; - - nick_node = lm_message_node_add_child (node, "nick", nick); - lm_message_node_set_attribute (nick_node, "xmlns", NS_NICK); -} - -void -lm_message_node_add_own_nick (LmMessageNode *node, - GabbleConnection *connection) -{ - gchar *nick; - GabbleConnectionAliasSource source; - TpBaseConnection *base = (TpBaseConnection *)connection; - - source = _gabble_connection_get_cached_alias (connection, - base->self_handle, &nick); - - if (source > GABBLE_CONNECTION_ALIAS_FROM_JID) - lm_message_node_add_nick (node, nick); - - g_free (nick); -} - -void -lm_message_node_unlink (LmMessageNode *orphan) -{ - if (orphan->parent && orphan == orphan->parent->children) - orphan->parent->children = orphan->next; - if (orphan->prev) - orphan->prev->next = orphan->next; - if (orphan->next) - orphan->next->prev = orphan->prev; -} - -void -lm_message_node_steal_children (LmMessageNode *snatcher, - LmMessageNode *mum) -{ - LmMessageNode *baby; - - g_return_if_fail (snatcher->children == NULL); - - if (mum->children == NULL) - return; - - snatcher->children = mum->children; - mum->children = NULL; - - for (baby = snatcher->children; - baby != NULL; - baby = baby->next) - baby->parent = snatcher; -} - -gboolean -lm_message_node_has_namespace (LmMessageNode *node, - const gchar *ns, - const gchar *tag) -{ - gchar *attribute = NULL; - const gchar *node_ns; - gboolean ret; - - if (tag != NULL) - attribute = g_strconcat ("xmlns:", tag, NULL); - - node_ns = lm_message_node_get_attribute (node, - tag != NULL ? attribute : "xmlns"); - - ret = !tp_strdiff (node_ns, ns); - - g_free (attribute); - - return ret; -} - -LmMessageNode * -lm_message_node_get_child_with_namespace (LmMessageNode *node, - const gchar *name, - const gchar *ns) -{ - LmMessageNode *tmp; - - for (tmp = node->children; - tmp != NULL; - tmp = tmp->next) - { - gchar *tag = NULL; - gboolean found; - - if (tp_strdiff (tmp->name, name)) - { - const gchar *suffix; - - suffix = strchr (tmp->name, ':'); - - if (suffix == NULL) - continue; - else - suffix++; - - if (tp_strdiff (suffix, name)) - continue; - - tag = g_strndup (tmp->name, suffix - tmp->name - 1); - } - - found = lm_message_node_has_namespace (tmp, ns, tag); - - g_free (tag); - - if (found) - return tmp; - } - - return NULL; -} - -/* note: these are only used internally for readability, not part of the API - */ -enum { - BUILD_END = '\0', - BUILD_ATTRIBUTE = '@', - BUILD_CHILD = '(', - BUILD_CHILD_END = ')', - BUILD_POINTER = '*', -}; - -/** - * lm_message_build: - * - * Build an LmMessageNode from a list of arguments employing an - * S-expression-like notation. Example: - * - * lm_message_build ("bob@jabber.org", LM_MESSAGE_TYPE_IQ, - * '(', 'query', 'lala', - * '@', 'xmlns', 'http://jabber.org/protocol/foo', - * ')', - * NULL); - * - * --> <iq to="bob@jabber.org"> - * <query xmlns="http://jabber.org/protocol/foo">lala</query> - * </iq> - */ -G_GNUC_NULL_TERMINATED -LmMessage * -lm_message_build (const gchar *to, LmMessageType type, guint spec, ...) -{ - LmMessage *msg; - va_list ap; - GSList *stack = NULL; - guint arg = spec; - - msg = lm_message_new (to, type); - stack = g_slist_prepend (stack, msg->node); - - va_start (ap, spec); - - while (arg != BUILD_END) - { - switch (arg) - { - case BUILD_END: - goto END; - - case BUILD_ATTRIBUTE: - { - gchar *key = va_arg (ap, gchar *); - gchar *value = va_arg (ap, gchar *); - - g_return_val_if_fail (key != NULL, NULL); - g_return_val_if_fail (value != NULL, NULL); - lm_message_node_set_attribute (stack->data, key, value); - } - break; - - case BUILD_CHILD: - { - gchar *name = va_arg (ap, gchar *); - gchar *value = va_arg (ap, gchar *); - LmMessageNode *child; - - g_return_val_if_fail (name != NULL, NULL); - g_return_val_if_fail (value != NULL, NULL); - child = lm_message_node_add_child (stack->data, name, value); - stack = g_slist_prepend (stack, child); - } - break; - - case BUILD_CHILD_END: - { - GSList *tmp; - - tmp = stack; - stack = stack->next; - tmp->next = NULL; - g_slist_free (tmp); - } - break; - - case BUILD_POINTER: - { - LmMessageNode **node = va_arg (ap, LmMessageNode **); - - g_return_val_if_fail (node != NULL, NULL); - *node = stack->data; - } - break; - - default: - g_assert_not_reached (); - } - - /* Note that we pull out an int-sized value here, whereas our sentinel, - * NULL, is pointer-sized. However, sizeof (void *) should always be >= - * sizeof (uint), so this shouldn't cause a problem. - */ - arg = va_arg (ap, guint); - } - - va_end (ap); - -END: - g_slist_free (stack); - - return msg; -} - -/** - * gabble_decode_jid - * - * Parses a JID which may be one of the following forms: - * server - * server/resource - * username@server - * username@server/resource - * room@service/nick - * and sets the caller's username_room, server_service and resource_nick - * pointers to the username/room, server/service and resource/nick parts - * respectively, if available in the provided JID. The caller may set any of - * the pointers to NULL if they are not interested in a certain component. - * - * The returned values may be NULL or zero-length if a component was either - * not present or zero-length respectively in the given JID. The username/room - * and server/service are lower-cased because the Jabber protocol treats them - * case-insensitively. - */ -void -gabble_decode_jid (const gchar *jid, - gchar **username_room, - gchar **server_service, - gchar **resource_nick) -{ - char *tmp_jid, *tmp_username, *tmp_server, *tmp_resource; - - g_assert (jid != NULL); - - if (username_room != NULL) - *username_room = NULL; - - if (server_service != NULL) - *server_service = NULL; - - if (resource_nick != NULL) - *resource_nick = NULL; - - /* take a local copy so we don't modify the caller's string */ - tmp_jid = g_strdup (jid); - - /* find an @ in username, truncate username to that length, and point - * 'server' to the byte afterwards */ - tmp_server = strchr (tmp_jid, '@'); - if (tmp_server) - { - tmp_username = tmp_jid; - - *tmp_server = '\0'; - tmp_server++; - - /* store the username if the user provided a pointer */ - if (username_room != NULL) - *username_room = g_utf8_strdown (tmp_username, -1); - } - else - { - tmp_username = NULL; - tmp_server = tmp_jid; - } - - /* if we have a server, find a / in it, truncate it to that length, and point - * 'resource' to the byte afterwards. otherwise, do the same to username to - * find any resource there. */ - tmp_resource = strchr (tmp_server, '/'); - if (tmp_resource) - { - *tmp_resource = '\0'; - tmp_resource++; - - /* store the resource if the user provided a pointer */ - if (resource_nick != NULL) - *resource_nick = g_strdup (tmp_resource); - } - - /* the server must be stored after the resource, in case we truncated a - * resource from it */ - if (server_service != NULL) - *server_service = g_utf8_strdown (tmp_server, -1); - - /* free our working copy */ - g_free (tmp_jid); -} - -#define INVALID_ARGUMENT(e, f, ...) \ - G_STMT_START { \ - DEBUG (f, ##__VA_ARGS__); \ - g_set_error (e, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, f, ##__VA_ARGS__);\ - } G_STMT_END - -gchar * -gabble_normalize_room (TpHandleRepoIface *repo, - const gchar *jid, - gpointer context, - GError **error) -{ - char *at = strchr (jid, '@'); - char *slash = strchr (jid, '/'); - - /* there'd better be an @ somewhere after the first character */ - if (at == NULL) - { - INVALID_ARGUMENT (error, - "invalid room JID %s: does not contain '@'", jid); - return NULL; - } - if (at == jid) - { - INVALID_ARGUMENT (error, - "invalid room JID %s: room name before '@' may not be empty", jid); - return NULL; - } - - /* room names can't contain the nick part */ - if (slash != NULL) - { - INVALID_ARGUMENT (error, - "invalid room JID %s: contains nickname part after '/' too", jid); - return NULL; - } - - /* the room and service parts are both case-insensitive, so lowercase - * them both; gabble_decode_jid is overkill here - */ - return g_utf8_strdown (jid, -1); -} - -gchar * -gabble_remove_resource (const gchar *jid) -{ - char *slash = strchr (jid, '/'); - gchar *buf; - - if (slash == NULL) - return g_strdup (jid); - - /* The user and domain parts can't contain '/', assuming it's valid */ - buf = g_malloc (slash - jid + 1); - strncpy (buf, jid, slash - jid); - buf[slash - jid] = '\0'; - - return buf; -} - -gchar * -gabble_normalize_contact (TpHandleRepoIface *repo, - const gchar *jid, - gpointer context, - GError **error) -{ - guint mode = GPOINTER_TO_UINT (context); - gchar *username = NULL, *server = NULL, *resource = NULL; - gchar *ret = NULL; - - gabble_decode_jid (jid, &username, &server, &resource); - - if (!username || !server || !username[0] || !server[0]) - { - INVALID_ARGUMENT (error, - "jid %s has invalid username or server", jid); - goto OUT; - } - - if (mode == GABBLE_JID_ROOM_MEMBER && resource == NULL) - { - INVALID_ARGUMENT (error, - "jid %s can't be a room member - it has no resource", jid); - goto OUT; - } - - if (mode != GABBLE_JID_GLOBAL && resource != NULL) - { - ret = g_strdup_printf ("%s@%s/%s", username, server, resource); - - if (mode == GABBLE_JID_ROOM_MEMBER - || (repo != NULL - && tp_dynamic_handle_repo_lookup_exact (repo, ret))) - { - /* either we know from context that it's a room member, or we - * already saw that contact in a room. Use ret as our answer - */ - goto OUT; - } - else - { - g_free (ret); - } - } - - /* if we get here, we suspect it's a global JID, either because the context - * says it is, or because the context isn't sure and we haven't seen it in - * use as a room member - */ - ret = g_strdup_printf ("%s@%s", username, server); - -OUT: - g_free (username); - g_free (server); - g_free (resource); - return ret; -} diff --git a/src/util.h b/src/util.h deleted file mode 100644 index efd8fe958..000000000 --- a/src/util.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * util.h - Headers for Gabble utility functions - * Copyright (C) 2006 Collabora Ltd. - * Copyright (C) 2006 Nokia Corporation - * @author Robert McQueen <robert.mcqueen@collabora.co.uk> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef __GABBLE_UTIL_H__ -#define __GABBLE_UTIL_H__ - -#include <glib.h> -#include <telepathy-glib/handle-repo.h> -#include <telepathy-glib/util.h> -#include <loudmouth/loudmouth.h> - -#include "gabble-types.h" - -gchar *sha1_hex (const gchar *bytes, guint len); -void lm_message_node_add_own_nick (LmMessageNode *node, - GabbleConnection *conn); -void lm_message_node_unlink (LmMessageNode *orphan); -void lm_message_node_steal_children (LmMessageNode *snatcher, - LmMessageNode *mum); -gboolean lm_message_node_has_namespace (LmMessageNode *node, const gchar *ns, - const gchar *tag); -LmMessageNode *lm_message_node_get_child_with_namespace (LmMessageNode *node, - const gchar *name, const gchar *ns); -G_GNUC_NULL_TERMINATED LmMessage *lm_message_build (const gchar *to, - LmMessageType type, guint spec, ...); - -/* format: a@b/c */ -void gabble_decode_jid (const gchar *jid, gchar **a, gchar **b, gchar **c); - -gchar *gabble_remove_resource (const gchar *jid); -gchar *gabble_normalize_contact (TpHandleRepoIface *repo, const gchar *jid, - gpointer userdata, GError **error); -gchar *gabble_normalize_room (TpHandleRepoIface *repo, const gchar *jid, - gpointer context, GError **error); - -#endif /* __GABBLE_UTIL_H__ */ diff --git a/src/vcard-manager.c b/src/vcard-manager.c deleted file mode 100644 index 837ff6ffd..000000000 --- a/src/vcard-manager.c +++ /dev/null @@ -1,1470 +0,0 @@ -/* - * vcard-manager.c - Source for Gabble vCard lookup helper - * - * Copyright (C) 2006 Collabora Ltd. - * Copyright (C) 2006 Nokia Corporation - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "vcard-manager.h" - -#include <telepathy-glib/dbus.h> - -#define DEBUG_FLAG GABBLE_DEBUG_VCARD - -#include "base64.h" -#include "debug.h" -#include "gabble-connection.h" -#include "namespaces.h" -#include <telepathy-glib/dbus.h> -#include <telepathy-glib/heap.h> -#include "time.h" -#include "util.h" - -#define DEFAULT_REQUEST_TIMEOUT 20000 -#define VCARD_CACHE_ENTRY_TTL 30 -#define VCARD_PIPELINE_SIZE 3 - -static const gchar *NO_ALIAS = "none"; - -/* signal enum */ -enum -{ - NICKNAME_UPDATE, - GOT_SELF_INITIAL_AVATAR, - LAST_SIGNAL -}; - -static guint signals[LAST_SIGNAL] = {0}; - -/* Properties */ -enum -{ - PROP_CONNECTION = 1, - PROP_HAVE_SELF_AVATAR, - LAST_PROPERTY -}; - -G_DEFINE_TYPE(GabbleVCardManager, gabble_vcard_manager, G_TYPE_OBJECT); - -typedef struct _GabbleVCardCacheEntry GabbleVCardCacheEntry; - -typedef struct _GabbleVCardManagerPrivate GabbleVCardManagerPrivate; -struct _GabbleVCardManagerPrivate -{ - GabbleConnection *connection; - GSList *requests; - GSList *request_pipeline; - GSList *reqs_in_flight; - - GHashTable *cache; - TpHeap *timed_cache; - guint cache_timer; - - gboolean have_self_avatar; - gboolean dispose_has_run; -}; - -struct _GabbleVCardManagerRequest -{ - GabbleVCardManager *manager; - guint timer_id; - guint timeout; - - TpHandle handle; - gchar **edit_args; - - GabbleVCardManagerCb callback; - gpointer user_data; - GObject *bound_object; -}; - -struct _GabbleVCardCacheEntry -{ - GabbleVCardManager *manager; - TpHandle handle; - GabbleVCardManagerRequest *request; - GSList *pending_requests; - time_t received; - LmMessage *message; -}; - -typedef struct _GabbleVCardPipelineItem GabbleVCardPipelineItem; -struct _GabbleVCardPipelineItem { - GabbleVCardManagerRequest *request; - LmMessage *msg; - gpointer callback; -}; - -GQuark -gabble_vcard_manager_error_quark (void) -{ - static GQuark quark = 0; - if (!quark) - quark = g_quark_from_static_string ("gabble-vcard-manager-error"); - return quark; -} - -GQuark -gabble_vcard_manager_cache_quark (void) -{ - static GQuark quark = 0; - if (!quark) - quark = g_quark_from_static_string ("gabble-vcard-manager-cache"); - return quark; -} - -#define GABBLE_VCARD_MANAGER_GET_PRIVATE(o)\ - ((GabbleVCardManagerPrivate*)((o)->priv)); - -static void cache_entry_free (void *data); -static gint cache_entry_compare (gconstpointer a, gconstpointer b); - -static void -gabble_vcard_manager_init (GabbleVCardManager *obj) -{ - GabbleVCardManagerPrivate *priv = - G_TYPE_INSTANCE_GET_PRIVATE (obj, GABBLE_TYPE_VCARD_MANAGER, - GabbleVCardManagerPrivate); - obj->priv = priv; - - priv->cache = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, - cache_entry_free); - /* no destructor here - the hash table is responsible for freeing it */ - priv->timed_cache = tp_heap_new (cache_entry_compare, NULL); -} - -static void gabble_vcard_manager_set_property (GObject *object, - guint property_id, const GValue *value, GParamSpec *pspec); -static void gabble_vcard_manager_get_property (GObject *object, - guint property_id, GValue *value, GParamSpec *pspec); -static void gabble_vcard_manager_dispose (GObject *object); -static void gabble_vcard_manager_finalize (GObject *object); - -static void -gabble_vcard_manager_class_init (GabbleVCardManagerClass *gabble_vcard_manager_class) -{ - GObjectClass *object_class = G_OBJECT_CLASS (gabble_vcard_manager_class); - GParamSpec *param_spec; - - g_type_class_add_private (gabble_vcard_manager_class, - sizeof (GabbleVCardManagerPrivate)); - - object_class->get_property = gabble_vcard_manager_get_property; - object_class->set_property = gabble_vcard_manager_set_property; - - object_class->dispose = gabble_vcard_manager_dispose; - object_class->finalize = gabble_vcard_manager_finalize; - - param_spec = g_param_spec_object ("connection", "GabbleConnection object", - "Gabble connection object that owns this " - "vCard lookup helper object.", - GABBLE_TYPE_CONNECTION, - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_READWRITE | - G_PARAM_STATIC_NICK | - G_PARAM_STATIC_BLURB); - g_object_class_install_property (object_class, PROP_CONNECTION, param_spec); - - param_spec = g_param_spec_boolean ("have-self-avatar", "Have our own avatar", - "TRUE after the local user's own vCard " - "has been retrieved in order to get" - "their initial avatar.", - FALSE, - G_PARAM_READABLE | - G_PARAM_STATIC_NICK | - G_PARAM_STATIC_BLURB); - g_object_class_install_property (object_class, PROP_HAVE_SELF_AVATAR, - param_spec); - - /* signal definitions */ - - signals[NICKNAME_UPDATE] = - g_signal_new ("nickname-update", - G_TYPE_FROM_CLASS (gabble_vcard_manager_class), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, - g_cclosure_marshal_VOID__UINT, - G_TYPE_NONE, 1, G_TYPE_UINT); - - signals[GOT_SELF_INITIAL_AVATAR] = - g_signal_new ("got-self-initial-avatar", - G_TYPE_FROM_CLASS (gabble_vcard_manager_class), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, - g_cclosure_marshal_VOID__STRING, - G_TYPE_NONE, 1, G_TYPE_STRING); -} - -static void -gabble_vcard_manager_get_property (GObject *object, - guint property_id, - GValue *value, - GParamSpec *pspec) -{ - GabbleVCardManager *chan = GABBLE_VCARD_MANAGER (object); - GabbleVCardManagerPrivate *priv = GABBLE_VCARD_MANAGER_GET_PRIVATE (chan); - - switch (property_id) { - case PROP_CONNECTION: - g_value_set_object (value, priv->connection); - break; - case PROP_HAVE_SELF_AVATAR: - g_value_set_boolean (value, priv->have_self_avatar); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } -} - -static void -gabble_vcard_manager_set_property (GObject *object, - guint property_id, - const GValue *value, - GParamSpec *pspec) -{ - GabbleVCardManager *chan = GABBLE_VCARD_MANAGER (object); - GabbleVCardManagerPrivate *priv = GABBLE_VCARD_MANAGER_GET_PRIVATE (chan); - - switch (property_id) { - case PROP_CONNECTION: - priv->connection = g_value_get_object (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } -} - -static void delete_request (GabbleVCardManagerRequest *request); -static void cancel_request (GabbleVCardManagerRequest *request); - -static gint -cache_entry_compare (gconstpointer a, gconstpointer b) -{ - const GabbleVCardCacheEntry *foo = a; - const GabbleVCardCacheEntry *bar = b; - return foo->received - bar->received; -} - -static void -cache_entry_free (void *data) -{ - GabbleVCardManagerPrivate *priv; - TpHandleRepoIface *contact_repo; - GabbleVCardCacheEntry *entry = (GabbleVCardCacheEntry *) data; - - g_assert (entry != NULL); - - while (entry->pending_requests) - { - cancel_request (entry->pending_requests->data); - } - - if (entry->request) - { - delete_request (entry->request); - } - - g_slist_free (entry->pending_requests); - - if (entry->message) - lm_message_unref (entry->message); - - priv = GABBLE_VCARD_MANAGER_GET_PRIVATE (entry->manager); - contact_repo = tp_base_connection_get_handles ( - (TpBaseConnection *)priv->connection, TP_HANDLE_TYPE_CONTACT); - - tp_handle_unref (contact_repo, entry->handle); - - g_slice_free (GabbleVCardCacheEntry, entry); -} - -static GabbleVCardCacheEntry * -cache_entry_get (GabbleVCardManager *manager, TpHandle handle) -{ - GabbleVCardManagerPrivate *priv = GABBLE_VCARD_MANAGER_GET_PRIVATE (manager); - TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( - (TpBaseConnection *)priv->connection, TP_HANDLE_TYPE_CONTACT); - GabbleVCardCacheEntry *entry; - - entry = g_hash_table_lookup (priv->cache, GUINT_TO_POINTER (handle)); - if (entry) - return entry; - - entry = g_slice_new0 (GabbleVCardCacheEntry); - - entry->manager = manager; - entry->handle = handle; - tp_handle_ref (contact_repo, handle); - g_hash_table_insert (priv->cache, GUINT_TO_POINTER (handle), entry); - - return entry; -} - -static gboolean -cache_entry_timeout (gpointer data) -{ - GabbleVCardManager *manager = data; - GabbleVCardManagerPrivate *priv = GABBLE_VCARD_MANAGER_GET_PRIVATE (manager); - GabbleVCardCacheEntry *entry; - - time_t now = time (NULL); - - while (NULL != (entry = tp_heap_peek_first (priv->timed_cache))) - { - if ((entry->received + VCARD_CACHE_ENTRY_TTL) > now) - break; - - /* shouldn't have in-flight request nor any pending requests */ - g_assert (entry->request == NULL); - - gabble_vcard_manager_invalidate_cache (manager, entry->handle); - } - - priv->cache_timer = 0; - - if (entry) - { - priv->cache_timer = g_timeout_add ( - 1000 * (entry->received + VCARD_CACHE_ENTRY_TTL - time (NULL)), - cache_entry_timeout, manager); - } - - return FALSE; -} - - -static void -cache_entry_attempt_to_free (GabbleVCardManager *manager, TpHandle handle) -{ - GabbleVCardManagerPrivate *priv = GABBLE_VCARD_MANAGER_GET_PRIVATE (manager); - GabbleVCardCacheEntry *entry = g_hash_table_lookup (priv->cache, - GUINT_TO_POINTER (handle)); - - if (!entry || entry->message || entry->pending_requests) - return; - - tp_heap_remove (priv->timed_cache, entry); - - g_hash_table_remove (priv->cache, GUINT_TO_POINTER (entry->handle)); -} - -void -gabble_vcard_manager_invalidate_cache (GabbleVCardManager *manager, - TpHandle handle) -{ - GabbleVCardManagerPrivate *priv = GABBLE_VCARD_MANAGER_GET_PRIVATE (manager); - GabbleVCardCacheEntry *entry = g_hash_table_lookup (priv->cache, - GUINT_TO_POINTER (handle)); - TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( - (TpBaseConnection *)priv->connection, TP_HANDLE_TYPE_CONTACT); - - g_return_if_fail (tp_handle_is_valid (contact_repo, handle, NULL)); - - if (!entry) - return; - - tp_heap_remove (priv->timed_cache, entry); - - if (entry->message) - { - lm_message_unref (entry->message); - entry->message = NULL; - } - - cache_entry_attempt_to_free (manager, handle); -} - -static void -cache_entry_fill (GabbleVCardManager *manager, LmMessage *msg, TpHandle handle) -{ - GabbleVCardCacheEntry *entry = cache_entry_get (manager, handle); - GabbleVCardManagerPrivate *priv = GABBLE_VCARD_MANAGER_GET_PRIVATE (manager); - - /* the upstream request should be finished by now */ - g_assert (entry->request == NULL); - - g_assert (entry->message == NULL); - entry->message = lm_message_new ("", LM_MESSAGE_TYPE_IQ); - lm_message_node_steal_children (entry->message->node, msg->node); - entry->received = time (NULL); - tp_heap_add (priv->timed_cache, entry); - - if (priv->cache_timer == 0) - { - GabbleVCardCacheEntry *first = tp_heap_peek_first (priv->timed_cache); - priv->cache_timer = g_timeout_add ((first->received + - VCARD_CACHE_ENTRY_TTL - time (NULL)) * 1000, cache_entry_timeout, - manager); - } -} - -static void -complete_pending_requests (GabbleVCardManager *manager, - TpHandle handle, - GError *err) -{ - GabbleVCardManagerPrivate *priv = GABBLE_VCARD_MANAGER_GET_PRIVATE (manager); - GabbleVCardCacheEntry *entry = - g_hash_table_lookup (priv->cache, GUINT_TO_POINTER (handle)); - LmMessageNode *vcard_node = NULL; - - g_assert (entry); - - /* the upstream request should be finished by now */ - g_assert (entry->request == NULL); - - if (entry->message) - vcard_node = lm_message_node_get_child (entry->message->node, "vCard"); - - while (entry->pending_requests) - { - GabbleVCardManagerRequest *req = entry->pending_requests->data; - - if (req->callback) - { - DEBUG ("invoking callback %p for pending request %p (handle %u)", - req->callback, req, handle); - (req->callback)(req->manager, req, handle, - vcard_node, err, req->user_data); - } - delete_request (req); - } - - cache_entry_attempt_to_free (manager, handle); - - if (err) - g_error_free (err); -} - -static void -complete_one_request (GabbleVCardManagerRequest *request, - gpointer data, - GError *err) -{ - GabbleVCardManager *manager = request->manager; - TpHandle handle = request->handle; - - if (request->callback) - { - DEBUG ("invoking callback %p for request %p (handle %u)", - request->callback, request, handle); - (request->callback)(manager, request, handle, - data, err, request->user_data); - } - delete_request (request); - - cache_entry_attempt_to_free (manager, handle); - - if (err) - g_error_free (err); -} - -void -gabble_vcard_manager_dispose (GObject *object) -{ - GabbleVCardManager *self = GABBLE_VCARD_MANAGER (object); - GabbleVCardManagerPrivate *priv = GABBLE_VCARD_MANAGER_GET_PRIVATE (self); - DBusGProxy *bus_proxy; - bus_proxy = tp_get_bus_proxy (); - - if (priv->dispose_has_run) - return; - - priv->dispose_has_run = TRUE; - - if (priv->cache_timer) - g_source_remove (priv->cache_timer); - - /* cancel request removes the element from the list after cancelling */ - while (priv->requests) - cancel_request (priv->requests->data); - - tp_heap_destroy (priv->timed_cache); - g_hash_table_destroy (priv->cache); - - /* should've been cleared by delete_request already */ - g_assert (priv->reqs_in_flight == NULL); - g_assert (priv->request_pipeline == NULL); - - if (G_OBJECT_CLASS (gabble_vcard_manager_parent_class)->dispose) - G_OBJECT_CLASS (gabble_vcard_manager_parent_class)->dispose (object); -} - -void -gabble_vcard_manager_finalize (GObject *object) -{ - G_OBJECT_CLASS (gabble_vcard_manager_parent_class)->finalize (object); -} - -/* Called during connection. */ -static void -initial_request_cb (GabbleVCardManager *self, - GabbleVCardManagerRequest *request, - TpHandle handle, - LmMessageNode *vcard, - GError *error, - gpointer user_data) -{ - GabbleVCardManagerPrivate *priv = GABBLE_VCARD_MANAGER_GET_PRIVATE (self); - gchar *alias = (gchar *)user_data; - LmMessageNode *node; - - if (!vcard) - { - g_free (alias); - return; - } - - /* We now have our own avatar (or lack thereof) so can answer - * GetAvatarTokens([self_handle]) - */ - priv->have_self_avatar = TRUE; - - /* try to patch the alias, if one was provided */ - if (alias) - { - DEBUG ("Patching our vCard to have alias \"%s\"", alias); - node = lm_message_node_get_child (vcard, "NICKNAME"); - if (node) - { - lm_message_node_set_value (node, alias); - } - else - { - lm_message_node_add_child (vcard, "NICKNAME", alias); - } - - /* basically ignore error here, there's not a lot we can do about it */ - gabble_vcard_manager_replace (self, vcard, 0, NULL, NULL, - G_OBJECT (priv->connection), NULL); - } - - /* Do we have an avatar already? If so, the presence cache ought to be - * told (anyone else's avatar SHA-1 we'd get from their presence, - * but unless we have another XEP-0153 resource connected, we never - * see our own presence) - */ - node = lm_message_node_get_child (vcard, "PHOTO"); - if (node) - { - DEBUG ("Our vCard has a PHOTO %p", node); - LmMessageNode *binval = lm_message_node_get_child (node, "BINVAL"); - - if (binval) - { - const gchar *binval_value; - - binval_value = lm_message_node_get_value (binval); - - if (binval_value) - { - gchar *sha1; - GString *avatar; - - avatar = base64_decode (binval_value); - - if (avatar) - { - sha1 = sha1_hex (avatar->str, avatar->len); - DEBUG ("Successfully decoded PHOTO.BINVAL, SHA-1 %s", sha1); - g_signal_emit (self, signals[GOT_SELF_INITIAL_AVATAR], 0, - sha1); - g_free (sha1); - } - else - { - DEBUG ("Avatar is in garbled Base64, ignoring it:\n%s", - lm_message_node_get_value (binval)); - } - - g_string_free (avatar, TRUE); - } - } - } - - g_free (alias); -} - -static void -status_changed_cb (GObject *object, - guint status, - guint reason, - gpointer user_data) -{ - GabbleVCardManager *self = GABBLE_VCARD_MANAGER (user_data); - GabbleConnection *conn = GABBLE_CONNECTION (object); - TpBaseConnection *base = (TpBaseConnection *)conn; - - if (status == TP_CONNECTION_STATUS_CONNECTED) - { - gchar *alias; - GabbleConnectionAliasSource alias_src; - - /* if we have a better alias, patch it into our vCard on the server */ - alias_src = _gabble_connection_get_cached_alias (conn, - base->self_handle, - &alias); - if (alias_src < GABBLE_CONNECTION_ALIAS_FROM_VCARD) - { - /* this alias isn't reliable enough to want to patch it in */ - g_free (alias); - alias = NULL; - } - - /* fetch our vCard, and possibly patch it to include our new alias */ - gabble_vcard_manager_request (self, base->self_handle, 0, - initial_request_cb, alias, - G_OBJECT (conn), NULL); - } -} - -/** - * gabble_vcard_manager_new: - * @conn: The #GabbleConnection to use for vCard lookup - * - * Creates an object to use for Jabber vCard lookup (JEP 0054). - * There should be one of these per connection - */ -GabbleVCardManager * -gabble_vcard_manager_new (GabbleConnection *conn) -{ - GabbleVCardManager *self; - - g_return_val_if_fail (GABBLE_IS_CONNECTION (conn), NULL); - - self = GABBLE_VCARD_MANAGER (g_object_new (GABBLE_TYPE_VCARD_MANAGER, - "connection", conn, NULL)); - g_signal_connect (conn, "status-changed", - G_CALLBACK (status_changed_cb), self); - return self; -} - -static void notify_delete_request (gpointer data, GObject *obj); -static void vcard_pipeline_go (GabbleVCardManager *manager); - -static void -delete_request (GabbleVCardManagerRequest *request) -{ - GabbleVCardManager *manager = request->manager; - GabbleVCardManagerPrivate *priv; - TpHandleRepoIface *contact_repo; - GabbleVCardCacheEntry *entry; - GSList *l; - - DEBUG ("Discarding request %p", request); - - g_assert (NULL != request); - g_assert (GABBLE_IS_VCARD_MANAGER (manager)); - - priv = GABBLE_VCARD_MANAGER_GET_PRIVATE (manager); - contact_repo = tp_base_connection_get_handles ( - (TpBaseConnection *)priv->connection, TP_HANDLE_TYPE_CONTACT); - - g_assert (NULL != g_slist_find (priv->requests, request)); - - priv->requests = g_slist_remove (priv->requests, request); - - for (l = priv->request_pipeline; l; l = g_slist_next (l)) - { - GabbleVCardPipelineItem *item = l->data; - if (item->request == request) - { - priv->request_pipeline = g_slist_remove (priv->request_pipeline, - item); - lm_message_unref (item->msg); - g_free (item); - break; - } - } - - if (g_slist_find (priv->reqs_in_flight, request)) - { - priv->reqs_in_flight = g_slist_remove (priv->reqs_in_flight, request); - vcard_pipeline_go (manager); - } - - if (NULL != request->bound_object) - { - g_object_weak_unref (request->bound_object, notify_delete_request, - request); - } - - if (0 != request->timer_id) - { - g_source_remove (request->timer_id); - } - - tp_handle_unref (contact_repo, request->handle); - - entry = g_hash_table_lookup (priv->cache, - GUINT_TO_POINTER (request->handle)); - if (entry) - { - if (request == entry->request) - { - entry->request = NULL; - } - else - { - entry->pending_requests = g_slist_remove (entry->pending_requests, - request); - } - } - - g_strfreev (request->edit_args); - - g_slice_free (GabbleVCardManagerRequest, request); -} - -static gboolean -timeout_request (gpointer data) -{ - GabbleVCardManagerRequest *request = (GabbleVCardManagerRequest*) data; - GError *err /* doesn't need initializing */; - g_return_val_if_fail (data != NULL, FALSE); - - err = g_error_new (GABBLE_VCARD_MANAGER_ERROR, - GABBLE_VCARD_MANAGER_ERROR_TIMEOUT, "Request timed out"); - DEBUG ("Request %p timed out, notifying callback %p", - request, request->callback); - - request->timer_id = 0; - complete_one_request (request, NULL, err); - return FALSE; -} - -static void -cancel_request (GabbleVCardManagerRequest *request) -{ - GError *err = g_error_new (GABBLE_VCARD_MANAGER_ERROR, - GABBLE_VCARD_MANAGER_ERROR_CANCELLED, "Request cancelled"); - - g_assert (request != NULL); - - DEBUG ("Request %p cancelled, notifying callback %p", - request, request->callback); - - complete_one_request (request, NULL, err); -} - -static gchar * -extract_nickname (LmMessageNode *vcard_node) -{ - LmMessageNode *node; - const gchar *nick; - gchar **bits; - gchar *ret; - - node = lm_message_node_get_child (vcard_node, "NICKNAME"); - - if (node == NULL) - return NULL; - - nick = lm_message_node_get_value (node); - - /* nick is comma-separated, we want the first one. rule out corner cases of - * the entire string or the first value being empty before we g_strsplit */ - if (nick == NULL || *nick == '\0' || *nick == ',') - return NULL; - - bits = g_strsplit (nick, ",", 2); - - ret = g_strdup (bits[0]); - - g_strfreev (bits); - - return ret; -} - -static void -observe_vcard (GabbleConnection *conn, - GabbleVCardManager *manager, - TpHandle handle, - LmMessageNode *vcard_node) -{ - TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( - (TpBaseConnection *)conn, TP_HANDLE_TYPE_CONTACT); - const gchar *field = "<NICKNAME>"; - gchar *alias; - const gchar *old_alias; - - alias = extract_nickname (vcard_node); - - if (alias == NULL) - { - LmMessageNode *fn_node = lm_message_node_get_child (vcard_node, "FN"); - - if (fn_node != NULL) - { - const gchar *fn = lm_message_node_get_value (fn_node); - - if (fn != NULL && *fn != '\0') - { - field = "<FN>"; - alias = g_strdup (fn); - } - } - } - - old_alias = gabble_vcard_manager_get_cached_alias (manager, handle); - - if (!tp_strdiff (alias, old_alias)) - { -#ifdef ENABLE_DEBUG - if (alias != NULL) - DEBUG ("no change to vCard alias \"%s\" for handle %u", alias, handle); - else - DEBUG ("still no vCard alias for handle %u", handle); -#endif - - g_free (alias); - return; - } - - if (alias != NULL) - { - DEBUG ("got vCard alias \"%s\" for handle %u from %s", alias, - handle, field); - - /* takes ownership of alias */ - tp_handle_set_qdata (contact_repo, handle, - gabble_vcard_manager_cache_quark (), alias, g_free); - } - else - { - DEBUG ("got no vCard alias for handle %u", handle); - - tp_handle_set_qdata (contact_repo, handle, - gabble_vcard_manager_cache_quark (), (gchar *) NO_ALIAS, NULL); - } - - g_signal_emit (G_OBJECT (manager), signals[NICKNAME_UPDATE], 0, handle); -} - -static LmHandlerResult -replace_reply_cb (GabbleConnection *conn, LmMessage *sent_msg, - LmMessage *reply_msg, GObject *object, gpointer user_data) -{ - GabbleVCardManagerRequest *request = (GabbleVCardManagerRequest*) user_data; - GabbleVCardManager *manager = GABBLE_VCARD_MANAGER (object); - GabbleVCardManagerPrivate *priv = GABBLE_VCARD_MANAGER_GET_PRIVATE (manager); - TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( - (TpBaseConnection *)conn, TP_HANDLE_TYPE_CONTACT); - GError *err = NULL; - - g_assert (request); - - DEBUG ("Replace request got a reply: conn@%p, sent_msg@%p, reply_msg@%p, " - "bound object@%p, request@%p", conn, sent_msg, reply_msg, object, - user_data); - - if (!g_slist_find (priv->requests, request)) - { - DEBUG ("I don't care about that request any more"); - return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; - } - - g_assert (tp_handle_is_valid (contact_repo, request->handle, NULL)); - - if (lm_message_get_sub_type (reply_msg) == LM_MESSAGE_SUB_TYPE_ERROR) - { - LmMessageNode *error_node; - - error_node = lm_message_node_get_child (reply_msg->node, "error"); - if (error_node) - { - err = gabble_xmpp_error_to_g_error ( - gabble_xmpp_error_from_node (error_node)); - } - - if (err == NULL) - { - err = g_error_new (GABBLE_VCARD_MANAGER_ERROR, - GABBLE_VCARD_MANAGER_ERROR_UNKNOWN, - "an unknown error occurred"); - } - } - - DEBUG ("Request %p %s, notifying callback %p", request, - err ? "failed" : "succeeded", request->callback); - - complete_one_request (request, sent_msg, err); - - return LM_HANDLER_RESULT_REMOVE_MESSAGE; -} - -static GabbleVCardManagerRequest *request_send (GabbleVCardManagerRequest *, - LmMessageNode *replacement, const gchar *jid, GError **error); - -static LmHandlerResult -request_reply_cb (GabbleConnection *conn, - LmMessage *sent_msg, - LmMessage *reply_msg, - GObject *object, - gpointer user_data) -{ - GabbleVCardManagerRequest *request = (GabbleVCardManagerRequest*) user_data; - GabbleVCardManager *manager = GABBLE_VCARD_MANAGER (object); - GabbleVCardManagerPrivate *priv = GABBLE_VCARD_MANAGER_GET_PRIVATE (manager); - TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( - (TpBaseConnection *)conn, TP_HANDLE_TYPE_CONTACT); - LmMessageNode *vcard_node = NULL; - GError *err = NULL; - - g_assert (request); - - DEBUG ("Fetch request got a reply: conn@%p, sent_msg@%p, reply_msg@%p, " - "bound object@%p, request@%p", conn, sent_msg, reply_msg, object, - user_data); - - if (!g_slist_find (priv->requests, request)) - { - DEBUG ("I don't care about that request any more"); - return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS; - } - - g_assert (tp_handle_is_valid (contact_repo, request->handle, NULL)); - - if (lm_message_get_sub_type (reply_msg) == LM_MESSAGE_SUB_TYPE_ERROR) - { - LmMessageNode *error_node; - - vcard_node = NULL; - - error_node = lm_message_node_get_child (reply_msg->node, "error"); - if (error_node) - { - err = gabble_xmpp_error_to_g_error ( - gabble_xmpp_error_from_node (error_node)); - } - - if (err == NULL) - { - err = g_error_new (GABBLE_VCARD_MANAGER_ERROR, - GABBLE_VCARD_MANAGER_ERROR_UNKNOWN, - "an unknown error occurred"); - } - } - else - { - vcard_node = lm_message_node_get_child (reply_msg->node, "vCard"); - - if (NULL == vcard_node) - { - DEBUG ("successful lookup response contained no <vCard> node, " - "creating an empty one"); - - vcard_node = lm_message_node_add_child (reply_msg->node, "vCard", - NULL); - lm_message_node_set_attribute (vcard_node, "xmlns", NS_VCARD_TEMP); - } - - if (!request->edit_args) - { - observe_vcard (conn, manager, request->handle, vcard_node); - } - /* else we'll observe the vcard after editing it if we intend - * to modify it */ - } - - if (vcard_node && request->edit_args) - { - gchar **ptr; - for (ptr = request->edit_args; *ptr; ptr++) - { - gchar *key = *ptr; - gchar *value = *(++ptr); - LmMessageNode *node; - - if (!value) - { - /* oops, someone passed in an odd number of args. */ - g_assert_not_reached (); - break; - } - - node = lm_message_node_get_child (vcard_node, key); - if (node) - { - lm_message_node_set_value (node, value); - } - else - { - node = lm_message_node_add_child (vcard_node, key, value); - } - } - - observe_vcard (conn, manager, request->handle, vcard_node); - - /* XXX: Manually remove the request from the list of requests in flight, - * so that it's not in there twice when we send it a second time. The - * correct fix is to remove it from reqs_in_flight as soon as we get the - * result IQ. - */ - priv->reqs_in_flight = g_slist_remove (priv->reqs_in_flight, request); - - request_send (request, vcard_node, NULL, &err); - - if (err) - { - complete_one_request (request, NULL, err); - } - else - { - DEBUG ("Request %p fetch succeeded", request); - } - } - else - { - GabbleVCardCacheEntry *entry = g_hash_table_lookup (priv->cache, - GUINT_TO_POINTER (request->handle)); - - if (entry) - { - DEBUG ("entry == %p, entry->request == %p, request == %p", - entry, entry->request, request); - g_assert (entry->request == request); - - entry->request = NULL; - cache_entry_fill (manager, reply_msg, request->handle); - complete_pending_requests (manager, request->handle, err); - delete_request (request); - } - else - { - complete_one_request (request, vcard_node, err); - } - } - - return LM_HANDLER_RESULT_REMOVE_MESSAGE; -} - -static void -vcard_pipeline_send_next_request (GabbleVCardManager *manager) -{ - GError *error = NULL; - GabbleVCardManagerPrivate *priv = GABBLE_VCARD_MANAGER_GET_PRIVATE (manager); - - if (priv->request_pipeline == NULL) - return; - - GabbleVCardPipelineItem *item = priv->request_pipeline->data; - priv->request_pipeline = g_slist_remove (priv->request_pipeline, item); - - DEBUG ("launching request %p", item->request); - - if (!_gabble_connection_send_with_reply (priv->connection, item->msg, - item->callback, G_OBJECT(manager), item->request, &error)) - { - GabbleVCardCacheEntry *entry = g_hash_table_lookup (priv->cache, - GUINT_TO_POINTER (item->request->handle)); - if (entry && entry->request == item->request) - { - entry->request = NULL; - complete_pending_requests (manager, item->request->handle, error); - delete_request (item->request); - } - else - { - complete_one_request (item->request, NULL, error); - } - vcard_pipeline_send_next_request (manager); - } - else - { - priv->reqs_in_flight = g_slist_prepend (priv->reqs_in_flight, - item->request); - } - - lm_message_unref (item->msg); - g_free (item); -} - -static void -vcard_pipeline_go (GabbleVCardManager *manager) -{ - GabbleVCardManagerPrivate *priv = GABBLE_VCARD_MANAGER_GET_PRIVATE (manager); - - DEBUG ("called"); - - while (priv->request_pipeline && - (g_slist_length (priv->reqs_in_flight) < VCARD_PIPELINE_SIZE)) - { - vcard_pipeline_send_next_request (manager); - } -} - -static void -vcard_pipeline_enqueue (GabbleVCardManagerRequest *request, LmMessage *msg, - gpointer callback) -{ - GabbleVCardManager *self = request->manager; - GabbleVCardManagerPrivate *priv = GABBLE_VCARD_MANAGER_GET_PRIVATE (self); - GabbleVCardPipelineItem *item = g_new0 (GabbleVCardPipelineItem, 1); - - item->request = request; - item->msg = msg; - item->callback = callback; - - DEBUG ("enqueue request %p", request); - - priv->request_pipeline = g_slist_append (priv->request_pipeline, item); - if (request->timeout && (0 == request->timer_id)) - { - request->timer_id = - g_timeout_add (request->timeout, timeout_request, request); - } -} - -/* If @replacement is NULL sends a request, calling request_reply_cb when - * it returns. - * - * Otherwise steals its children and sends an update, calling - * replace_reply_cb when it returns. - * - * Frees the @request on error, returns it on success. */ -static GabbleVCardManagerRequest * -request_send (GabbleVCardManagerRequest *request, - LmMessageNode *replacement, - const gchar *jid, - GError **error) -{ - GabbleVCardManager *self = request->manager; - GabbleVCardManagerPrivate *priv = GABBLE_VCARD_MANAGER_GET_PRIVATE (self); - TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( - (TpBaseConnection *)priv->connection, TP_HANDLE_TYPE_CONTACT); - LmMessage *msg; - LmMessageNode *lm_node; - - DEBUG ("Sending off request %p to %s for %s", request, - replacement ? "replace vCard" : "retrieve vCard", - jid ? jid : "myself"); - msg = lm_message_new_with_sub_type (jid, LM_MESSAGE_TYPE_IQ, - (replacement - ? LM_MESSAGE_SUB_TYPE_SET - : LM_MESSAGE_SUB_TYPE_GET)); - lm_node = lm_message_node_add_child (msg->node, "vCard", NULL); - lm_message_node_set_attribute (lm_node, "xmlns", NS_VCARD_TEMP); - - if (replacement) - lm_message_node_steal_children (lm_node, replacement); - - g_assert (tp_handle_is_valid (contact_repo, request->handle, NULL)); - - vcard_pipeline_enqueue (request, msg, - (replacement ? replace_reply_cb : request_reply_cb)); - - vcard_pipeline_go (self); - - return request; -} - -static void -notify_delete_request (gpointer data, GObject *obj) -{ - GabbleVCardManagerRequest *request = (GabbleVCardManagerRequest *) data; - GabbleVCardManager *manager = request->manager; - TpHandle handle = request->handle; - - request->bound_object = NULL; - delete_request (request); - cache_entry_attempt_to_free (manager, handle); -} - -/* Request the vCard for the given handle. When it arrives, call the given - * callback. - * - * The callback may be NULL if you just want the side-effect of this - * operation, which is to update the cached alias. - */ -GabbleVCardManagerRequest * -gabble_vcard_manager_request (GabbleVCardManager *self, - TpHandle handle, - guint timeout, - GabbleVCardManagerCb callback, - gpointer user_data, - GObject *object, - GError **error) -{ - GabbleVCardManagerPrivate *priv = GABBLE_VCARD_MANAGER_GET_PRIVATE (self); - TpBaseConnection *connection = (TpBaseConnection *)priv->connection; - TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( - connection, TP_HANDLE_TYPE_CONTACT); - GabbleVCardManagerRequest *request; - GabbleVCardCacheEntry *entry; - const gchar *jid; - - g_return_val_if_fail (tp_handle_is_valid (contact_repo, handle, NULL), NULL); - - if (timeout == 0) - timeout = DEFAULT_REQUEST_TIMEOUT; - - request = g_slice_new0 (GabbleVCardManagerRequest); - DEBUG ("Created request %p to retrieve <%u>'s vCard", request, handle); - request->timeout = timeout; - request->manager = self; - tp_handle_ref (contact_repo, handle); - request->handle = handle; - request->callback = callback; - request->user_data = user_data; - request->bound_object = object; - - if (NULL != object) - g_object_weak_ref (object, notify_delete_request, request); - - priv->requests = g_slist_prepend (priv->requests, request); - if (handle == connection->self_handle) - { - jid = NULL; - } - else - { - jid = tp_handle_inspect (contact_repo, handle); - } - - entry = cache_entry_get (self, handle); - if (entry->request) - { - DEBUG ("adding pending request to existing cache entry %p", entry); - entry->pending_requests = g_slist_prepend (entry->pending_requests, - request); - } - else - { - entry->request = g_slice_new0 (GabbleVCardManagerRequest); - entry->request->manager = self; - entry->request->handle = handle; - tp_handle_ref (contact_repo, handle); - priv->requests = g_slist_prepend (priv->requests, entry->request); - - entry->pending_requests = g_slist_prepend (entry->pending_requests, - request); - DEBUG ("adding the request to new entry %p and sending the request", - entry); - - if (!request_send (entry->request, NULL, jid, error)) - { - DEBUG ("some kind of error happened"); - - delete_request (entry->request); - entry->request = NULL; - gabble_vcard_manager_invalidate_cache (self, entry->handle); - - delete_request (request); - return NULL; - } - } - - return request; -} - -GabbleVCardManagerRequest * -gabble_vcard_manager_replace (GabbleVCardManager *self, - LmMessageNode *replacement, - guint timeout, - GabbleVCardManagerCb callback, - gpointer user_data, - GObject *object, - GError **error) -{ - GabbleVCardManagerPrivate *priv = GABBLE_VCARD_MANAGER_GET_PRIVATE (self); - TpBaseConnection *connection = (TpBaseConnection *)priv->connection; - TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( - connection, TP_HANDLE_TYPE_CONTACT); - GabbleVCardManagerRequest *request; - - if (timeout == 0) - timeout = DEFAULT_REQUEST_TIMEOUT; - - request = g_slice_new0 (GabbleVCardManagerRequest); - DEBUG ("Created request %p to replace my vCard, callback = %p", - request, callback); - request->timeout = timeout; - request->manager = self; - tp_handle_ref (contact_repo, connection->self_handle); - request->handle = connection->self_handle; - request->callback = callback; - request->user_data = user_data; - request->bound_object = object; - - if (NULL != object) - g_object_weak_ref (object, notify_delete_request, request); - - priv->requests = g_slist_prepend (priv->requests, request); - - gabble_vcard_manager_invalidate_cache (self, request->handle); - - return request_send (request, replacement, NULL, error); -} - -GabbleVCardManagerRequest * -gabble_vcard_manager_edit (GabbleVCardManager *self, - guint timeout, - GabbleVCardManagerCb callback, - gpointer user_data, - GObject *object, - GError **error, - ...) -{ - va_list ap; - size_t i, argc; - GabbleVCardManagerPrivate *priv = GABBLE_VCARD_MANAGER_GET_PRIVATE (self); - TpBaseConnection *connection = (TpBaseConnection *)priv->connection; - TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( - connection, TP_HANDLE_TYPE_CONTACT); - GabbleVCardManagerRequest *request; - - if (timeout == 0) - timeout = DEFAULT_REQUEST_TIMEOUT; - - request = g_slice_new0 (GabbleVCardManagerRequest); - DEBUG ("Created request %p to edit my vCard", request); - request->timeout = timeout; - request->manager = self; - tp_handle_ref (contact_repo, connection->self_handle); - request->handle = connection->self_handle; - request->callback = callback; - request->user_data = user_data; - request->bound_object = object; - - if (NULL != object) - g_object_weak_ref (object, notify_delete_request, request); - - priv->requests = g_slist_prepend (priv->requests, request); - - argc = 0; - va_start (ap, error); - while (va_arg (ap, const gchar *) != NULL) - { - argc++; - } - va_end (ap); - g_return_val_if_fail (argc % 2 == 0, NULL); - - request->edit_args = g_new (gchar *, argc + 1); - - va_start (ap, error); - for (i = 0; i < argc; i++) - { - request->edit_args[i] = g_strdup (va_arg (ap, const gchar *)); - } - request->edit_args[argc] = NULL; - va_end (ap); - - return request_send (request, NULL, NULL, error); -} - -void -gabble_vcard_manager_cancel_request (GabbleVCardManager *manager, - GabbleVCardManagerRequest *request) -{ - GabbleVCardManagerPrivate *priv; - - g_return_if_fail (GABBLE_IS_VCARD_MANAGER (manager)); - g_return_if_fail (NULL != request); - - priv = GABBLE_VCARD_MANAGER_GET_PRIVATE (manager); - - g_return_if_fail (NULL != g_slist_find (priv->requests, request)); - - cancel_request (request); -} - -/** - * Return cached message for the handle's VCard if it's available. - */ -gboolean -gabble_vcard_manager_get_cached (GabbleVCardManager *self, TpHandle handle, - LmMessageNode **node) -{ - GabbleVCardManagerPrivate *priv = GABBLE_VCARD_MANAGER_GET_PRIVATE (self); - GabbleVCardCacheEntry *entry = g_hash_table_lookup (priv->cache, - GUINT_TO_POINTER (handle)); - TpHandleRepoIface *contact_repo = tp_base_connection_get_handles ( - (TpBaseConnection *)priv->connection, TP_HANDLE_TYPE_CONTACT); - - g_return_val_if_fail (tp_handle_is_valid (contact_repo, handle, NULL), - FALSE); - - if ((entry == NULL) || (entry->message == NULL)) - return FALSE; - - if (node != NULL) - *node = lm_message_node_get_child (entry->message->node, "vCard"); - - return TRUE; -} - -/** - * Return the cached alias derived from the vCard for the given handle, - * if any. If there is no cached alias, return NULL. - */ -const gchar * -gabble_vcard_manager_get_cached_alias (GabbleVCardManager *manager, - TpHandle handle) -{ - GabbleVCardManagerPrivate *priv; - TpHandleRepoIface *contact_repo; - const gchar *s; - - g_return_val_if_fail (GABBLE_IS_VCARD_MANAGER (manager), NULL); - - priv = GABBLE_VCARD_MANAGER_GET_PRIVATE (manager); - contact_repo = tp_base_connection_get_handles ( - (TpBaseConnection *)priv->connection, TP_HANDLE_TYPE_CONTACT); - - g_return_val_if_fail (tp_handle_is_valid (contact_repo, handle, NULL), NULL); - - s = tp_handle_get_qdata (contact_repo, handle, - gabble_vcard_manager_cache_quark ()); - - if (s == NO_ALIAS) - s = NULL; - - return s; -} - -/** - * Return TRUE if we've tried looking up an alias for this handle before. - */ -gboolean -gabble_vcard_manager_has_cached_alias (GabbleVCardManager *manager, - TpHandle handle) -{ - GabbleVCardManagerPrivate *priv; - TpHandleRepoIface *contact_repo; - gpointer p; - - g_return_val_if_fail (GABBLE_IS_VCARD_MANAGER (manager), FALSE); - - priv = GABBLE_VCARD_MANAGER_GET_PRIVATE (manager); - contact_repo = tp_base_connection_get_handles ( - (TpBaseConnection *)priv->connection, TP_HANDLE_TYPE_CONTACT); - - g_return_val_if_fail (tp_handle_is_valid (contact_repo, handle, NULL), - FALSE); - - p = tp_handle_get_qdata (contact_repo, handle, - gabble_vcard_manager_cache_quark ()); - - return p != NULL; -} - diff --git a/src/vcard-manager.h b/src/vcard-manager.h deleted file mode 100644 index 8d3acb74b..000000000 --- a/src/vcard-manager.h +++ /dev/null @@ -1,130 +0,0 @@ -/* - * vcard-manager.h - vCard lookup helper for Gabble connections - * - * Copyright (C) 2006 Collabora Ltd. - * Copyright (C) 2006 Nokia Corporation - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef __GABBLE_VCARD_MANAGER_H__ -#define __GABBLE_VCARD_MANAGER_H__ - -#include <glib-object.h> -#include <loudmouth/loudmouth.h> - -#include "gabble-types.h" - -G_BEGIN_DECLS - -typedef struct _GabbleVCardManagerClass GabbleVCardManagerClass; -typedef struct _GabbleVCardManagerRequest GabbleVCardManagerRequest; - -/** - * GabbleVCardManagerError: - * @GABBLE_VCARD_MANAGER_ERROR_CANCELLED: The vCard request was cancelled - * @GABBLE_VCARD_MANAGER_ERROR_TIMEOUT: The vCard request timed out - * @GABBLE_VCARD_MANAGER_ERROR_UNKNOWN: An unknown error occured - */ -typedef enum -{ - GABBLE_VCARD_MANAGER_ERROR_CANCELLED, - GABBLE_VCARD_MANAGER_ERROR_TIMEOUT, - GABBLE_VCARD_MANAGER_ERROR_UNKNOWN -} GabbleVCardManagerError; - -GQuark gabble_vcard_manager_error_quark (void); -#define GABBLE_VCARD_MANAGER_ERROR gabble_vcard_manager_error_quark () - -GType gabble_vcard_manager_get_type (void); - -/* TYPE MACROS */ -#define GABBLE_TYPE_VCARD_MANAGER \ - (gabble_vcard_manager_get_type ()) -#define GABBLE_VCARD_MANAGER(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), GABBLE_TYPE_VCARD_MANAGER, \ - GabbleVCardManager)) -#define GABBLE_VCARD_MANAGER_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass), GABBLE_TYPE_VCARD_MANAGER, \ - GabbleVCardManagerClass)) -#define GABBLE_IS_VCARD_MANAGER(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj), GABBLE_TYPE_VCARD_MANAGER)) -#define GABBLE_IS_VCARD_MANAGER_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass), GABBLE_TYPE_VCARD_MANAGER)) -#define GABBLE_VCARD_MANAGER_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS ((obj), GABBLE_TYPE_VCARD_MANAGER, \ - GabbleVCardManagerClass)) - -struct _GabbleVCardManagerClass { - GObjectClass parent_class; -}; - -struct _GabbleVCardManager { - GObject parent; - gpointer priv; -}; - -typedef void (*GabbleVCardManagerCb)(GabbleVCardManager *self, - GabbleVCardManagerRequest *request, - TpHandle handle, - LmMessageNode *vcard, - GError *error, - gpointer user_data); - -GabbleVCardManager *gabble_vcard_manager_new (GabbleConnection *); - -GQuark gabble_vcard_manager_cache_quark (void); - -GabbleVCardManagerRequest *gabble_vcard_manager_request (GabbleVCardManager *, - TpHandle, - guint timeout, - GabbleVCardManagerCb, - gpointer user_data, - GObject *object, - GError **error); - -GabbleVCardManagerRequest *gabble_vcard_manager_replace (GabbleVCardManager *, - LmMessageNode *, - guint timeout, - GabbleVCardManagerCb, - gpointer user_data, - GObject *object, - GError **error); - -GabbleVCardManagerRequest *gabble_vcard_manager_edit (GabbleVCardManager *, - guint timeout, - GabbleVCardManagerCb, - gpointer user_data, - GObject *object, - GError **error, - ...) - G_GNUC_NULL_TERMINATED; - -void gabble_vcard_manager_cancel_request (GabbleVCardManager *manager, - GabbleVCardManagerRequest *request); - -const gchar *gabble_vcard_manager_get_cached_alias (GabbleVCardManager *, - TpHandle); -gboolean gabble_vcard_manager_has_cached_alias (GabbleVCardManager *manager, - TpHandle handle); - -gboolean gabble_vcard_manager_get_cached (GabbleVCardManager *, - TpHandle, - LmMessageNode **); -void gabble_vcard_manager_invalidate_cache (GabbleVCardManager *, TpHandle); - -G_END_DECLS - -#endif diff --git a/src/write-mgr-file.c b/src/write-mgr-file.c deleted file mode 100644 index fd61ef6b0..000000000 --- a/src/write-mgr-file.c +++ /dev/null @@ -1,103 +0,0 @@ -/* - * write_mgr_file.c - utility to produce gabble.manager. Part of Gabble. - * Copyright (C) 2006 Collabora Ltd. - * Copyright (C) 2006 Nokia Corporation - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include <stdio.h> - -#include <dbus/dbus-glib.h> -#include <dbus/dbus-protocol.h> - -#include <telepathy-glib/enums.h> -#include "gabble-connection-manager.h" - -static gchar * -mgr_file_contents (const char *busname, - const char *objpath, - const TpCMProtocolSpec protocols[], - GError **error) -{ - GKeyFile *f = g_key_file_new (); - const TpCMProtocolSpec *protocol; - const TpCMParamSpec *row; - - g_key_file_set_string (f, "ConnectionManager", "BusName", busname); - g_key_file_set_string (f, "ConnectionManager", "ObjectPath", objpath); - - for (protocol = protocols; protocol->name; protocol++) - { - gchar *section_name = g_strdup_printf ("Protocol %s", protocol->name); - - for (row = protocol->parameters; row->name; row++) - { - gchar *param_name = g_strdup_printf ("param-%s", row->name); - gchar *param_value = g_strdup_printf ("%s%s%s", row->dtype, - (row->flags & TP_CONN_MGR_PARAM_FLAG_REQUIRED ? " required" : ""), - (row->flags & TP_CONN_MGR_PARAM_FLAG_REGISTER ? " register" : "")); - g_key_file_set_string (f, section_name, param_name, param_value); - g_free (param_value); - g_free (param_name); - } - - for (row = protocol->parameters; row->name; row++) - { - if (row->flags & TP_CONN_MGR_PARAM_FLAG_HAS_DEFAULT) - { - gchar *default_name = g_strdup_printf ("default-%s", row->name); - - switch (row->gtype) - { - case G_TYPE_STRING: - g_key_file_set_string (f, section_name, default_name, - row->def); - break; - case G_TYPE_INT: - case G_TYPE_UINT: - g_key_file_set_integer (f, section_name, default_name, - GPOINTER_TO_INT(row->def)); - break; - case G_TYPE_BOOLEAN: - g_key_file_set_boolean (f, section_name, default_name, - GPOINTER_TO_INT(row->def) ? 1 : 0); - } - g_free (default_name); - } - } - g_free (section_name); - } - return g_key_file_to_data (f, NULL, error); -} - -int -main (void) -{ - GError *error = NULL; - - gchar *s = mgr_file_contents (TP_CM_BUS_NAME_BASE "gabble", - TP_CM_OBJECT_PATH_BASE "gabble", - gabble_protocols, &error); - if (!s) - { - fprintf (stderr, error->message); - g_error_free (error); - return 1; - } - printf ("%s", s); - g_free (s); - return 0; -} diff --git a/tests/.git-darcs-dir b/tests/.git-darcs-dir deleted file mode 100644 index e69de29bb..000000000 --- a/tests/.git-darcs-dir +++ /dev/null diff --git a/tests/Makefile.am b/tests/Makefile.am deleted file mode 100644 index d5de5b88d..000000000 --- a/tests/Makefile.am +++ /dev/null @@ -1,65 +0,0 @@ -noinst_PROGRAMS = \ - test-gabble-presence \ - test-base64 \ - test-handles - -python_tests = \ - test-basic-connect - -python_tests_pythonfiles = $(python_tests:=.py) -python_tests_shfiles = $(python_tests:=.sh) - -test_gabble_presence_LDADD = \ - $(top_builddir)/src/libgabble-convenience.la \ - $(top_builddir)/lib/telepathy-glib/libtelepathy-glib.la - -test_base64_LDADD = \ - $(top_builddir)/src/libgabble-convenience.la \ - $(top_builddir)/lib/telepathy-glib/libtelepathy-glib.la - -test_handles_LDADD = \ - $(top_builddir)/src/libgabble-convenience.la \ - $(top_builddir)/lib/telepathy-glib/libtelepathy-glib.la - -AM_CFLAGS = $(ERROR_CFLAGS) @DBUS_CFLAGS@ @GLIB_CFLAGS@ @LOUDMOUTH_CFLAGS@ \ - @COVERAGE_CFLAGS@ \ - -I $(top_srcdir)/lib -I $(top_builddir)/lib \ - -I $(top_srcdir)/src -I $(top_builddir)/src -AM_LDFLAGS = @DBUS_LIBS@ @GLIB_LIBS@ @LOUDMOUTH_LIBS@ - -TESTS = $(noinst_PROGRAMS) -EXTRA_TESTS = $(python_tests_shfiles) - -check-extra: $(BUILT_SOURCES) - chmod +x run-with-tmp-session-bus.sh $(python_tests_shfiles) \ - $(python_tests_pythonfiles) exec-with-log.sh - $(MAKE) check TESTS="$(EXTRA_TESTS)" - -exec-with-log.sh: exec-with-log.sh.in - @sed -e "s|[@]abs_top_builddir[@]|@abs_top_builddir@|g" $< > $@ - -%.conf: %.conf.in - @sed -e "s|[@]abs_top_builddir[@]|@abs_top_builddir@|g" $< > $@ - -%.service: %.service.in - @sed -e "s|[@]abs_top_builddir[@]|@abs_top_builddir@|g" $< > $@ - -# D-Bus service file for testing -service_in_files = org.freedesktop.Telepathy.ConnectionManager.gabble.service.in -service_files = $(service_in_files:.service.in=.service) - -# D-Bus config file for testing -conf_in_files = run-with-tmp-session-bus.conf.in -conf_files = $(conf_in_files:.conf.in=.conf) - -BUILT_SOURCES = $(service_files) $(conf_files) exec-with-log.sh - -EXTRA_DIST = $(service_in_files) $(conf_in_files) exec-with-log.sh.in - -CLEANFILES = $(BUILT_SOURCES) gabble-[1-9]*.log - -check_c_sources = *.c -check_misc_sources = \ - $(python_tests_shfiles) \ - $(python_tests_pythonfiles) -include $(top_srcdir)/check-coding-style.mk diff --git a/tests/dbustesting.py b/tests/dbustesting.py deleted file mode 100644 index 3186510cd..000000000 --- a/tests/dbustesting.py +++ /dev/null @@ -1,609 +0,0 @@ -import sys -import traceback - -import dbus -import dbus.glib -import dbus.service -from dbus.proxies import ProxyObject -import gobject - -# The default timeout for all blocking calls -DEFAULT_TIMEOUT=10 - -# The session bus used for the tests -session_bus = dbus.SessionBus() - -# The loop used for the tests, if needed provide another one. -LOOP = gobject.MainLoop() - -class MainloopClock: - def __init__(self, callback=None): - self._ticks = None - self._callback = callback - - def _tick(self): - self._ticks[1] -= 1 - return True - - def _reset(self): - if self._ticks != None: - gobject.source_remove(self._ticks[0]) - self._ticks = None - - def sleep(self, timeout): - """ - Sleeps for 'timeout' seconds without blocking the Mainloop. Blocking call. - """ - if self._callback == None: - print 'Sleeping for %d seconds' % (timeout) - self._reset() - - self._ticks = [gobject.timeout_add(timeout*1000, self._tick), timeout] - while self._ticks[1] > 0 and (self._callback == None or self._callback()): - LOOP.get_context().iteration(True) - -class TestCase: - """ - Subclass this class to create your TestCase. then pass the class to the run() - function to execute the tests. - """ - def __init__(self): - pass - - def Sleep(self, timeout): - """ - Calling this method will block for 'timeout' seconds, without hanging the process. - """ - MainloopClock().sleep(timeout) - - def run(self): - """ - This method is called when the test must be run. Override it to implement your - test script. Any exception thrown in this method will be reported as a test failure. - """ - raise NotImplementedError() - - def __repr__(self): - return str(self.__class__) - -def run(cases): - """ - Call this method with a TestCase sub-class, or a list of TestCase sub-classes. - Warning ! - As the proxy objects and various signal connections aren't destroyed after a test, it is - not a good idea to run more than one test after the other, better launch one process per test. - - The test case to run will be given on the command line, if not, a help notice is printed - """ - if type(cases) != list: - cases = [cases] - - # Command line foo - def class_name(name): - i = name.rfind(".") - if i == -1: - return name - else: - return name[i+1:] - - print 'These are the available test cases:', [class_name(str(case)) for case in cases] - if len(sys.argv) != 2: - print 'You must run a single test case by appending its name on the command line' - sys.exit(0) - - cases = [case for case in cases if class_name(str(case)) in sys.argv] - print 'Running the following test case:', [class_name(str(case)) for case in cases] - - results = [] - def finish(): - LOOP.quit() - print 'Test Case ended.' - for result in results: - success, reason = result - print '-------\nTestCase:\n%s\n--------' % (reason) - if not success: - sys.exit(1) - - def run_case(case_class): - print '----\nTestCase: Creating from class %s' % (str(case_class)) - case = case_class() - print 'TestCase: Starting %s' % (case) - try: - case.run() - results.append((True, "Success")) - except Exception, msg: - print 'TestCase: Failing %s----' % (case) - print msg - results.append((False, traceback.format_exc())) - - print 'TestCase: Ending %s\n-----' % (case) - - def run_all_cases(): - for case in cases: - run_case(case) - finish() - - gobject.idle_add(run_all_cases) - LOOP.run() - -class TestingException(Exception): - pass - - -#-----------Start of Client Side Scripting ------------------------------------- - - -class ClientSignalProxy: - def __init__ (self, provider, proxy, name): - self.provider = provider - self.proxy = proxy - self.name = name - self.connected = False - - def _signal_emitted(self, *args): - print "%s: Signal '%s' emitted with args:%s" % (self.provider, self.name, args) - self.results.append(args) - - def listen(self): - """ - Starts listening for the requested signal. The effect is to reset all counters for this - signal. - """ - if not self.connected: - print '%s: Connecting to signal "%s"' % (self.provider, self.name) - self.proxy.connect_to_signal(self.name, self._signal_emitted) - self.connected = True - - print "%s: Listening to signal '%s'" % (self.provider, self.name) - self.results = [] - - def wait(self, n=1, timeout=DEFAULT_TIMEOUT): - """ - Wait for the signal to happen n times. This method is blocking. If after 'timeout' - seconds the signal hasn't happened 'n' times, an exception is thrown. - Returns the list (or the single if n = 1) of values returned by successive - signal emissions - """ - if not self.connected: - raise TestingException("Waiting for a signal before it's listening") - - print "%s: Waiting for signal '%s'" % (self.provider, self.name) - if len(self.results) != n: - MainloopClock(lambda: (len(self.results) != n)).sleep(timeout) - - results = self.results - if len(results) != n: - raise TestingException("%s: Signal '%s' failed to happen %d times: %s" % (self.provider, self.name, n, results)) - - if n == 1: - results = self.results[0] - - print "%s: Signal '%s' happened %d times: %s" % (self.provider, self.name, n, results) - return results - -class ClientProxy: - def __init__ (self, provider, proxy, name): - self.provider = provider - self.name = name - self.proxy = proxy - self.signal_proxy = ClientSignalProxy(provider, proxy, name) - self.async_reply = None - - def _reset(self): - ret = self.async_reply - self.async_reply = None - return ret - - def _handle_reply(self, *args): - if len(args) == 1: - args = args[0] - print '%s: Got method return value: %s' % (self.provider, args) - self.async_reply = args - - def _handle_error(self, e): - print e - - def _call_async(self, *args, **kwargs): - print "%s: Calling method %s(%s, %s)" % (self.provider, self.name, args, kwargs) - if "timeout" not in kwargs: - kwargs["timeout"] = DEFAULT_TIMEOUT*1000 - getattr(self.proxy, self.name)(reply_handler=self._handle_reply, error_handler=self._handle_error, *args, **kwargs) - - # Wait to receive the return value - while self.async_reply == None: - LOOP.get_context().iteration(True) - - ret = self._reset() - print "%s: Method %s returned: %s" % (self.provider, self.name, ret) - return ret - - def call(self, *args, **kwargs): - """ - Call this method with the given arguments. If timeout=xx is used, the method - will wait at most xx msecs before returning an error. If it's not used - the default timeout value is used. - """ - print "%s: Calling method %s(%s, %s)" % (self.provider, self.name, args, kwargs) - if "timeout" not in kwargs: - kwargs["timeout"] = DEFAULT_TIMEOUT*1000 - - ret = getattr(self.proxy, self.name)(*args, **kwargs) - print "%s: Method %s returned: %s" % (self.provider, self.name, ret) - return ret - - def listen(self): - """ - Start listening for this signal to happen (reset counters, etc). See ClientSignalProxy.listen - """ - # Lazy signal connection - self.signal_proxy.listen() - - def wait(self, n=1, timeout=DEFAULT_TIMEOUT): - """ - Wait and block until this signal happens. See ClientSignalProxy.wait - """ - # Lazy signal wait - return self.signal_proxy.wait(n, timeout) - -class ClientProxyProvider: - def __init__ (self, provider, interface): - self.provider = provider - self.interface = interface - self.cache = {} - - def __getitem__(self, name): - """ - Return the ClientProxy for the method or signal named 'name'. - """ - if name in self.cache: - return self.cache[name] - - proxy = ClientProxy(self.provider, self.interface, name) - - # Cache for future reuses - self.cache[name] = proxy - - return proxy - - def __str__(self): - return str(self.provider) - -class ClientInterfaceProvider: - def __init__ (self, provider, proxy, interfaces): - self.provider = provider - self.interfaces = {} - - for nick, iface in interfaces.items(): - self.interfaces[nick] = ClientProxyProvider(self, dbus.Interface(proxy, iface)) - - def __getitem__(self, name): - """ - Return the ClientProxyProvider for the interface nicknamed 'name'. - """ - return self.interfaces[name] - - def __str__(self): - return str(self.provider) - -class ClientProvider: - def __init__ (self, introspect=True): - self.proxies = {} - self.introspect = introspect - # FIXME: this should be solved, the deadlock/introspect thing - #self.introspect = True - - def __getitem__(self, name): - """ - Return the ClientInterfaceProvider or ClientProxy for the service nicknamed 'name' - """ - return self.proxies[name] - - def __setitem__(self, name, value): - """ - Create a new Client with the given value. value must be a tuple with three items: - service: text of the dbus service, - obj: path of the dbus remote object, - ifaces: either a string of the interface name implemented by the object OR - a dictionnary of name:iface pairs, name being a nickname for the interface, and - iface one of the interfaces implemented by the object. - """ - service, obj, ifaces = value - print "%s: Adding service '%s': %s,%s,%s" % (self, name, service, obj, ifaces) - proxy = ProxyObject(session_bus, service, obj, self.introspect) - if type(ifaces) == dict: - self.proxies[name] = ClientInterfaceProvider(self, proxy, ifaces) - else: - self.proxies[name] = ClientProxyProvider(self, dbus.Interface(proxy, ifaces)) - - def __str__(self): - return str(self.__class__) - -#-----------End of Client Side scripting --------------------------------------- - -#-----------Start of Service Side Scripting ------------------------------------ - -# This one intercepts calls to the remote dbus object and log the call, register the arguments -# and sometimes override return value -def intercept(obj): - """ - This decorator is to be used on exported MockService method and must be applied *after* the - @dbus.service.method decorator (that means be written *before* it in source code) for example: - - @intercept - @dbus.service.method("foo.bar.baz") - def Foobar(self, arg): - blah - - This is used so the testing framework can wrap the actual method call and do some voodoo around it. - """ - def _meth_wrapper(self, *args, **kwargs): - override = self._called(*args, **kwargs) - real = obj(self, *args, **kwargs) - if override != None: - return override - return real - - # FIXME: HACK: this is a hack to get the dbus method decorator working even if we wrap it - for i in dir(obj): - if i.startswith("_dbus"): - setattr(_meth_wrapper, i, getattr(obj, i)) - - return _meth_wrapper - -class MockService(dbus.service.Object): - """ - This class is the super class for services definition. Each service you want to use - must define the exported methods and signals by subclassing this class and using - the appropriate dbus decorators and @intercept when it's a method. - """ - def __init__(self, service, obj): - service = dbus.service.BusName(service, bus=session_bus) - dbus.service.Object.__init__(self, service, obj) - - def _called(self, *args, **kwargs): - # To be overridden by ServiceProxy implementation - # Called by the @intercept decorator on method call - pass - -class ServiceProxy: - def __init__ (self, provider, service, name): - self.provider = provider - self.name = name - self.service = service - service._called = self._called - self._reset() - - def _reset(self): - self.received_call = None - self.return_value = None - - def _called(self, *args, **kwargs): - # Dbus callback - print '%s: Got method call "%s" with args: %s %s' % (self.provider, self.name, args, kwargs) - self.received_call = (args, kwargs) - if self.return_value != None: - return self.return_value(*args, **kwargs) - - def _wait_listening_call(self, timeout=DEFAULT_TIMEOUT): - print '%s: Waiting for Method Call "%s"' % (self.provider, self.name) - - MainloopClock(lambda: self.received_call == None).sleep(timeout) - if self.received_call == None: - raise TestingException("%s: Method Call '%s' failed to happen" % (self.provider, self.name)) - - def listen_call(self, return_val=None): - """ - Starts listening for a method call on this service side. Resets counters and stuff. - If return_val is not None but is a callable, it will be called with the method - call arguments, and the return value will override the return value as defined in the MockService Subclass. - - use like: - xxx.listen_call(lambda *args, **kwargs: 3) - - will return 3 for this method whatever was defined in the MockService imoplementation - """ - self._reset() - if return_val != None: - print '%s: Listening for Method Call "%s" with overriden return val' % (self.provider, self.name) - self.return_value = return_val - else: - print '%s: Listening for Method Call "%s"' % (self.provider, self.name) - - def wait_call(self, listening=False, return_val=None, timeout=DEFAULT_TIMEOUT): - """ - This will block waiting for the method call to effectively happen on service side. - - If listening is False, it will automatically starts listening for the call and then block - while the method isn't called. - - If listening is True, it will only wait for a previously listened call to happen (after having called - manually listen_call()) - - See listen_call for return_val definition. - - timeout is the number of seconds to block at most before throwing an Exception - """ - if not listening: - self.listen_call(return_val) - - self._wait_listening_call(timeout) - - def emit(self, *args, **kwargs): - """ - Make the service emit this signal with the given arguments. - """ - print '%s: Emitting signal "%s" with args: %s %s' % (self.provider, self.name, args, kwargs) - getattr(self.service, self.name)(*args, **kwargs) - - -class ServiceProxyProvider: - def __init__ (self, provider, service): - self.provider = provider - self.service = service - self.cache = {} - - def __getitem__(self, name): - """ - Return the ServiceProxy for the method or signal named 'name' - """ - if name in self.cache: - return self.cache[name] - - proxy = ServiceProxy(self.provider, self.service, name) - - # Cache for future reuses - self.cache[name] = proxy - - return proxy - - def __str__(self): - return str(self.provider) - -class ServiceProvider: - def __init__(self): - self.services = {} - - def __setitem__(self, name, value): - """ - Creates a service implementation with the given value. - value must be a 3-tuple whith: - klass: the class of the MockService subclass defining the service - service: the name of the dbus service (eg. org.foo.bar) - obj: the path for this exported object on Dbus - """ - klass, service, obj = value - print "%s: Adding service '%s': %s,%s,%s" % (self, name, klass, service, obj) - self.services[name] = ServiceProxyProvider(self, klass(service, obj)) - - def __getitem__(self, name): - """ - Return the ServiceProxyProvider for service nicknamed 'name' - """ - return self.services[name] - - def __str__(self): - return str(self.__class__) - -#-----------End of Service Side scripting -------------------------------------- - -#-----------Start of Mixed Side Scripting ------------------------------------ - -# The classes defined below are used to adapt service/client interfaces to a mixed mode. -# That means you can use both API's depending on wether the nickname refers -# to a client or a service or a mixed object. -# Mixed object are both a service and a client and implement both capabilities. -# see MixedProvider.__setitem__ for how to construct such objects. -# If you want your test case to use both client and service objects, you should use the -# MixedTestCase superclass. -class MixedProxy: - def __init__(self, provider, client, service, name): - self.provider = provider - self.client = client - self.service = service - self.name = name - - # Mirror the client+service api's - def emit(self, *args, **kwargs): - self.service.emit(*args, **kwargs) - - def wait_call(self, listening=False, return_val=None, timeout=DEFAULT_TIMEOUT): - return self.service.wait_call(listening, return_val, timeout) - - def listen_call(self, return_val=None): - self.service.listen_call(return_val) - - def call(self, *args, **kwargs): - return self.client._call_async(*args, **kwargs) - - def listen(self): - self.client.listen() - - def wait(self, n=1, timeout=DEFAULT_TIMEOUT): - return self.client.wait(n, timeout) - -class MixedProxyProvider: - def __init__(self, provider, client, service): - self.provider = provider - self.client = client - self.service = service - self.cache = {} - - def __getitem__(self, name): - if name in self.cache: - return self.cache[name] - - proxy = MixedProxy(self.provider, self.client[name], self.service[name], name) - - # Cache for future reuses - self.cache[name] = proxy - - return proxy - - def __str__(self): - return str(self.provider) - -class MixedProvider: - def __init__(self): - # FIXME: the introspect=True causes a massive deadlock, introspect=False is likely to break things too :) - self.client = ClientProvider(introspect=False) - self.service = ServiceProvider() - self.mappings = {} - - def __setitem__(self, name, value): - """ - Construct a mixed Proxy, depending on the value a client, a service, or a mixed proxy will be created. - if value is a 3-uple with first elements being a MockService subclass, a service is created - (as defined in ServiceProvider.__setitem__) - if value is a 3-uple with first element being a string, a client is created - (as defined in ClientProvider.__setitem__) - if value is a 4-uple with: - klass: the MockProxy subclass of the service to implement - service: the name of dbus service to export - object: the name of dbus object to export - ifaces: either a dic or a string describing the interfaces implemented by this object - then a mixed client/service is created allowing to use both API's on it. - """ - if len(value) == 4: - self.service[name] = value[:3] - self.client[name] = value[1:] - self.mappings[name] = MixedProxyProvider(self, self.client[name], self.service[name]) - elif issubclass(value[0], MockService): - # New service - self.service[name] = value - self.mappings[name] = self.service - else: - self.client[name] = value - self.mappings[nickname] = self.client - - def __getitem__(self, name): - return self.mappings[name] - - def __str__(self): - return str(self.__class__) - -#-----------End of Mixed Side scripting -------------------------------------- - -class ClientTestCase(ClientProvider, TestCase): - """ - Subclass this to build a test case using only clients - """ - def __init__(self): - TestCase.__init__(self) - ClientProvider.__init__(self) - -class ServiceTestCase(ServiceProvider, TestCase): - """ - Subclass this to build a test case using only services - """ - def __init__(self): - TestCase.__init__(self) - ServiceProvider.__init__(self) - -class MixedTestCase(MixedProvider, TestCase): - """ - Subclass this to build a test case that uses both clients and services - """ - def __init__(self): - TestCase.__init__(self) - MixedProvider.__init__(self) - diff --git a/tests/exec-with-log.sh.in b/tests/exec-with-log.sh.in deleted file mode 100644 index bcbc61eeb..000000000 --- a/tests/exec-with-log.sh.in +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/sh - -cd "@abs_top_builddir@/tests" - -export GABBLE_DEBUG=all LM_DEBUG=net -ulimit -c unlimited -exec > gabble-$$.log 2>&1 - -exec ../src/telepathy-gabble diff --git a/tests/org.freedesktop.Telepathy.ConnectionManager.gabble.service.in b/tests/org.freedesktop.Telepathy.ConnectionManager.gabble.service.in deleted file mode 100644 index 1d1933c65..000000000 --- a/tests/org.freedesktop.Telepathy.ConnectionManager.gabble.service.in +++ /dev/null @@ -1,3 +0,0 @@ -[D-BUS Service] -Name=org.freedesktop.Telepathy.ConnectionManager.gabble -Exec=@abs_top_builddir@/tests/exec-with-log.sh diff --git a/tests/run-with-tmp-session-bus.conf.in b/tests/run-with-tmp-session-bus.conf.in deleted file mode 100644 index 1d3c399dc..000000000 --- a/tests/run-with-tmp-session-bus.conf.in +++ /dev/null @@ -1,30 +0,0 @@ -<!-- This configuration file controls the per-user-login-session message bus. - Add a session-local.conf and edit that rather than changing this - file directly. --> - -<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-Bus Bus Configuration 1.0//EN" - "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd"> -<busconfig> - <!-- Our well-known bus type, don't change this --> - <type>session</type> - - <listen>unix:tmpdir=/tmp</listen> - - <servicedir>@abs_top_builddir@/tests</servicedir> - - <policy context="default"> - <!-- Allow everything to be sent --> - <allow send_destination="*"/> - <!-- Allow everything to be received --> - <allow eavesdrop="true"/> - <!-- Allow anyone to own anything --> - <allow own="*"/> - </policy> - - <!-- This is included last so local configuration can override what's - in this standard file --> - - - - -</busconfig> diff --git a/tests/run-with-tmp-session-bus.sh b/tests/run-with-tmp-session-bus.sh deleted file mode 100644 index 1021723c4..000000000 --- a/tests/run-with-tmp-session-bus.sh +++ /dev/null @@ -1,49 +0,0 @@ -#! /bin/sh - -SCRIPTNAME=$0 -WRAPPED_SCRIPT=$1 -shift - -die() -{ - if ! test -z "$DBUS_SESSION_BUS_PID" ; then - echo "killing message bus "$DBUS_SESSION_BUS_PID >&2 - kill -9 $DBUS_SESSION_BUS_PID - fi - echo $SCRIPTNAME: $* >&2 - exit 1 -} - -## convenient to be able to ctrl+C without leaking the message bus process -trap 'die "Received SIGINT"' INT - -CONFIG_FILE=./run-with-tmp-session-bus.conf - -unset DBUS_SESSION_BUS_ADDRESS -unset DBUS_SESSION_BUS_PID - -echo "Running dbus-launch --sh-syntax --config-file=$CONFIG_FILE" >&2 - -eval `dbus-launch --sh-syntax --config-file=$CONFIG_FILE` - -if test -z "$DBUS_SESSION_BUS_PID" ; then - die "Failed to launch message bus for test script to run" -fi - -echo "Started bus pid $DBUS_SESSION_BUS_PID at $DBUS_SESSION_BUS_ADDRESS" >&2 - -#Uncomment the following for debugging -#GABBLE_DEBUG=all ../src/telepathy-gabble & - -# Execute wrapped script -echo "Running $WRAPPED_SCRIPT $@" >&2 -$WRAPPED_SCRIPT "$@" || die "script \"$WRAPPED_SCRIPT\" failed" - -kill -TERM $DBUS_SESSION_BUS_PID || die "Message bus vanished! should not have happened" && echo "Killed daemon $DBUS_SESSION_BUS_PID" >&2 - -sleep 0.1 - -## be sure it really died -kill -9 $DBUS_SESSION_BUS_PID > /dev/null 2>&1 || true - -exit 0 diff --git a/tests/test-base64.c b/tests/test-base64.c deleted file mode 100644 index 40d694847..000000000 --- a/tests/test-base64.c +++ /dev/null @@ -1,98 +0,0 @@ - -#include <base64.h> - -struct test { - gchar *str; - gchar *encoded; -}; - -struct test tests[] = { - { "c", "Yw==" }, - { "ca", "Y2E=" }, - { "car", "Y2Fy" }, - { "carnal pleasure.", "Y2FybmFsIHBsZWFzdXJlLg==" }, - { "carnal pleasure", "Y2FybmFsIHBsZWFzdXJl" }, - { "carnal pleasur", "Y2FybmFsIHBsZWFzdXI=" }, - { "carnal pleasu", "Y2FybmFsIHBsZWFzdQ==" }, - { NULL, NULL } -}; - -int -main (void) -{ - gchar *s; - GString *tmp1, *tmp2; - struct test *t; - - for (t = tests; t->str != NULL; t++) - { - tmp1 = g_string_new (t->str); - s = base64_encode (tmp1); - g_string_free (tmp1, TRUE); - tmp1 = g_string_new (s); - tmp2 = g_string_new (t->encoded); - g_assert (g_string_equal (tmp1, tmp2)); - g_free (s); - g_string_free (tmp1, TRUE); - g_string_free (tmp2, TRUE); - } - - /* test long (> 76 / 4 * 3) string */ - tmp1 = g_string_new ( - "1234567890" - "1234567890" - "1234567890" - "1234567890" - "1234567890" - "1234567890"); - s = base64_encode (tmp1); - g_assert (g_str_equal (s, - "MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM" - "0NTY3\nODkw")); - g_free (s); - g_string_free (tmp1, TRUE); - - /* test string with valid characters but invalid length */ - tmp1 = base64_decode ("AAA"); - g_assert (tmp1 == NULL); - - /* test string with valid length but invalid characters */ - tmp1 = base64_decode ("????"); - g_assert (tmp1 == NULL); - - /* test string with embedded newline */ - tmp1 = base64_decode ("bWF6\ndWxlbQ=="); - tmp2 = g_string_new ("mazulem"); - g_assert (tmp1); - g_assert (g_string_equal (tmp1, tmp2)); - g_string_free (tmp1, TRUE); - g_string_free (tmp2, TRUE); - - /* test string with misc whitespace */ - tmp1 = base64_decode ("bW F\r6\r\ndW\nxlbQ==\r\n"); - tmp2 = g_string_new ("mazulem"); - g_assert (tmp1); - g_assert (g_string_equal (tmp1, tmp2)); - g_string_free (tmp1, TRUE); - g_string_free (tmp2, TRUE); - - /* test string with embedded NULL */ - tmp1 = base64_decode ("Zm9vAGJhcg=="); - tmp2 = g_string_new_len ("foo\0bar", 7); - g_assert (g_string_equal (tmp1, tmp2)); - g_string_free (tmp1, TRUE); - g_string_free (tmp2, TRUE); - - for (t = tests; t->str != NULL; t++) - { - tmp1 = base64_decode (t->encoded); - g_assert (tmp1); - tmp2 = g_string_new (t->str); - g_assert (g_string_equal (tmp1, tmp2)); - g_string_free (tmp1, TRUE); - g_string_free (tmp2, TRUE); - } - - return 0; -} - diff --git a/tests/test-basic-connect.py b/tests/test-basic-connect.py deleted file mode 100644 index 2dee7b9f2..000000000 --- a/tests/test-basic-connect.py +++ /dev/null @@ -1,90 +0,0 @@ -#!/usr/bin/python -from dbustesting import * - -service = "org.freedesktop.Telepathy.ConnectionManager.gabble" -path = "/org/freedesktop/Telepathy/ConnectionManager/gabble" -iface_conn_mgr = "org.freedesktop.Telepathy.ConnectionManager" -iface_conn = "org.freedesktop.Telepathy.Connection" -iface_chan = "org.freedesktop.Telepathy.Channel" -chan_type_text = "org.freedesktop.Telepathy.Channel.Type.Text" - -class BasicConnectTest(ClientTestCase): - - def start_manager(self, nick): - self[nick] = (service, path, iface_conn_mgr) - - def connect(self, nick, proto, params): - # Connect to the manager - conn, obj = self[nick]["RequestConnection"].call(proto, params) - # register connection as self.nick_conn - self[nick+"_conn"] = (conn, obj, iface_conn) - self[nick+"_conn"]["Connect"].call(timeout=30000) - - # Retrieve connection status, and wait until connected - self[nick+"_conn"]["StatusChanged"].listen() - status = self[nick+"_conn"]["GetStatus"].call() - while status != 0: - if status == 2: - raise Exception("Could not connect") - status, reason = self[nick+"_conn"]["StatusChanged"].wait() - - return conn - - def run(self): - # Test Setup - self.start_manager("client1") - self.start_manager("client2") - - # Connect the two accounts - conn1 = self.connect("client1", "jabber", - {"account": "telepathytest1@jabber.org", "password": "telepathy"}) - conn2 = self.connect("client2", "jabber", - { "account": "telepathytest2@jabber.org","password": "telepathy"}) - - # Start listening to NewChannel signal emitted when client2 starts to talk to client1 - self["client1_conn"]["NewChannel"].listen() - - # Create a text channel - self["client2_chan"] = ( - # The connection service - conn2, - # A new text channel to the client 1 - self["client2_conn"]["RequestChannel"].call(chan_type_text, 1, - self["client2_conn"]["RequestHandles"].call (1, ["telepathytest1@jabber.org"])[0], False), - # The interfaces for this text channel - {"text": chan_type_text, "chan": iface_chan}) - - # Send a test message to client 1 from client 2 - self["client2_chan"]["text"]["Send"].call (0, "Test") - - # We got a newchannel from client 1 - obj, channel_type, handle_type, handle, suppress_handler = self["client1_conn"]["NewChannel"].wait() - - assert channel_type == chan_type_text - assert handle_type == 1 - assert suppress_handler == False - - # Create the channel structure for client 1 - self["client1_chan"] = ( - conn1, - obj, - {"text": chan_type_text, "chan": iface_chan}) - - # Retreive pending messages - messages = self["client1_chan"]["text"]["ListPendingMessages"].call(True) - - id, stamp, from_handle, msg_type, flags, string = messages[0] - assert len(messages) == 1 - assert from_handle == handle - assert msg_type == 0 - assert flags == 0 - assert string == "Test" - - # Close/Disconnect stuff - self["client2_chan"]["chan"]["Close"].call() - self["client1_chan"]["chan"]["Close"].call() - self["client2_conn"]["Disconnect"].call() - self["client1_conn"]["Disconnect"].call() - - -run(BasicConnectTest) diff --git a/tests/test-basic-connect.sh b/tests/test-basic-connect.sh deleted file mode 100644 index 93af74d54..000000000 --- a/tests/test-basic-connect.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/sh -./run-with-tmp-session-bus.sh `pwd`/test-basic-connect.py BasicConnectTest diff --git a/tests/test-gabble-presence.c b/tests/test-gabble-presence.c deleted file mode 100644 index b39d934b8..000000000 --- a/tests/test-gabble-presence.c +++ /dev/null @@ -1,137 +0,0 @@ - -#include <string.h> - -#include <glib.h> -#include <presence.h> - -int main (int argc, char **argv) -{ - const gchar *resource; - gchar *dump; - GabblePresence *presence; - - g_type_init (); - - presence = gabble_presence_new (); - g_assert (GABBLE_PRESENCE_OFFLINE == presence->status); - g_assert (NULL == presence->status_message); - - /* offline presence from unknown resource: no change */ - g_assert (FALSE == gabble_presence_update (presence, "foo", - GABBLE_PRESENCE_OFFLINE, NULL, 0)); - /* available presence from unknown resource: change */ - g_assert (TRUE == gabble_presence_update (presence, "foo", - GABBLE_PRESENCE_AVAILABLE, NULL, 0)); - - /* accumulated presence has changed; status message unchanged */ - g_assert (GABBLE_PRESENCE_AVAILABLE == presence->status); - g_assert (NULL == presence->status_message); - - /* available presence again; no change */ - g_assert (FALSE == gabble_presence_update (presence, "foo", - GABBLE_PRESENCE_AVAILABLE, NULL, 0)); - /* available presence again, but with status message: change */ - g_assert (TRUE == gabble_presence_update (presence, "foo", - GABBLE_PRESENCE_AVAILABLE, "status message", 0)); - - /* accumulated presence unchanged; status message changed */ - g_assert (GABBLE_PRESENCE_AVAILABLE == presence->status); - g_assert (0 == strcmp ("status message", presence->status_message)); - - /* same presence again; no change */ - g_assert (FALSE == gabble_presence_update (presence, "foo", - GABBLE_PRESENCE_AVAILABLE, "status message", 0)); - - /* presence from different resource, but equal present-ness; unchanged */ - g_assert (FALSE == gabble_presence_update (presence, "bar", - GABBLE_PRESENCE_AVAILABLE, "dingoes", 0)); - - g_assert (GABBLE_PRESENCE_AVAILABLE == presence->status); - g_assert (0 == strcmp ("status message", presence->status_message)); - - /* presence with higher priority; presence and message changed */ - g_assert (TRUE == gabble_presence_update (presence, "bar", - GABBLE_PRESENCE_AVAILABLE, "dingoes", 1)); - - g_assert (GABBLE_PRESENCE_AVAILABLE == presence->status); - g_assert (0 == strcmp ("dingoes", presence->status_message)); - - /* presence from first resource with greated present-ness: change */ - g_assert (TRUE == gabble_presence_update (presence, "foo", - GABBLE_PRESENCE_CHAT, "status message", 0)); - - g_assert (GABBLE_PRESENCE_CHAT == presence->status); - g_assert (0 == strcmp ("status message", presence->status_message)); - - /* no resource has the Google voice cap */ - resource = gabble_presence_pick_resource_by_caps (presence, - PRESENCE_CAP_GOOGLE_VOICE); - g_assert (NULL == resource); - - /* give voice cap to second resource, but make priority negative */ - g_assert (FALSE == gabble_presence_update (presence, "bar", - GABBLE_PRESENCE_AVAILABLE, "dingoes", -1)); - gabble_presence_set_capabilities (presence, "bar", - PRESENCE_CAP_GOOGLE_VOICE, 0); - - dump = gabble_presence_dump (presence); - g_assert (0 == strcmp (dump, - "nickname: (null)\n" - "accumulated status: 6\n" - "accumulated status msg: status message\n" - "accumulated capabilities: 2\n" - "kept while unavailable: 0\n" - "resources:\n" - " foo\n" - " capabilities: 0\n" - " status: 6\n" - " status msg: status message\n" - " priority: 0\n" - " bar\n" - " capabilities: 2\n" - " status: 5\n" - " status msg: dingoes\n" - " priority: -1\n")); - g_free (dump); - - /* no resource with non-negative priority has the Google voice cap */ - resource = gabble_presence_pick_resource_by_caps (presence, - PRESENCE_CAP_GOOGLE_VOICE); - g_assert (NULL == resource); - - /* give voice cap to first resource */ - gabble_presence_set_capabilities (presence, "foo", - PRESENCE_CAP_GOOGLE_VOICE, 0); - - /* resource has voice cap */ - resource = gabble_presence_pick_resource_by_caps (presence, - PRESENCE_CAP_GOOGLE_VOICE); - g_assert (0 == strcmp ("foo", resource)); - - /* presence turns up from null resource; it trumps other presence regardless - * of whether status is more present or not */ - g_assert (TRUE == gabble_presence_update (presence, NULL, - GABBLE_PRESENCE_OFFLINE, "gone", 0)); - g_assert (GABBLE_PRESENCE_OFFLINE == presence->status); - g_assert (0 == strcmp ("gone", presence->status_message)); - - /* caps are gone too */ - resource = gabble_presence_pick_resource_by_caps (presence, - PRESENCE_CAP_GOOGLE_VOICE); - g_assert (NULL == resource); - - dump = gabble_presence_dump (presence); - g_assert (0 == strcmp (dump, - "nickname: (null)\n" - "accumulated status: 0\n" - "accumulated status msg: gone\n" - "accumulated capabilities: 2\n" - "kept while unavailable: 0\n" - "resources:\n" - " (none)\n")); - g_free (dump); - - - return 0; -} - diff --git a/tests/test-handles.c b/tests/test-handles.c deleted file mode 100644 index c9bc51931..000000000 --- a/tests/test-handles.c +++ /dev/null @@ -1,109 +0,0 @@ -#include <string.h> -#include <glib.h> -#include <glib-object.h> -#include <gabble-connection.h> -#include <telepathy-glib/enums.h> -#include <telepathy-glib/interfaces.h> -#include <telepathy-glib/errors.h> - -void test_handles (guint handle_type) -{ - TpHandleRepoIface *repos[NUM_TP_HANDLE_TYPES]; - TpHandleRepoIface *tp_repo = NULL; - GError *error = NULL; - guint i; - - TpHandle handle = 0; - const gchar *jid = "handle.test@foobar"; - const gchar *return_jid; - - for (i = 0; i < NUM_TP_HANDLE_TYPES; i++) - { - repos[i] = NULL; - } - _gabble_connection_create_handle_repos (NULL, repos); - tp_repo = repos[handle_type]; - g_assert (tp_repo != NULL); - - /* Handle zero is never valid */ - g_assert (tp_handle_is_valid (tp_repo, 0, &error) == FALSE); - g_assert (error->code == TP_ERROR_INVALID_ARGUMENT); - - g_error_free (error); - error = NULL; - - /* Properly return error when handle isn't in the repo */ - g_assert (tp_handle_is_valid (tp_repo, 65536, &error) == FALSE); - g_assert (error->code == TP_ERROR_INVALID_ARGUMENT); - - g_error_free (error); - error = NULL; - - /* Properly return when error out argument isn't provided */ - g_assert (tp_handle_is_valid (tp_repo, 65536, NULL) == FALSE); - - if (handle_type == TP_HANDLE_TYPE_LIST) - { - /* for the static repo we need a name that's actually valid */ - jid = "deny"; - } - else - { - /* It's not there to start with, unless we're using the static repo */ - handle = tp_handle_lookup (tp_repo, jid, NULL, NULL); - g_assert (handle == 0); - } - /* ... but when we call tp_handle_ensure we get a new ref to it */ - handle = tp_handle_ensure (tp_repo, jid, NULL, NULL); - g_assert (handle != 0); - - /* Try to inspect it */ - return_jid = tp_handle_inspect (tp_repo, handle); - g_assert (!strcmp (return_jid, jid)); - - if (handle_type != TP_HANDLE_TYPE_LIST) - { - /* Hold the handle */ - g_assert (tp_handle_client_hold (tp_repo, "TestSuite", handle, NULL) == TRUE); - - /* Now unref it */ - tp_handle_unref (tp_repo, handle); - - /* Validate it, should be all healthy because client holds it still */ - g_assert (tp_handle_is_valid (tp_repo, handle, NULL) == TRUE); - - /* Ref it again */ - tp_handle_ref (tp_repo, handle); - - /* Client releases it */ - g_assert (tp_handle_client_release (tp_repo, "TestSuite", handle, NULL) == TRUE); - } - - /* Now unref it */ - tp_handle_unref (tp_repo, handle); - - if (handle_type != TP_HANDLE_TYPE_LIST) - { - /* Try to unref it again, should fail */ - g_assert (tp_handle_is_valid (tp_repo, handle, NULL) == FALSE); - } - - for (i = 0; i < NUM_TP_HANDLE_TYPES; i++) - { - if (repos[i]) - g_object_unref ((GObject *)repos[i]); - } -} - -int main (int argc, char **argv) -{ - g_type_init (); - - g_debug ("Testing contact handles"); - test_handles (TP_HANDLE_TYPE_CONTACT); - g_debug ("Testing room handles"); - test_handles (TP_HANDLE_TYPE_ROOM); - g_debug ("Testing list handles"); - test_handles (TP_HANDLE_TYPE_LIST); - return 0; -} |