summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMathias Hasselmann <hasselmm@gnome.org>2010-01-10 00:43:02 +0100
committerMathias Hasselmann <hasselmm@gnome.org>2010-01-10 00:43:03 +0100
commit9f8ee8d8bb0fde950650560d77f79aec1e81512a (patch)
treed1a7196906915b84255d4a037542355908f03b39
parentbcea784a38f6bb23b51c83066eff1c5e222279ee (diff)
Add browser extension for accessing FacebookHEADmaster
Add a browser extension which provides a D-Bus interface for accessing Faceboook without violating their terms of service.
-rw-r--r--.gitignore9
-rw-r--r--Makefile.am135
-rw-r--r--configure.ac24
-rw-r--r--data/gruschler-support.html44
-rw-r--r--m4/facebook.m458
-rw-r--r--m4/mozilla.m428
-rw-r--r--src/browser-extension/tgBrowserService.cpp176
-rw-r--r--src/browser-extension/tgBrowserService.h63
-rw-r--r--src/browser-extension/tgChannelProxy.cpp349
-rw-r--r--src/browser-extension/tgChannelProxy.h68
-rw-r--r--src/browser-extension/tgDBusService.cpp45
-rw-r--r--src/browser-extension/tgDBusService.h37
-rw-r--r--src/browser-extension/tgModule.cpp45
-rw-r--r--src/browser-service/browser-service.c85
-rw-r--r--src/browser-service/browser-service.h35
-rw-r--r--src/browser-service/channel-proxy.c88
-rw-r--r--src/browser-service/channel-proxy.h33
-rw-r--r--src/browser-service/marshallers.list2
-rw-r--r--src/connection-manager/connection-manager.c (renamed from src/connection-manager.c)0
-rw-r--r--src/connection-manager/connection-manager.h (renamed from src/connection-manager.h)0
-rw-r--r--src/connection-manager/facebook-connection.c (renamed from src/facebook-connection.c)0
-rw-r--r--src/connection-manager/facebook-connection.h (renamed from src/facebook-connection.h)0
-rw-r--r--src/connection-manager/facebook-contact-list.c (renamed from src/facebook-contact-list.c)0
-rw-r--r--src/connection-manager/facebook-contact-list.h (renamed from src/facebook-contact-list.h)0
-rw-r--r--src/connection-manager/main.c (renamed from src/main.c)0
-rw-r--r--tests/phonebook.js1
-rw-r--r--tests/test-browser-service.c98
-rw-r--r--tests/test-browser-service.py37
28 files changed, 1413 insertions, 47 deletions
diff --git a/.gitignore b/.gitignore
index f434f72..9bd9caa 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,10 @@
.*.sw?
.deps
.dirstamp
+.libs
*.o
+*.l[oa]
+*.l[oa]T
/Makefile
/Makefile.in
@@ -16,6 +19,10 @@
/libtool
/ltmain.sh
/missing
-/src/telepathy-gruschler
+/src/browser-service/marshallers.c
+/src/browser-service/marshallers.h
+/src/connection-manager/telepathy-gruschler
/stamp-h1
+/tests/test-browser-service
/tests/test-facebook
+
diff --git a/Makefile.am b/Makefile.am
index 3f94376..4690557 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,20 +1,123 @@
+AM_CPPFLAGS = -I$(top_srcdir)/src
AM_CFLAGS = -Wall -Werror -Wmissing-prototypes
+AM_CXXFLAGS = -Wall -Werror
ACLOCAL_AMFLAGS = -I m4
telepathy_libdir=$(libdir)/telepathy
-telepathy_lib_PROGRAMS = src/telepathy-gruschler
-noinst_PROGRAMS = tests/test-facebook
-
-src_telepathy_gruschler_CFLAGS = $(AM_CFLAGS) $(REST_CFLAGS) $(TELEPATHY_CFLAGS)
-src_telepathy_gruschler_LDADD = $(AM_LDFLAGS) $(REST_LIBS) $(TELEPATHY_LIBS)
-
-src_telepathy_gruschler_SOURCES = src/connection-manager.c \
- src/connection-manager.h \
- src/facebook-connection.c \
- src/facebook-connection.h \
- src/facebook-contact-list.c \
- src/facebook-contact-list.h \
- src/main.c
-
-tests_test_facebook_CFLAGS = $(AM_CFLAGS) $(REST_CFLAGS)
-tests_test_facebook_LDADD = $(AM_LDFLAGS) $(REST_LIBS)
+
+mozilla_com_LTLIBRARIES = \
+ src/browser-extension/libtelepathy-gruschler.la \
+ $(null)
+noinst_LTLIBRARIES = \
+ src/browser-service/libbrowser-service.la \
+ $(null)
+telepathy_lib_PROGRAMS = \
+ src/connection-manager/telepathy-gruschler \
+ $(null)
+noinst_PROGRAMS = \
+ tests/test-browser-service \
+ tests/test-facebook \
+ $(null)
+
+#############################################################################
+##### BROWSER EXTENSION
+#############################################################################
+
+src_browser_extension_libtelepathy_gruschler_la_CXXFLAGS = \
+ $(AM_CXXFLAGS) $(DBUS_CFLAGS) $(MOZILLA_CFLAGS)
+src_browser_extension_libtelepathy_gruschler_la_LIBADD = \
+ $(AM_LDLIBS) $(DBUS_LIBS) $(MOZILLA_LIBS)
+src_browser_extension_libtelepathy_gruschler_la_LDFLAGS = \
+ $(AM_LDFLAGS) -avoid-version -Wl,--no-undefined
+src_browser_extension_libtelepathy_gruschler_la_SOURCES = \
+ src/browser-extension/tgBrowserService.cpp \
+ src/browser-extension/tgBrowserService.h \
+ src/browser-extension/tgChannelProxy.cpp \
+ src/browser-extension/tgChannelProxy.h \
+ src/browser-extension/tgDBusService.cpp \
+ src/browser-extension/tgDBusService.h \
+ src/browser-extension/tgModule.cpp \
+ $(null)
+
+#############################################################################
+##### BROWSER SERVICE DBUS INTERFACE
+#############################################################################
+
+src_browser_service_libbrowser_service_la_CFLAGS = \
+ $(AM_CFLAGS) $(DBUS_GLIB_CFLAGS)
+src_browser_service_libbrowser_service_la_LIBADD = \
+ $(AM_LDFLAGS) $(DBUS_GLIB_LIBS)
+src_browser_service_libbrowser_service_la_SOURCES = \
+ src/browser-service/browser-service.c \
+ src/browser-service/browser-service.h \
+ src/browser-service/channel-proxy.c \
+ src/browser-service/channel-proxy.h \
+ src/browser-service/marshallers.c \
+ src/browser-service/marshallers.h \
+ $(null)
+
+#############################################################################
+##### CONNECTION MANAGER
+#############################################################################
+
+src_connection_manager_telepathy_gruschler_CFLAGS = \
+ $(AM_CFLAGS) $(REST_CFLAGS) $(TELEPATHY_CFLAGS)
+src_connection_manager_telepathy_gruschler_LDADD = \
+ $(AM_LDFLAGS) $(REST_LIBS) $(TELEPATHY_LIBS) \
+ src/browser-service/libbrowser-service.la
+src_connection_manager_telepathy_gruschler_SOURCES = \
+ src/connection-manager/connection-manager.c \
+ src/connection-manager/connection-manager.h \
+ src/connection-manager/facebook-connection.c \
+ src/connection-manager/facebook-connection.h \
+ src/connection-manager/facebook-contact-list.c \
+ src/connection-manager/facebook-contact-list.h \
+ src/connection-manager/main.c \
+ $(null)
+
+#############################################################################
+##### TEST APPLICATIONS
+#############################################################################
+
+tests_test_browser_service_CFLAGS = \
+ $(AM_CFLAGS) $(DBUS_GLIB_CFLAGS)
+tests_test_browser_service_LDADD = \
+ $(AM_LDFLAGS) $(DBUS_GLIB_LIBS) \
+ src/browser-service/libbrowser-service.la
+
+tests_test_facebook_CFLAGS = \
+ $(AM_CFLAGS) $(REST_CFLAGS)
+tests_test_facebook_LDADD = \
+ $(AM_LDFLAGS) $(REST_LIBS)
+
+#############################################################################
+##### IMPLICIT RULES
+#############################################################################
+
+.idl.h:
+ $(MOZILLA_XPIDL) -I "$(mozilla_idldir)" -m header -e $@ $^
+.idl.xpt:
+ $(MOZILLA_XPIDL) -I "$(mozilla_idldir)" -m typelib -e $@ $^
+
+.list.h:
+ $(GLIB_GENMARSHAL) --header --prefix=gruschler_cclosure_marshal $^ > $@.tmp
+ cmp -s $@ $@.tmp || mv $@.tmp $@; rm -f $@.tmp
+.list.c:
+ echo '#include "$*.h"' > $@.tmp
+ $(GLIB_GENMARSHAL) --body --prefix=gruschler_cclosure_marshal $^ >> $@.tmp
+ cmp -s $@ $@.tmp || mv $@.tmp $@; rm -f $@.tmp
+
+#############################################################################
+##### INFORMATION ABOUT BUILT SOURCES
+#############################################################################
+
+EXTRA_DIST_FILES = \
+ src/browser-service/marshallers.list \
+ tests/test-browser-service.py \
+ $(null)
+
+BUILT_SOURCES = \
+ src/browser-service/marshallers.c \
+ src/browser-service/marshallers.h \
+ $(null)
+
diff --git a/configure.ac b/configure.ac
index 4f00c57..f25f212 100644
--- a/configure.ac
+++ b/configure.ac
@@ -7,14 +7,30 @@ AC_CONFIG_MACRO_DIR([m4])
AC_PROG_CC
AM_PROG_CC_C_O
+AC_PROG_CXX
+
+AC_DISABLE_STATIC
AC_PROG_LIBTOOL
+
AM_PATH_GLIB_2_0([2.20.3])
-PKG_CHECK_MODULES([TELEPATHY], [rtcom-telepathy-glib >= 0.1.38
- telepathy-glib >= 0.7.35])
-PKG_CHECK_MODULES([REST], [rest-extras-0.6 >= 0.6.1])
+PKG_CHECK_MODULES([DBUS],
+ [dbus-1 >= 1.2.14])
+PKG_CHECK_MODULES([DBUS_GLIB],
+ [dbus-glib-1 >= 0.78])
+PKG_CHECK_MODULES([TELEPATHY],
+ [rtcom-telepathy-glib >= 0.1.38
+ telepathy-glib >= 0.7.35])
+PKG_CHECK_MODULES([REST],
+ [rest-extras-0.6 >= 0.6.1])
+
+AC_ARG_VAR([DBUS_BINDING_TOOL])
+AC_PATH_PROG([DBUS_BINDING_TOOL],
+ [dbus-binding-tool],
+ [/usr/bin/dbus-binding-tool])
-FACEBOOK_APPKEY([GRUSCHLER])
+GRUSCHLER_CHECK_FACEBOOK
+GRUSCHLER_CHECK_MOZILLA
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_FILES([Makefile])
diff --git a/data/gruschler-support.html b/data/gruschler-support.html
new file mode 100644
index 0000000..2603e3b
--- /dev/null
+++ b/data/gruschler-support.html
@@ -0,0 +1,44 @@
+<html>
+ <head>
+ <script type="text/javascript">
+ function gruschlerInit() {
+ var errorPane = document.getElementById('errorPane');
+ var resultPane = document.getElementById('resultPane');
+
+ try {
+ netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+ const cid = "@freedesktop.org/telepathy-gruschler/GruschlerSupport;1";
+ resultPane.innerHTML += "<br>1:" + cid;
+
+ var iface = Components.interfaces.IGruschlerSupport;
+ resultPane.innerHTML += "<br>2:" + Components.interfaces.nsISupports + "/" + iface;
+
+ var obj = Components.classes[cid].createInstance();
+ resultPane.innerHTML += "<br>3:" + obj + "/" + obj.QueryInterface + "/" + Components.interfaces;
+
+ obj = obj.QueryInterface(iface);
+ resultPane.innerHTML += "<br>4:" + obj;
+ } catch (err) {
+ errorPane.innerHTML = 'Error: ' + err;
+ }
+ }
+
+ /*
+ /*
+
+
+ //var c = obj.Add(a, b);
+
+ //alert('Performing ' + a + ' + ' + b + '. Returned ' + c + '.');
+ }*/
+
+ window.addEventListener('load', gruschlerInit, false);
+ </script>
+ </head>
+
+ <body>
+ <div id="errorPane" style="color:red">Error Console</div>
+ <div id="resultPane" style="color:green">Result Console</div>
+ </body>
+</html>
+
diff --git a/m4/facebook.m4 b/m4/facebook.m4
index 4a2fca1..150793b 100644
--- a/m4/facebook.m4
+++ b/m4/facebook.m4
@@ -1,32 +1,38 @@
-AC_DEFUN([FACEBOOK_APPKEY],[
+AC_DEFUN([GRUSCHLER_CHECK_FACEBOOK],
+[
+ AC_REQUIRE([AC_PROG_SED])
-AC_MSG_CHECKING([Facebook keys])
-
-if ! test -r "facebook.conf"; then
- AC_MSG_ERROR([facebook.conf not found])
-fi
-
-facebook_apikey=`sed -ne 's/^apikey=//p' facebook.conf`
-facebook_secret=`sed -ne 's/^secret=//p' facebook.conf`
-facebook_email=`sed -ne 's/^email=//p' facebook.conf`
-facebook_password=`sed -ne 's/^password=//p' facebook.conf`
+ AC_MSG_CHECKING([for Facebook settings])
-if ! test -n "$facebook_apikey"; then
- AC_MSG_ERROR([API key not found])
-fi
+ if ! test -f "facebook.conf"; then
+ AC_MSG_ERROR([facebook.conf not found])
+ fi
-if ! test -n "$facebook_secret"; then
- AC_MSG_ERROR([secret key not found])
-fi
+ gruschler_facebook_apikey=`$SED -ne s/^apikey=//p facebook.conf`
+ gruschler_facebook_secret=`$SED -ne "s/^secret=//p" facebook.conf`
+ gruschler_facebook_email=`$SED -ne "s/^email=//p" facebook.conf`
+ gruschler_facebook_password=`$SED -ne "s/^password=//p" facebook.conf`
-AC_DEFINE_UNQUOTED([$1_FACEBOOK_APIKEY], ["$facebook_apikey"],
- [The API key for accessing Facebook])
-AC_DEFINE_UNQUOTED([$1_FACEBOOK_SECRET], ["$facebook_secret"],
- [The secret key for accessing Facebook])
-AC_DEFINE_UNQUOTED([$1_FACEBOOK_DEFAULT_EMAIL], ["$facebook_email"],
- [Default email address for accessing Facebook])
-AC_DEFINE_UNQUOTED([$1_FACEBOOK_DEFAULT_PASSWORD], ["$facebook_password"],
- [Default password for accessing Facebook])
+ if ! test -n "$gruschler_facebook_apikey"; then
+ AC_MSG_ERROR(['apikey' setting not found])
+ fi
-AC_MSG_RESULT([$facebook_apikey])
+ if ! test -n "$gruschler_facebook_secret"; then
+ AC_MSG_ERROR(['secret' setting not found])
+ fi
+
+ AC_MSG_RESULT([yes])
+
+ AC_DEFINE_UNQUOTED([GRUSCHLER_FACEBOOK_APIKEY],
+ ["$gruschler_facebook_apikey"],
+ [The API key for accessing Facebook])
+ AC_DEFINE_UNQUOTED([GRUSCHLER_FACEBOOK_SECRET],
+ ["$gruschler_facebook_secret"],
+ [The secret key for accessing Facebook])
+ AC_DEFINE_UNQUOTED([GRUSCHLER_FACEBOOK_DEFAULT_EMAIL],
+ ["$gruschler_facebook_email"],
+ [Default email address for accessing Facebook])
+ AC_DEFINE_UNQUOTED([GRUSCHLER_FACEBOOK_DEFAULT_PASSWORD],
+ ["$gruschler_facebook_password"],
+ [Default password for accessing Facebook])
])
diff --git a/m4/mozilla.m4 b/m4/mozilla.m4
new file mode 100644
index 0000000..7b1f4ff
--- /dev/null
+++ b/m4/mozilla.m4
@@ -0,0 +1,28 @@
+AC_DEFUN([GRUSCHLER_CHECK_MOZILLA],
+[
+ PKG_CHECK_MODULES([MOZILLA],[libxul >= 3.6])
+
+ mozilla_idldir=$($PKG_CONFIG --variable=idldir libxul)
+ mozilla_libdir=$($PKG_CONFIG --variable=libdir libxul)
+ mozilla_prefix=$($PKG_CONFIG --variable=prefix libxul)
+
+ if test -z "$mozilla_libdir"
+ then
+ mozilla_libdir="$mozilla_prefix/lib/$(basename $mozilla_idldir)"
+ fi
+
+ mozilla_comdir="$mozilla_libdir/components"
+ MOZILLA_XPIDL="$mozilla_libdir/xpidl"
+
+ AC_CHECK_FILE([$mozilla_idldir/nsISupports.idl])
+ AC_CHECK_FILE([$mozilla_libdir/libxpcom.so])
+ AC_CHECK_FILE([$mozilla_comdir])
+ AC_CHECK_FILE([$MOZILLA_XPIDL])
+
+ AC_SUBST([mozilla_idldir])
+ AC_SUBST([mozilla_libdir])
+ AC_SUBST([mozilla_comdir])
+ AC_SUBST([MOZILLA_XPIDL])
+
+ AC_DEFINE([MOZILLA_STRICT_API],[1],[Only use strict Mozilla API])
+])
diff --git a/src/browser-extension/tgBrowserService.cpp b/src/browser-extension/tgBrowserService.cpp
new file mode 100644
index 0000000..7180059
--- /dev/null
+++ b/src/browser-extension/tgBrowserService.cpp
@@ -0,0 +1,176 @@
+#include "config.h"
+#include "tgBrowserService.h"
+
+#include "nsAppShellCID.h"
+#include "nsIAppStartup.h"
+#include "nsXPFEComponentsCID.h"
+#include "tgChannelProxy.h"
+
+NS_IMPL_ISUPPORTS1(tgBrowserService, nsISupports);
+
+tgBrowserService::tgBrowserService()
+{
+fprintf(stderr, "%s:%d\n", __func__, __LINE__);
+ NS_INIT_ISUPPORTS();
+}
+
+tgBrowserService::~tgBrowserService()
+{
+fprintf(stderr, "%s:%d\n", __func__, __LINE__);
+}
+
+NS_IMETHODIMP
+tgBrowserService::Init()
+{
+ nsresult rv;
+
+ shell = do_GetService (NS_APPSHELLSERVICE_CONTRACTID, &rv);
+fprintf(stderr, "%s:%d: shell=%p, rv=%x\n", __func__, __LINE__, shell.get(), rv);
+ NS_ENSURE_SUCCESS (rv, rv);
+
+ nsCOMPtr<nsIAppStartup> startup;
+ startup = do_GetService (NS_APPSTARTUP_CONTRACTID, &rv);
+fprintf(stderr, "%s:%d: startup=%p, rv=%x\n", __func__, __LINE__, startup.get(), rv);
+ NS_ENSURE_SUCCESS (rv, rv);
+
+ rv = startup->CreateHiddenWindow();
+fprintf(stderr, "%s:%d: rv=%x\n", __func__, __LINE__, rv);
+ NS_ENSURE_SUCCESS (rv, rv);
+
+ io = do_GetIOService (&rv);
+fprintf(stderr, "%s:%d: io=%p, rv=%x\n", __func__, __LINE__, io.get(), rv);
+ NS_ENSURE_SUCCESS (rv, rv);
+
+ DBusConnection *session = dbus_bus_get(DBUS_BUS_SESSION, NULL);
+fprintf(stderr, "%s:%d: session=%p\n", __func__, __LINE__, session);
+
+ if (NULL == session)
+ return NS_ERROR_FAILURE;
+
+ if (-1 == dbus_bus_request_name(session,
+ TG_BROWSER_SERVICE_DBUS_NAME,
+ DBUS_NAME_FLAG_ALLOW_REPLACEMENT |
+ DBUS_NAME_FLAG_REPLACE_EXISTING,
+ NULL))
+ return NS_ERROR_FAILURE;
+fprintf(stderr, "%s:%d\n", __func__, __LINE__);
+
+ rv = Register(session);
+ NS_ENSURE_SUCCESS (rv, rv);
+fprintf(stderr, "%s:%d\n", __func__, __LINE__);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+tgBrowserService::GetObjectPath(nsCString & aObjectPath)
+{
+ aObjectPath.AssignLiteral(TG_BROWSER_SERVICE_DBUS_PATH);
+ return NS_OK;
+}
+
+DBusMessage *
+tgBrowserService::OnMethodCall(DBusConnection * aConnection,
+ DBusMessage * aMessage)
+{
+ const nsCString interface(dbus_message_get_interface(aMessage));
+ const nsCString member(dbus_message_get_member(aMessage));
+fprintf(stderr, "%s:%d: %s::%s\n", __func__, __LINE__, interface.BeginReading(), member.BeginReading());
+
+ if (interface == DBUS_INTERFACE_INTROSPECTABLE)
+ {
+ if (member == "Introspect")
+ return OnIntrospect(aConnection, aMessage);
+ }
+
+ if (interface == TG_BROWSER_SERVICE_DBUS_INTERFACE)
+ {
+ if (member == "NewChannel")
+ return OnNewChannel(aConnection, aMessage);
+ if (member == "NewWindow")
+ return OnNewWindow(aConnection, aMessage);
+ }
+
+ return NULL;
+}
+
+DBusMessage *
+tgBrowserService::OnIntrospect(DBusConnection * aConnection,
+ DBusMessage * aMessage)
+{
+ static const char interfaceDescription[] =
+ DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
+ "<node name=\"" TG_BROWSER_SERVICE_DBUS_PATH "\">\n"
+ " <interface name=\"" TG_BROWSER_SERVICE_DBUS_INTERFACE "\">\n"
+ " <method name=\"NewChannel\">\n"
+ " <arg name=\"uri\" type=\"s\" direction=\"in\"/>\n"
+ " <arg name=\"proxy_path\" type=\"o\" direction=\"out\"/>\n"
+ " </method>\n"
+ " </interface>\n"
+ " <interface name=\"" DBUS_INTERFACE_INTROSPECTABLE "\">\n"
+ " <method name=\"Introspect\">\n"
+ " <arg name=\"xml_data\" type=\"s\" direction=\"out\"/>\n"
+ " </method>\n"
+ " </interface>\n"
+ "</node>\n";
+
+ DBusMessage * reply = dbus_message_new_method_return(aMessage);
+ const char *vInterfaceDescription = interfaceDescription;
+
+ dbus_message_append_args(reply,
+ DBUS_TYPE_STRING, &vInterfaceDescription,
+ DBUS_TYPE_INVALID);
+
+ return reply;
+}
+
+DBusMessage *
+tgBrowserService::OnNewChannel(DBusConnection * aConnection,
+ DBusMessage * aMessage)
+{
+fprintf(stderr, "%s:%d\n", __func__, __LINE__);
+ nsCOMPtr<nsIChannel> channel;
+ const char *uri;
+ nsresult rv;
+
+ if (!dbus_message_get_args(aMessage, NULL, DBUS_TYPE_STRING, &uri, DBUS_TYPE_INVALID))
+ return NULL;
+
+fprintf(stderr, "%s:%d: uri=%s\n", __func__, __LINE__, uri);
+ rv = io->NewChannel(nsCAutoString(uri), nsnull, nsnull, getter_AddRefs(channel));
+
+ if (NS_FAILED(rv))
+ return dbus_message_new_error_printf(aMessage, DBUS_ERROR_FAILED,
+ "nsIIOService::NewChannel() failed (rv=%x)", rv);
+
+ nsCOMPtr<tgChannelProxy> proxy(new tgChannelProxy);
+ rv = proxy->Init(channel, aConnection);
+
+ if (NS_FAILED(rv))
+ return dbus_message_new_error_printf(aMessage, DBUS_ERROR_FAILED,
+ "tgChannelProxy::Init() failed (rv=%x)", rv);
+
+ nsCString proxyPath;
+ rv = proxy->GetObjectPath(proxyPath);
+
+ if (NS_FAILED(rv))
+ return dbus_message_new_error_printf(aMessage, DBUS_ERROR_FAILED,
+ "tgChannelProxy::GetObjectPath() failed (rv=%x)", rv);
+
+ DBusMessage * reply = dbus_message_new_method_return(aMessage);
+ const char *vProxyPath = proxyPath.BeginReading();
+
+ dbus_message_append_args(reply,
+ DBUS_TYPE_OBJECT_PATH, &vProxyPath,
+ DBUS_TYPE_INVALID);
+
+ return reply;
+}
+
+DBusMessage*
+tgBrowserService::OnNewWindow(DBusConnection * aConnection,
+ DBusMessage * aMessage)
+{
+ return NULL;
+}
+
diff --git a/src/browser-extension/tgBrowserService.h b/src/browser-extension/tgBrowserService.h
new file mode 100644
index 0000000..f753806
--- /dev/null
+++ b/src/browser-extension/tgBrowserService.h
@@ -0,0 +1,63 @@
+/* telepathy-gruschler - A Telepathy connection manager for social networks.
+ * Copyright (C) 2009 Mathias Hasselmann
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __TG_BROWSER_SERVICE_H__
+#define __TG_BROWSER_SERVICE_H__
+
+#include "nsIAppShellService.h"
+#include "nsNetUtil.h"
+#include "tgDBusService.h"
+
+#define TG_BROWSER_SERVICE_CONTRACTID "@freedesktop.org/telepathy-gruschler/BrowserService;1"
+#define TG_BROWSER_SERVICE_CLASSNAME "Telepathy Gruschler Browser Service"
+#define TG_BROWSER_SERVICE_CID { 0x76a9731d, 0xf28e, 0x4998, { 0x8b, 0x2f, 0x3f, 0x69, 0x74, 0xe7, 0x61, 0x89 } }
+
+#define TG_BROWSER_SERVICE_DBUS_NAME "org.freedesktop.Telepathy.Gruschler.BrowserService"
+#define TG_BROWSER_SERVICE_DBUS_PATH "/org/freedesktop/Telepathy/Gruschler/BrowserService"
+#define TG_BROWSER_SERVICE_DBUS_INTERFACE "org.freedesktop.Telepathy.Gruschler.BrowserService"
+
+class tgBrowserService:
+ public nsISupports,
+ public tgDBusService
+{
+ NS_DECL_ISUPPORTS;
+
+public:
+ tgBrowserService();
+
+private:
+ ~tgBrowserService();
+
+public:
+ NS_METHOD Init();
+ NS_IMETHOD GetObjectPath(nsCString & aObjectPath);
+
+protected:
+ virtual DBusMessage * OnMethodCall(DBusConnection * aConnection, DBusMessage * aMessage);
+
+private:
+ DBusMessage * OnIntrospect(DBusConnection * aConnection, DBusMessage * aMessage);
+ DBusMessage * OnNewChannel(DBusConnection * aConnection, DBusMessage * aMessage);
+ DBusMessage * OnNewWindow(DBusConnection * aConnection, DBusMessage * aMessage);
+
+private:
+ nsCOMPtr<nsIIOService> io;
+ nsCOMPtr<nsIAppShellService> shell;
+};
+
+#endif /* __TG_BROWSER_SERVICE_H__ */
+
diff --git a/src/browser-extension/tgChannelProxy.cpp b/src/browser-extension/tgChannelProxy.cpp
new file mode 100644
index 0000000..5b5381f
--- /dev/null
+++ b/src/browser-extension/tgChannelProxy.cpp
@@ -0,0 +1,349 @@
+#include "config.h"
+#include "tgChannelProxy.h"
+
+#include "nsIInputStream.h"
+#include "nsServiceManagerUtils.h"
+
+#include <plstr.h>
+#include <stdio.h>
+
+NS_IMPL_ISUPPORTS1(tgChannelProxy, nsIStreamListener);
+
+int tgChannelProxy::lastId = 0;
+
+class DBusMessagePtr
+{
+public:
+ DBusMessagePtr(DBusMessage * aMessage = NULL): message(aMessage) {}
+ virtual ~DBusMessagePtr() { assign(NULL); }
+
+ void operator=(DBusMessage * rhs) { assign(rhs); }
+ operator DBusMessage *() const { return get(); }
+
+ void
+ assign(DBusMessage * other)
+ {
+ if (other)
+ dbus_message_ref (other);
+ if (message)
+ dbus_message_unref (message);
+
+ message = other;
+ }
+
+ DBusMessage *
+ forget()
+ {
+ DBusMessage * tmp = message;
+ message = NULL;
+ return tmp;
+ }
+
+ DBusMessage *
+ get() const
+ {
+ return message;
+ }
+
+private:
+ DBusMessage * message;
+};
+
+tgChannelProxy::tgChannelProxy():
+ connection(NULL)
+{
+ NS_INIT_ISUPPORTS();
+}
+
+NS_METHOD
+tgChannelProxy::Init(nsIChannel * aChannel,
+ DBusConnection * aConnection)
+{
+ if (NULL == aChannel)
+ return NS_ERROR_INVALID_ARG;
+ if (NULL == aConnection)
+ return NS_ERROR_INVALID_ARG;
+
+ nsresult rv;
+
+ rv = Register(aConnection);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ timer = do_GetService(NS_TIMER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // FIXME: figure out why the callback is not called
+ rv = timer->InitWithFuncCallback(OnProxyTimeout, this, 15000, nsITimer::TYPE_ONE_SHOT);
+
+ AddRef(); // borrow a reference until the channel was used
+ channel = aChannel; // remember the channel we use
+ connection = dbus_connection_ref(aConnection);
+
+ return NS_OK;
+}
+
+tgChannelProxy::~tgChannelProxy()
+{
+ if (connection)
+ {
+ if (objectPath.Length() > 0)
+ dbus_connection_unregister_object_path(connection, objectPath.BeginReading());
+
+ dbus_connection_unref(connection);
+ }
+}
+
+NS_IMETHODIMP
+tgChannelProxy::OnStartRequest(nsIRequest * aRequest,
+ nsISupports * aContext)
+{
+ nsCString requestName;
+ nsresult rv;
+
+ channel = NULL;
+
+ rv = aRequest->GetName(requestName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ const char *vRequestName = requestName.BeginReading();
+ DBusMessage *message;
+
+ message = dbus_message_new_signal(objectPath.BeginReading(),
+ TG_CHANNEL_PROXY_DBUS_INTERFACE,
+ "RequestStarted");
+
+ dbus_message_append_args(message,
+ DBUS_TYPE_STRING, &vRequestName,
+ DBUS_TYPE_INVALID);
+
+ if (!dbus_connection_send(connection, message, NULL))
+ rv = NS_ERROR_FAILURE;
+ else
+ rv = NS_OK;
+
+ dbus_message_unref(message);
+
+ return rv;
+}
+
+NS_IMETHODIMP
+tgChannelProxy::OnStopRequest(nsIRequest * aRequest,
+ nsISupports * aContext,
+ nsresult aStatusCode)
+{
+ nsCString requestName;
+ nsresult rv;
+
+ rv = aRequest->GetName(requestName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ const char *vRequestName = requestName.BeginReading();
+ dbus_uint32_t vStatusCode = aStatusCode;
+
+ DBusMessage *message;
+
+ message = dbus_message_new_signal(objectPath.BeginReading(),
+ TG_CHANNEL_PROXY_DBUS_INTERFACE,
+ "RequestStopped");
+
+ dbus_message_append_args(message,
+ DBUS_TYPE_STRING, &vRequestName,
+ DBUS_TYPE_UINT32, &vStatusCode,
+ DBUS_TYPE_INVALID);
+
+ if (!dbus_connection_send(connection, message, NULL))
+ rv = NS_ERROR_FAILURE;
+ else
+ rv = NS_OK;
+
+ dbus_message_unref(message);
+ channel = NULL; // FIXME: check error handling
+
+ return rv;
+}
+
+NS_IMETHODIMP
+tgChannelProxy::OnDataAvailable(nsIRequest * aRequest,
+ nsISupports * aContext,
+ nsIInputStream * aInputStream,
+ PRUint32 aOffset,
+ PRUint32 aCount)
+{
+ nsCString requestName;
+ nsresult rv;
+
+ rv = aRequest->GetName(requestName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ char buffer[aCount];
+ const char *vRequestName = requestName.BeginReading();
+ const char *vBuffer = buffer;
+ rv = NS_OK;
+
+ while (aCount > 0)
+ {
+ PRUint32 bytesRead;
+
+ rv = aInputStream->Read(buffer, aCount, &bytesRead);
+ NS_ENSURE_SUCCESS(rv, rv);
+ aCount -= bytesRead;
+
+ DBusMessage *message;
+
+ message = dbus_message_new_signal(objectPath.BeginReading(),
+ TG_CHANNEL_PROXY_DBUS_INTERFACE,
+ "DataAvailable");
+
+ dbus_message_append_args(message,
+ DBUS_TYPE_STRING, &vRequestName,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &vBuffer, bytesRead,
+ DBUS_TYPE_INVALID);
+
+ if (!dbus_connection_send(connection, message, NULL))
+ {
+ dbus_message_unref(message);
+ rv = NS_ERROR_FAILURE;
+ break;
+ }
+
+ dbus_message_unref(message);
+ rv = NS_OK;
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+tgChannelProxy::GetObjectPath(nsCString & aObjectPath)
+{
+ if (0 == objectPath.Length())
+ {
+ objectPath.Assign(TG_CHANNEL_PROXY_DBUS_PATH);
+ objectPath.AppendInt(++lastId);
+ }
+
+ aObjectPath.Assign(objectPath);
+ return NS_OK;
+}
+
+DBusMessage *
+tgChannelProxy::OnMethodCall(DBusConnection * aConnection,
+ DBusMessage * aMessage)
+{
+ const nsCString interface(dbus_message_get_interface(aMessage));
+ const nsCString member(dbus_message_get_member(aMessage));
+
+ if (interface == DBUS_INTERFACE_INTROSPECTABLE)
+ {
+ if (member == "Introspect")
+ return OnIntrospect(aConnection, aMessage);
+ }
+
+ if (interface == TG_CHANNEL_PROXY_DBUS_INTERFACE)
+ {
+ if (member == "Open")
+ return OnOpen(aConnection, aMessage);
+ }
+
+ return NULL;
+}
+
+DBusMessage *
+tgChannelProxy::OnIntrospect(DBusConnection * aConnection,
+ DBusMessage * aMessage)
+{
+ nsCString interfaceDescription;
+
+ interfaceDescription.AppendLiteral(
+ DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
+ "<node name=\"");
+ interfaceDescription.Append(objectPath);
+
+ interfaceDescription.AppendLiteral(
+ "\">\n"
+ " <interface name=\"" TG_CHANNEL_PROXY_DBUS_INTERFACE "\">\n"
+ " <method name=\"Open\"/>\n"
+ " <signal name=\"RequestStarted\">\n"
+ " <arg name=\"request_name\" type=\"s\"/>\n"
+ " </signal>\n"
+ " <signal name=\"RequestStopped\">\n"
+ " <arg name=\"request_name\" type=\"s\"/>\n"
+ " <arg name=\"status_code\" type=\"i\"/>\n"
+ " </signal>\n"
+ " <signal name=\"DataAvailable\">\n"
+ " <arg name=\"request_name\" type=\"s\"/>\n"
+ " <arg name=\"data\" type=\"ay\"/>\n"
+ " </signal>\n"
+ " </interface>\n"
+ " <interface name=\"" DBUS_INTERFACE_INTROSPECTABLE "\">\n"
+ " <method name=\"Introspect\">\n"
+ " <arg name=\"xml_data\" type=\"s\" direction=\"out\"/>\n"
+ " </method>\n"
+ " </interface>\n"
+ "</node>\n");
+
+ DBusMessage * reply = dbus_message_new_method_return(aMessage);
+ const char *vInterfaceDescription = interfaceDescription.BeginReading();
+
+ dbus_message_append_args(reply,
+ DBUS_TYPE_STRING, &vInterfaceDescription,
+ DBUS_TYPE_INVALID);
+
+ return reply;
+}
+
+DBusMessage *
+tgChannelProxy::OnOpen(DBusConnection * aConnection,
+ DBusMessage * aMessage)
+{
+ if (!timer)
+ return dbus_message_new_error_printf(aMessage, DBUS_ERROR_FAILED,
+ "This channel proxy is expired");
+
+ nsresult rv = channel->AsyncOpen(this, nsnull);
+
+ if (NS_FAILED (rv))
+ return dbus_message_new_error_printf(aMessage, DBUS_ERROR_FAILED,
+ "nsIChannel::AsyncOpen() failed (rv=%x)", rv);
+
+ timer = NULL; // discard the timer
+ //Release(); // give back borrowed reference
+ //TODO: call channel->removeObserver() at some point?
+
+ return dbus_message_new_method_return (aMessage);
+}
+
+void
+tgChannelProxy::OnProxyTimeout(nsITimer * aTimer,
+ void * aClosure)
+{
+ tgChannelProxy *proxy = static_cast<tgChannelProxy*>(aClosure);
+
+ if (proxy->timer)
+ {
+ proxy->timer = NULL; // discard the timer
+ proxy->Release(); // give back borrowed reference
+ }
+}
+
+
+/*
+To create an HTTP POST, a few additional steps are required after the nsIChannel is created.
+
+First, a nsIInputStream instance is created, after which the setData method is called. The first argument is the POST data as a string, while the second argument is the length of that data. In this case, the data is URL encoded, meaning that the string should look like this: foo=bar&baz=eek.
+
+var inputStream = Components.classes["@mozilla.org/io/string-input-stream;1"]
+ .createInstance(Components.interfaces.nsIStringInputStream);
+inputStream.setData(postData, postData.length);
+
+Next, the nsIChannel is QIed to an nsIUploadChannel. Its setUploadStream method is called, passing in the nsIInputStream and the type (in this case, "application/x-www-form-urlencoded"):
+
+var uploadChannel = gChannel.QueryInterface(Components.interfaces.nsIUploadChannel);
+uploadChannel.setUploadStream(inputStream, "application/x-www-form-urlencoded", -1);
+
+Due to a bug, calling setUploadStream will reset the nsIHttpChannel to be a PUT request, so now the request type is set to POST:
+
+// order important - setUploadStream resets to PUT
+httpChannel.requestMethod = "POST";
+
+*/
diff --git a/src/browser-extension/tgChannelProxy.h b/src/browser-extension/tgChannelProxy.h
new file mode 100644
index 0000000..98a7bec
--- /dev/null
+++ b/src/browser-extension/tgChannelProxy.h
@@ -0,0 +1,68 @@
+/* telepathy-gruschler - A Telepathy connection manager for social networks.
+ * Copyright (C) 2009 Mathias Hasselmann
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __TG_CHANNEL_PROXY_H__
+#define __TG_CHANNEL_PROXY_H__
+
+#include "nsCOMPtr.h"
+#include "nsIChannel.h"
+#include "nsIStreamListener.h"
+#include "nsITimer.h"
+#include "tgDBusService.h"
+
+#define TG_CHANNEL_PROXY_DBUS_PATH "/org/freedesktop/Telepathy/Gruschler/ChannelProxy/"
+#define TG_CHANNEL_PROXY_DBUS_INTERFACE "org.freedesktop.Telepathy.Gruschler.ChannelProxy"
+
+class tgChannelProxy:
+ public nsIStreamListener,
+ public tgDBusService
+{
+ NS_DECL_ISUPPORTS;
+ NS_DECL_NSIREQUESTOBSERVER;
+ NS_DECL_NSISTREAMLISTENER;
+
+public:
+ tgChannelProxy();
+
+private:
+ ~tgChannelProxy();
+
+public:
+ NS_METHOD Init(nsIChannel * aChannel, DBusConnection * aConnection);
+ NS_IMETHOD GetObjectPath(nsCString & aObjectPath);
+
+protected:
+ virtual DBusMessage * OnMethodCall(DBusConnection * aConnection, DBusMessage * aMessage);
+
+private:
+ static void OnProxyTimeout(nsITimer *aTimer, void * aClosure);
+
+ DBusMessage * OnIntrospect(DBusConnection * aConnection, DBusMessage * aMessage);
+ DBusMessage * OnOpen(DBusConnection * aConnection, DBusMessage * aMessage);
+
+private:
+ nsCOMPtr<nsIChannel> channel;
+ nsCOMPtr<nsITimer> timer;
+
+ DBusConnection * connection;
+ nsCString objectPath;
+ static int lastId;
+
+};
+
+#endif /* __CHANNEL_PROXY_H__ */
+
diff --git a/src/browser-extension/tgDBusService.cpp b/src/browser-extension/tgDBusService.cpp
new file mode 100644
index 0000000..d0ba80a
--- /dev/null
+++ b/src/browser-extension/tgDBusService.cpp
@@ -0,0 +1,45 @@
+#include "config.h"
+#include "tgDBusService.h"
+
+#include <stdio.h>
+
+NS_IMETHODIMP
+tgDBusService::Register(DBusConnection * aConnection)
+{
+ const DBusObjectPathVTable vtable = { NULL, OnDBusMessage, NULL, };
+ nsCString objectPath;
+ nsresult rv;
+
+ rv = GetObjectPath(objectPath);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!dbus_connection_register_object_path(aConnection,
+ objectPath.BeginReading(),
+ &vtable, this))
+ return NS_ERROR_FAILURE;
+
+ return NS_OK;
+}
+
+
+DBusHandlerResult
+tgDBusService::OnDBusMessage(DBusConnection * aConnection,
+ DBusMessage * aMessage,
+ void * aData)
+{
+fprintf(stderr, "%s:%d\n", __func__, __LINE__);
+ tgDBusService * service = static_cast<tgDBusService*>(aData);
+ DBusMessage *reply = NULL;
+
+ if (DBUS_MESSAGE_TYPE_METHOD_CALL == dbus_message_get_type(aMessage))
+ reply = service->OnMethodCall(aConnection, aMessage);
+
+ if (NULL == reply)
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ dbus_connection_send(aConnection, reply, NULL);
+ dbus_message_unref(reply);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
diff --git a/src/browser-extension/tgDBusService.h b/src/browser-extension/tgDBusService.h
new file mode 100644
index 0000000..65a43c8
--- /dev/null
+++ b/src/browser-extension/tgDBusService.h
@@ -0,0 +1,37 @@
+/* telepathy-gruschler - A Telepathy connection manager for social networks.
+ * Copyright (C) 2010 Mathias Hasselmann
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __TG_DBUS_SERVICE_H__
+#define __TG_DBUS_SERVICE_H__
+
+#include <dbus/dbus.h>
+#include "nsStringAPI.h"
+
+class tgDBusService
+{
+public:
+ NS_IMETHOD GetObjectPath(nsCString & aObjectPath) = 0;
+
+protected:
+ NS_METHOD Register(DBusConnection * aConnection);
+ virtual DBusMessage * OnMethodCall(DBusConnection * aConnection, DBusMessage * aMessage) = 0;
+
+private:
+ static DBusHandlerResult OnDBusMessage(DBusConnection * aConnection, DBusMessage * aMessage, void * aData);
+};
+
+#endif /* __TG_DBUS_SERVICE_H__ */
diff --git a/src/browser-extension/tgModule.cpp b/src/browser-extension/tgModule.cpp
new file mode 100644
index 0000000..29f2da6
--- /dev/null
+++ b/src/browser-extension/tgModule.cpp
@@ -0,0 +1,45 @@
+#include "config.h"
+
+#include "nsIGenericFactory.h"
+#include "nsICategoryManager.h"
+#include "nsServiceManagerUtils.h"
+
+#include "tgBrowserService.h"
+
+NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(tgBrowserService, Init);
+
+static NS_METHOD
+tgBrowserServiceRegistration(nsIComponentManager *aCompMgr,
+ nsIFile *aPath,
+ const char *registryLocation,
+ const char *componentType,
+ const nsModuleComponentInfo *info)
+{
+fprintf(stderr, "%s:%d\n", __func__, __LINE__);
+ nsCOMPtr<nsICategoryManager> catman;
+ nsresult rv;
+
+ catman = do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv);
+
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = catman->AddCategoryEntry(NS_XPCOM_STARTUP_CATEGORY,
+ TG_BROWSER_SERVICE_CLASSNAME,
+ TG_BROWSER_SERVICE_CONTRACTID,
+ PR_TRUE, PR_TRUE, nsnull);
+
+ return rv;
+}
+
+static nsModuleComponentInfo components[] = {
+ { TG_BROWSER_SERVICE_CLASSNAME,
+ TG_BROWSER_SERVICE_CID,
+ TG_BROWSER_SERVICE_CONTRACTID,
+ tgBrowserServiceConstructor,
+ tgBrowserServiceRegistration,
+ },
+};
+
+NS_IMPL_NSGETMODULE("tgModule", components);
+
diff --git a/src/browser-service/browser-service.c b/src/browser-service/browser-service.c
new file mode 100644
index 0000000..a1eda45
--- /dev/null
+++ b/src/browser-service/browser-service.c
@@ -0,0 +1,85 @@
+#include "config.h"
+#include "browser-service.h"
+
+#include <dbus/dbus-glib-bindings.h>
+
+struct _GruschlerBrowserService
+{
+ DBusGProxy parent;
+};
+
+struct _GruschlerBrowserServiceClass
+{
+ DBusGProxyClass parent_class;
+};
+
+G_DEFINE_TYPE (GruschlerBrowserService,
+ gruschler_browser_service,
+ DBUS_TYPE_G_PROXY);
+
+static void
+gruschler_browser_service_init (GruschlerBrowserService *proxy)
+{
+}
+
+static void
+gruschler_browser_service_class_init (GruschlerBrowserServiceClass *class)
+{
+}
+
+DBusGProxy *
+gruschler_browser_service_new (DBusGConnection *connection,
+ GError **error)
+{
+ char *unique_name = NULL;
+ DBusGProxy *proxy;
+ gboolean success;
+
+ g_return_val_if_fail (NULL != connection, NULL);
+
+ proxy = dbus_g_proxy_new_for_name (connection, DBUS_SERVICE_DBUS,
+ DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS);
+
+ success = org_freedesktop_DBus_get_name_owner (proxy,
+ GRUSCHLER_BROWSER_SERVICE_DBUS_NAME,
+ &unique_name, error);
+
+ g_object_unref (proxy);
+ proxy = NULL;
+
+ if (success)
+ {
+ proxy = g_object_new (GRUSCHLER_TYPE_BROWSER_SERVICE,
+ "connection", connection, "name", unique_name,
+ "interface", GRUSCHLER_BROWSER_SERVICE_DBUS_INTERFACE,
+ "path", GRUSCHLER_BROWSER_SERVICE_DBUS_PATH, NULL);
+ }
+
+ g_free (unique_name);
+
+ return proxy;
+}
+
+DBusGProxy *
+gruschler_browser_service_new_channel (DBusGProxy *service,
+ const char *uri,
+ GError **error)
+{
+ DBusGProxy *channel;
+ char *path;
+
+ g_return_val_if_fail (GRUSCHLER_IS_BROWSER_SERVICE (service), NULL);
+ g_return_val_if_fail (NULL != uri, NULL);
+
+ if (!dbus_g_proxy_call (service, "NewChannel", error,
+ G_TYPE_STRING, uri, G_TYPE_INVALID,
+ DBUS_TYPE_G_OBJECT_PATH, &path,
+ G_TYPE_INVALID))
+ return NULL;
+
+ channel = gruschler_channel_proxy_new (service, path);
+
+ g_free (path);
+
+ return channel;
+}
diff --git a/src/browser-service/browser-service.h b/src/browser-service/browser-service.h
new file mode 100644
index 0000000..98938fa
--- /dev/null
+++ b/src/browser-service/browser-service.h
@@ -0,0 +1,35 @@
+#ifndef __GRUSCHLER_BROWSER_SERVICE_H__
+#define __GRUSCHLER_BROWSER_SERVICE_H__
+
+#include "channel-proxy.h"
+
+G_BEGIN_DECLS
+
+#define GRUSCHLER_TYPE_BROWSER_SERVICE (gruschler_browser_service_get_type ())
+#define GRUSCHLER_BROWSER_SERVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GRUSCHLER_TYPE_BROWSER_SERVICE, GruschlerBrowserService))
+#define GRUSCHLER_BROWSER_SERVICE_CLASS(cls) (G_TYPE_CHECK_CLASS_CAST ((cls), GRUSCHLER_TYPE_BROWSER_SERVICE, GruschlerBrowserServiceClass))
+#define GRUSCHLER_IS_BROWSER_SERVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GRUSCHLER_TYPE_BROWSER_SERVICE))
+#define GRUSCHLER_IS_BROWSER_SERVICE_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((obj), GRUSCHLER_TYPE_BROWSER_SERVICE))
+#define GRUSCHLER_BROWSER_SERVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GRUSCHLER_TYPE_BROWSER_SERVICE, GruschlerBrowserServiceClass))
+#define GRUSCHLER_BROWSER_SERVICE_DBUS_NAME ("org.freedesktop.Telepathy.Gruschler.BrowserService")
+#define GRUSCHLER_BROWSER_SERVICE_DBUS_PATH ("/org/freedesktop/Telepathy/Gruschler/BrowserService")
+#define GRUSCHLER_BROWSER_SERVICE_DBUS_INTERFACE ("org.freedesktop.Telepathy.Gruschler.BrowserService")
+
+typedef struct _GruschlerBrowserService GruschlerBrowserService;
+typedef struct _GruschlerBrowserServiceClass GruschlerBrowserServiceClass;
+
+GType
+gruschler_browser_service_get_type (void) G_GNUC_CONST;
+
+DBusGProxy *
+gruschler_browser_service_new (DBusGConnection *connection,
+ GError **error);
+
+DBusGProxy *
+gruschler_browser_service_new_channel (DBusGProxy *service,
+ const char *uri,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* __GRUSCHLER_BROWSER_SERVICE_H__ */
diff --git a/src/browser-service/channel-proxy.c b/src/browser-service/channel-proxy.c
new file mode 100644
index 0000000..3ff9fac
--- /dev/null
+++ b/src/browser-service/channel-proxy.c
@@ -0,0 +1,88 @@
+#include "config.h"
+#include "channel-proxy.h"
+#include "marshallers.h"
+
+struct _GruschlerChannelProxy
+{
+ DBusGProxy parent;
+};
+
+struct _GruschlerChannelProxyClass
+{
+ DBusGProxyClass parent_class;
+};
+
+G_DEFINE_TYPE (GruschlerChannelProxy,
+ gruschler_channel_proxy,
+ DBUS_TYPE_G_PROXY);
+
+static void
+gruschler_channel_proxy_init (GruschlerChannelProxy *proxy)
+{
+}
+
+static void
+gruschler_channel_proxy_constructed (GObject *object)
+{
+ GObjectClass *parent_class;
+ DBusGProxy *proxy;
+
+ parent_class = G_OBJECT_CLASS (gruschler_channel_proxy_parent_class);
+
+ if (parent_class->constructed)
+ parent_class->constructed (object);
+
+ proxy = DBUS_G_PROXY (object);
+
+ dbus_g_proxy_add_signal (proxy, "RequestStarted",
+ G_TYPE_STRING, G_TYPE_INVALID);
+ dbus_g_proxy_add_signal (proxy, "RequestStopped",
+ G_TYPE_STRING, G_TYPE_UINT, G_TYPE_INVALID);
+ dbus_g_proxy_add_signal (proxy, "DataAvailable",
+ G_TYPE_STRING, DBUS_TYPE_G_UCHAR_ARRAY,
+ G_TYPE_INVALID);
+}
+
+static void
+gruschler_channel_proxy_class_init (GruschlerChannelProxyClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+ object_class->constructed = gruschler_channel_proxy_constructed;
+
+ dbus_g_object_register_marshaller (gruschler_cclosure_marshal_VOID__STRING_UINT,
+ G_TYPE_NONE, G_TYPE_STRING, G_TYPE_UINT,
+ G_TYPE_INVALID);
+ dbus_g_object_register_marshaller (gruschler_cclosure_marshal_VOID__STRING_BOXED,
+ G_TYPE_NONE, G_TYPE_STRING, DBUS_TYPE_G_UCHAR_ARRAY,
+ G_TYPE_INVALID);
+}
+
+DBusGProxy *
+gruschler_channel_proxy_new (DBusGProxy *browser_service,
+ const char *object_path)
+{
+ DBusGConnection *connection;
+
+ g_return_val_if_fail (DBUS_IS_G_PROXY (browser_service), NULL);
+ g_return_val_if_fail (NULL != object_path, NULL);
+
+ g_object_get (browser_service, "connection", &connection, NULL);
+
+ return g_object_new (GRUSCHLER_TYPE_CHANNEL_PROXY,
+ "connection", connection, "path", object_path,
+ "name", dbus_g_proxy_get_bus_name (browser_service),
+ "interface", GRUSCHLER_CHANNEL_PROXY_DBUS_INTERFACE,
+ NULL);
+}
+
+gboolean
+gruschler_channel_proxy_open (DBusGProxy *channel,
+ GError **error)
+{
+ g_return_val_if_fail (GRUSCHLER_IS_CHANNEL_PROXY (channel), FALSE);
+
+ return dbus_g_proxy_call (channel, "Open", error,
+ G_TYPE_INVALID, G_TYPE_INVALID);
+}
+
diff --git a/src/browser-service/channel-proxy.h b/src/browser-service/channel-proxy.h
new file mode 100644
index 0000000..2c9a2fc
--- /dev/null
+++ b/src/browser-service/channel-proxy.h
@@ -0,0 +1,33 @@
+#ifndef __GRUSCHLER_CHANNEL_PROXY_H__
+#define __GRUSCHLER_CHANNEL_PROXY_H__
+
+#include <dbus/dbus-glib.h>
+
+G_BEGIN_DECLS
+
+#define GRUSCHLER_TYPE_CHANNEL_PROXY (gruschler_channel_proxy_get_type ())
+#define GRUSCHLER_CHANNEL_PROXY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GRUSCHLER_TYPE_CHANNEL_PROXY, GruschlerChannelProxy))
+#define GRUSCHLER_CHANNEL_PROXY_CLASS(cls) (G_TYPE_CHECK_CLASS_CAST ((cls), GRUSCHLER_TYPE_CHANNEL_PROXY, GruschlerChannelProxyClass))
+#define GRUSCHLER_IS_CHANNEL_PROXY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GRUSCHLER_TYPE_CHANNEL_PROXY))
+#define GRUSCHLER_IS_CHANNEL_PROXY_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((obj), GRUSCHLER_TYPE_CHANNEL_PROXY))
+#define GRUSCHLER_CHANNEL_PROXY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GRUSCHLER_TYPE_CHANNEL_PROXY, GruschlerChannelProxyClass))
+#define GRUSCHLER_CHANNEL_PROXY_DBUS_INTERFACE ("org.freedesktop.Telepathy.Gruschler.ChannelProxy")
+
+typedef struct _GruschlerChannelProxy GruschlerChannelProxy;
+typedef struct _GruschlerChannelProxyClass GruschlerChannelProxyClass;
+
+GType
+gruschler_channel_proxy_get_type (void) G_GNUC_CONST;
+
+DBusGProxy *
+gruschler_channel_proxy_new (DBusGProxy *browser_service,
+ const char *object_path);
+
+gboolean
+gruschler_channel_proxy_open (DBusGProxy *channel,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* __GRUSCHLER_CHANNEL_PROXY_H__ */
+
diff --git a/src/browser-service/marshallers.list b/src/browser-service/marshallers.list
new file mode 100644
index 0000000..1c09e9e
--- /dev/null
+++ b/src/browser-service/marshallers.list
@@ -0,0 +1,2 @@
+VOID:STRING,UINT
+VOID:STRING,BOXED
diff --git a/src/connection-manager.c b/src/connection-manager/connection-manager.c
index 824ea2e..824ea2e 100644
--- a/src/connection-manager.c
+++ b/src/connection-manager/connection-manager.c
diff --git a/src/connection-manager.h b/src/connection-manager/connection-manager.h
index 3609fbd..3609fbd 100644
--- a/src/connection-manager.h
+++ b/src/connection-manager/connection-manager.h
diff --git a/src/facebook-connection.c b/src/connection-manager/facebook-connection.c
index 3d9273f..3d9273f 100644
--- a/src/facebook-connection.c
+++ b/src/connection-manager/facebook-connection.c
diff --git a/src/facebook-connection.h b/src/connection-manager/facebook-connection.h
index 80b7e45..80b7e45 100644
--- a/src/facebook-connection.h
+++ b/src/connection-manager/facebook-connection.h
diff --git a/src/facebook-contact-list.c b/src/connection-manager/facebook-contact-list.c
index 020d8d1..020d8d1 100644
--- a/src/facebook-contact-list.c
+++ b/src/connection-manager/facebook-contact-list.c
diff --git a/src/facebook-contact-list.h b/src/connection-manager/facebook-contact-list.h
index 97b47ce..97b47ce 100644
--- a/src/facebook-contact-list.h
+++ b/src/connection-manager/facebook-contact-list.h
diff --git a/src/main.c b/src/connection-manager/main.c
index 1feaf2e..1feaf2e 100644
--- a/src/main.c
+++ b/src/connection-manager/main.c
diff --git a/tests/phonebook.js b/tests/phonebook.js
new file mode 100644
index 0000000..1999ea8
--- /dev/null
+++ b/tests/phonebook.js
@@ -0,0 +1 @@
+for (;;);{"error":0,"errorSummary":"","errorDescription":"","errorIsWarning":false,"payload":{"phone":{"580470081":{"cell":"358504871917"},"537462825":{"cell":"+44 7503373836"},"1388303124":{"phone":"+41 44 740 6374","cell":"+41 79 601 2573"},"1551425973":{"cell":"+49 177 4047480"},"669198432":{"cell":"+49-1520-6135210"},"534688144":{"cell":"0048510113510"},"515036710":{"phone":"0034933020816","cell":"0034666350320"},"763040959":{"cell":"+32473888449"},"204504892":{"cell":"+44 7894822184"},"1147471757":{"phone":"+493954585454","cell":"+4917621305287"},"863605649":{"phone":"+441912819444","cell":"+447985101959"},"36904498":{"cell":"+44-7834-491733"},"1605372670":{"cell":"+4917648869110"},"5409882":{"phone":"214.810.6565","cell":"817.917.4545"},"625072498":{"cell":"358505686514"},"500830522":{"cell":"+1-408-605-1621"},"1551841992":{"cell":"+44 774 73 222 03"},"523775632":{"phone":"01433650428","cell":"447891533856"},"61001428":{"cell":"07909827212"},"711233913":{"cell":"+49 170 7917711"},"708112763":{"cell":"358504873505"}}}}
diff --git a/tests/test-browser-service.c b/tests/test-browser-service.c
new file mode 100644
index 0000000..76a1f2c
--- /dev/null
+++ b/tests/test-browser-service.c
@@ -0,0 +1,98 @@
+#include "config.h"
+#include <dbus/dbus-glib.h>
+
+#include "browser-service/browser-service.h"
+#include "browser-service/channel-proxy.h"
+
+static void
+request_started_cb (DBusGProxy *channel,
+ const char *request)
+{
+ g_print ("%s: %s\n", G_STRFUNC, request);
+}
+
+static void
+request_stopped_cb (DBusGProxy *channel,
+ const char *request,
+ guint status,
+ gpointer user_data)
+{
+ GMainLoop *loop = user_data;
+ g_print ("%s: %s: status=%d\n", G_STRFUNC, request, status);
+ g_main_loop_quit (loop);
+}
+
+static void
+data_available_cb (DBusGProxy *channel,
+ const char *request,
+ GArray *data)
+{
+ g_print ("%s: %s: count=%d\n", G_STRFUNC, request, data->len);
+ g_print ("%s...\n", g_strndup(data->data, MIN (data->len, 60)));
+}
+
+int
+main (int argc,
+ char **argv)
+{
+ DBusGConnection *session = NULL;
+ DBusGProxy *service = NULL;
+ DBusGProxy *channel = NULL;
+ GMainLoop *loop = NULL;
+ GError *error = NULL;
+ char *path = NULL;
+
+ g_type_init ();
+
+ loop = g_main_loop_new (NULL, FALSE);
+ session = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
+
+ if (!session)
+ goto OUT;
+
+ service = gruschler_browser_service_new (session, &error);
+
+ if (!service)
+ goto OUT;
+
+ g_print ("service name: %s\n", dbus_g_proxy_get_bus_name (service));
+
+ channel = gruschler_browser_service_new_channel (service,
+ "http://www.facebook.com/home.php",
+ &error);
+
+ g_print ("channel path: %s\n", dbus_g_proxy_get_path (channel));
+
+ dbus_g_proxy_connect_signal (channel, "RequestStarted",
+ G_CALLBACK (request_started_cb), loop, NULL);
+ dbus_g_proxy_connect_signal (channel, "RequestStopped",
+ G_CALLBACK (request_stopped_cb), loop, NULL);
+ dbus_g_proxy_connect_signal (channel, "DataAvailable",
+ G_CALLBACK (data_available_cb), loop, NULL);
+
+ if (!gruschler_channel_proxy_open (channel, &error))
+ goto OUT;
+
+ g_main_loop_run (loop);
+
+OUT:
+ if (error)
+ {
+ g_printerr ("%s(%d): %s\n",
+ g_quark_to_string (error->domain),
+ error->code, error->message);
+ g_error_free (error);
+ }
+
+ if (channel)
+ g_object_unref (channel);
+ if (service)
+ g_object_unref (service);
+
+ if (loop)
+ g_main_loop_unref (loop);
+
+ g_free (path);
+
+ return (error ? 1 : 0);
+}
diff --git a/tests/test-browser-service.py b/tests/test-browser-service.py
new file mode 100644
index 0000000..8b5eb73
--- /dev/null
+++ b/tests/test-browser-service.py
@@ -0,0 +1,37 @@
+#!/usr/bin/python2.5
+
+import dbus
+import dbus.mainloop.glib
+import glib
+
+loop = dbus.mainloop.glib.DBusGMainLoop()
+dbus.set_default_main_loop(loop)
+
+session = dbus.SessionBus()
+
+proxy = session.get_object('org.freedesktop.Telepathy.Gruschler.BrowserService',
+ '/org/freedesktop/Telepathy/Gruschler/BrowserService')
+
+
+service = dbus.Interface(proxy, 'org.freedesktop.Telepathy.Gruschler.BrowserService')
+path = service.NewChannel('http://www.facebook.com/home.php')
+
+print service.bus_name, path
+
+def request_started_cb(*args):
+ print args
+
+def request_stopped_cb(*args):
+ print args
+ loop.quit()
+
+def data_available_cb(*args):
+ print args
+
+proxy = session.get_object(service.bus_name, path)
+proxy.connect_to_signal('RequestStarted', request_started_cb)
+proxy.connect_to_signal('RequestStopped', request_stopped_cb)
+proxy.connect_to_signal('DataAvailable', data_available_cb)
+
+loop = glib.MainLoop()
+loop.run()