summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorNicolas Dufresne <nicolas.dufresne@collabora.co.uk>2010-02-25 16:17:46 -0500
committerNicolas Dufresne <nicolas.dufresne@collabora.co.uk>2010-02-25 16:17:46 -0500
commit8d43e64804d43aafe3ab6db69ecdd47395638955 (patch)
treef919157e63239ee3a32b7bf8eaf6cec45c6dbd1b /src
parent8d48b82762275383debd8197e63f3c78e05c2f06 (diff)
parenta88baea0dd8f3144678175cb871c84576a511c77 (diff)
downloadtelepathy-gabble-8d43e64804d43aafe3ab6db69ecdd47395638955.tar.gz
Merge branch 'email-notification'
Reviewed-by: Simon McVittie <simon.mcvittie@collabora.co.uk>
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am2
-rw-r--r--src/conn-mail-notif.c757
-rw-r--r--src/conn-mail-notif.h38
-rw-r--r--src/connection.c46
-rw-r--r--src/connection.h11
-rw-r--r--src/debug.c1
-rw-r--r--src/debug.h3
-rw-r--r--src/ft-channel.c2
-rw-r--r--src/jingle-session.c2
-rw-r--r--src/namespaces.h1
-rw-r--r--src/util.h2
-rw-r--r--src/vcard-manager.c2
12 files changed, 860 insertions, 7 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index e0d656a74..02ef616c9 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -53,6 +53,8 @@ libgabble_convenience_la_SOURCES = \
conn-presence.c \
conn-sidecars.h \
conn-sidecars.c \
+ conn-mail-notif.h \
+ conn-mail-notif.c \
connection.h \
connection.c \
connection-manager.h \
diff --git a/src/conn-mail-notif.c b/src/conn-mail-notif.c
new file mode 100644
index 000000000..072c693de
--- /dev/null
+++ b/src/conn-mail-notif.c
@@ -0,0 +1,757 @@
+/*
+ * conn-mail-notif - Gabble mail notification interface
+ * Copyright (C) 2009-2010 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
+ */
+
+/* This is implementation of Gmail Notification protocol as documented at
+ * http://code.google.com/apis/talk/jep_extensions/gmail.html
+ * This is the only protocol supported at the moment. To add new protocol,
+ * one would have to split the google specific parts wich are the
+ * update_unread_mails() function and the new-mail signal on google xml
+ * namespace. The data structure and suscription mechanism shall remain across
+ * protocols.
+ */
+
+#include "config.h"
+
+#include "conn-mail-notif.h"
+
+#include <string.h>
+
+#include <dbus/dbus-glib-lowlevel.h>
+#include <telepathy-glib/dbus.h>
+#include <telepathy-glib/interfaces.h>
+#include <telepathy-glib/svc-connection.h>
+#include <telepathy-glib/util.h>
+
+#define DEBUG_FLAG GABBLE_DEBUG_MAIL_NOTIF
+#include "connection.h"
+#include "debug.h"
+#include "extensions/extensions.h"
+#include "namespaces.h"
+#include "util.h"
+
+
+enum
+{
+ PROP_MAIL_NOTIFICATION_FLAGS,
+ PROP_UNREAD_MAIL_COUNT,
+ PROP_UNREAD_MAILS,
+ PROP_MAIL_ADDRESS,
+ NUM_OF_PROP,
+};
+
+
+static void unsubscribe (GabbleConnection *conn, const gchar *name,
+ gboolean remove_all);
+static void update_unread_mails (GabbleConnection *conn);
+
+
+static void
+subscriber_name_owner_changed (TpDBusDaemon *dbus_daemon,
+ const gchar *name,
+ const gchar *new_owner,
+ gpointer user_data)
+{
+ GabbleConnection *conn = GABBLE_CONNECTION (user_data);
+
+ if (CHECK_STR_EMPTY (new_owner))
+ {
+ DEBUG ("Sender removed: %s", name);
+ unsubscribe (conn, name, TRUE);
+ }
+}
+
+
+static void
+unsubscribe (GabbleConnection *conn,
+ const gchar *name, gboolean remove_all)
+{
+ guint count;
+
+ g_return_if_fail (g_hash_table_size (conn->mail_subscribers) > 0);
+
+ count = GPOINTER_TO_UINT (
+ g_hash_table_lookup (conn->mail_subscribers, name));
+
+ if (count == 1 || remove_all)
+ {
+ tp_dbus_daemon_cancel_name_owner_watch (conn->daemon, name,
+ subscriber_name_owner_changed, conn);
+
+ g_hash_table_remove (conn->mail_subscribers, name);
+
+ if (g_hash_table_size (conn->mail_subscribers) == 0)
+ {
+ DEBUG ("Last subscriber unsubscribed, cleaning up!");
+ g_free (conn->inbox_url);
+ conn->inbox_url = NULL;
+
+ if (conn->unread_mails != NULL)
+ {
+ g_hash_table_unref (conn->unread_mails);
+ conn->unread_mails = NULL;
+ }
+ }
+ }
+ else
+ {
+ g_hash_table_insert (conn->mail_subscribers, g_strdup (name),
+ GUINT_TO_POINTER (--count));
+ }
+}
+
+
+static inline gboolean
+check_supported_or_dbus_return (GabbleConnection *conn,
+ DBusGMethodInvocation *context)
+{
+ if (TP_BASE_CONNECTION (conn)->status != TP_CONNECTION_STATUS_CONNECTED)
+ {
+ GError e = { TP_ERRORS, TP_ERROR_DISCONNECTED, "Not connected" };
+ dbus_g_method_return_error (context, &e);
+ return TRUE;
+ }
+
+ if (!(conn->features & GABBLE_CONNECTION_FEATURES_GOOGLE_MAIL_NOTIFY))
+ {
+ tp_dbus_g_method_return_not_implemented (context);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+static void
+gabble_mail_notification_subscribe (GabbleSvcConnectionInterfaceMailNotification *iface,
+ DBusGMethodInvocation *context)
+{
+ GabbleConnection *conn = GABBLE_CONNECTION (iface);
+ gchar *subscriber;
+ guint count;
+
+ if (check_supported_or_dbus_return (conn, context))
+ return;
+
+ subscriber = dbus_g_method_get_sender (context);
+
+ DEBUG ("Subscribe called by: %s", subscriber);
+
+ count = GPOINTER_TO_UINT (
+ g_hash_table_lookup (conn->mail_subscribers, subscriber));
+
+ /* Gives subscriber ownership to mail_subscribers hash table */
+ g_hash_table_insert (conn->mail_subscribers, subscriber,
+ GUINT_TO_POINTER (++count));
+
+ if (count == 1)
+ {
+ if (g_hash_table_size (conn->mail_subscribers) == 1)
+ update_unread_mails (conn);
+
+ tp_dbus_daemon_watch_name_owner (conn->daemon, subscriber,
+ subscriber_name_owner_changed, conn, NULL);
+ }
+
+ gabble_svc_connection_interface_mail_notification_return_from_subscribe (
+ context);
+}
+
+
+static void
+gabble_mail_notification_unsubscribe (GabbleSvcConnectionInterfaceMailNotification *iface,
+ DBusGMethodInvocation *context)
+{
+ GabbleConnection *conn = GABBLE_CONNECTION (iface);
+ gchar *subscriber;
+
+ if (check_supported_or_dbus_return (conn, context))
+ return;
+
+ subscriber = dbus_g_method_get_sender (context);
+
+ DEBUG ("Unsubscribe called by: %s", subscriber);
+
+ if (!g_hash_table_lookup_extended (conn->mail_subscribers, subscriber,
+ NULL, NULL))
+ {
+ GError e = { TP_ERRORS, TP_ERROR_NOT_AVAILABLE, "Not subscribed" };
+
+ DEBUG ("Subscriber '%s' is not subscribed!", subscriber);
+
+ dbus_g_method_return_error (context, &e);
+ g_free (subscriber);
+ return;
+ }
+
+ unsubscribe (conn, subscriber, FALSE);
+
+ g_free (subscriber);
+ gabble_svc_connection_interface_mail_notification_return_from_unsubscribe (context);
+}
+
+
+static void
+gabble_mail_notification_request_inbox_url (
+ GabbleSvcConnectionInterfaceMailNotification *iface,
+ DBusGMethodInvocation *context)
+{
+ GabbleConnection *conn = GABBLE_CONNECTION (iface);
+ GValueArray *result;
+ GPtrArray *empty_array;
+
+ if (check_supported_or_dbus_return (conn, context))
+ return;
+
+ /* TODO Make sure we don't have to authenticate again */
+
+ empty_array = g_ptr_array_new ();
+
+ result = tp_value_array_build (3,
+ G_TYPE_STRING, conn->inbox_url ? conn->inbox_url : "",
+ G_TYPE_UINT, GABBLE_HTTP_METHOD_GET,
+ GABBLE_ARRAY_TYPE_HTTP_POST_DATA_LIST, empty_array,
+ G_TYPE_INVALID);
+
+ gabble_svc_connection_interface_mail_notification_return_from_request_inbox_url (
+ context, result);
+
+ g_value_array_free (result);
+ g_ptr_array_free (empty_array, TRUE);
+}
+
+
+static void
+gabble_mail_notification_request_mail_url (
+ GabbleSvcConnectionInterfaceMailNotification *iface,
+ const gchar *in_id,
+ const GValue *in_url_data,
+ DBusGMethodInvocation *context)
+{
+ GabbleConnection *conn = GABBLE_CONNECTION (iface);
+ GValueArray *result;
+ gchar *url = NULL;
+ GPtrArray *empty_array;
+
+ if (check_supported_or_dbus_return (conn, context))
+ return;
+
+ /* TODO Make sure we don't have to authenticate again */
+
+ if (conn->inbox_url != NULL)
+ {
+ /* IDs are decimal on the XMPP side and hexadecimal on the wemail side. */
+ guint64 tid = g_ascii_strtoull (in_id, NULL, 0);
+ url = g_strdup_printf ("%s/#inbox/%" G_GINT64_MODIFIER "x",
+ conn->inbox_url, tid);
+ }
+
+ empty_array = g_ptr_array_new ();
+
+ result = tp_value_array_build (3,
+ G_TYPE_STRING, url ? url : "",
+ G_TYPE_UINT, GABBLE_HTTP_METHOD_GET,
+ GABBLE_ARRAY_TYPE_HTTP_POST_DATA_LIST, empty_array,
+ G_TYPE_INVALID);
+
+ gabble_svc_connection_interface_mail_notification_return_from_request_mail_url (
+ context, result);
+
+ g_value_array_free (result);
+ g_ptr_array_free (empty_array, TRUE);
+}
+
+
+static gboolean
+sender_each (WockyXmppNode *node,
+ gpointer user_data)
+{
+ GPtrArray *senders = user_data;
+
+ if (!tp_strdiff ("1", wocky_xmpp_node_get_attribute (node, "unread")))
+ {
+ GValueArray *sender;
+ const gchar *name;
+ const gchar *address;
+
+ name = wocky_xmpp_node_get_attribute (node, "name");
+ address = wocky_xmpp_node_get_attribute (node, "address");
+
+ sender = tp_value_array_build (2,
+ G_TYPE_STRING, name ? name : "",
+ G_TYPE_STRING, address ? address : "",
+ G_TYPE_INVALID);
+
+ g_ptr_array_add (senders, sender);
+ }
+ return TRUE;
+}
+
+
+static gboolean
+handle_senders (WockyXmppNode *parent_node,
+ GHashTable *mail)
+{
+ gboolean dirty = FALSE;
+ WockyXmppNode *node;
+
+ node = wocky_xmpp_node_get_child (parent_node, "senders");
+ if (node != NULL)
+ {
+ GType addr_list_type = GABBLE_ARRAY_TYPE_MAIL_ADDRESS_LIST;
+ GPtrArray *senders, *old_senders;
+
+ senders = g_ptr_array_new ();
+ wocky_xmpp_node_each_child (node, sender_each, senders);
+
+ old_senders = tp_asv_get_boxed (mail, "senders", addr_list_type);
+
+ if (old_senders == NULL || senders->len != old_senders->len)
+ dirty = TRUE;
+
+ tp_asv_take_boxed (mail, "senders", addr_list_type, senders);
+ }
+
+ return dirty;
+}
+
+
+static gboolean
+handle_subject (WockyXmppNode *parent_node,
+ GHashTable *mail)
+{
+ gboolean dirty = FALSE;
+ WockyXmppNode *node;
+
+ node = wocky_xmpp_node_get_child (parent_node, "subject");
+ if (node != NULL)
+ {
+ if (tp_strdiff (node->content, tp_asv_get_string (mail, "subject")))
+ {
+ dirty = TRUE;
+ tp_asv_set_string (mail, "subject", node->content);
+ }
+ }
+
+ return dirty;
+}
+
+
+static gboolean
+handle_snippet (WockyXmppNode *parent_node,
+ GHashTable *mail)
+{
+ gboolean dirty = FALSE;
+ WockyXmppNode *node;
+
+ node = wocky_xmpp_node_get_child (parent_node, "snippet");
+ if (node != NULL)
+ {
+ if (tp_strdiff (node->content, tp_asv_get_string (mail, "content")))
+ {
+ dirty = TRUE;
+ tp_asv_set_boolean (mail, "truncated", TRUE);
+ tp_asv_set_string (mail, "content", node->content);
+ }
+ }
+
+ return dirty;
+}
+
+
+/* Structure used has user_data mail_thread_info_each callback */
+typedef struct
+{
+ GabbleConnection *conn;
+ /* stolen from conn -> unread_mails, the left items in this is
+ * represent the removed emails */
+ GHashTable *old_mails;
+ GPtrArray *mails_added;
+} MailThreadCollector;
+
+static gboolean
+mail_thread_info_each (WockyXmppNode *node,
+ gpointer user_data)
+{
+ MailThreadCollector *collector = user_data;
+
+ if (!tp_strdiff (node->name, "mail-thread-info"))
+ {
+ GHashTable *mail = NULL;
+ const gchar *val_str;
+ gchar *tid;
+ gboolean dirty = FALSE;
+
+ val_str = wocky_xmpp_node_get_attribute (node, "tid");
+
+ /* We absolutly need an ID */
+ if (val_str == NULL)
+ return TRUE;
+
+ tid = g_strdup (val_str);
+
+ if (collector->old_mails != NULL)
+ {
+ mail = g_hash_table_lookup (collector->old_mails, tid);
+ g_hash_table_steal (collector->old_mails, tid);
+ }
+
+ if (mail == NULL)
+ {
+ mail = tp_asv_new ("id", G_TYPE_STRING, tid,
+ "url-data", G_TYPE_STRING, "",
+ NULL);
+ dirty = TRUE;
+ }
+
+ val_str = wocky_xmpp_node_get_attribute (node, "date");
+
+ if (val_str != NULL)
+ {
+ gint64 date;
+
+ date = (g_ascii_strtoll (val_str, NULL, 0) / 1000l);
+ if (date != tp_asv_get_int64 (mail, "received-timestamp", NULL))
+ dirty = TRUE;
+
+ tp_asv_set_int64 (mail, "received-timestamp", date);
+ }
+
+ if (handle_senders (node, mail))
+ dirty = TRUE;
+
+ if (handle_subject (node, mail))
+ dirty = TRUE;
+
+ if (handle_snippet (node, mail))
+ dirty = TRUE;
+
+ /* gives tid ownership to unread_mails hash table */
+ g_hash_table_insert (collector->conn->unread_mails, tid, mail);
+
+ if (dirty)
+ g_ptr_array_add (collector->mails_added, mail);
+ }
+
+ return TRUE;
+}
+
+
+static void
+store_unread_mails (GabbleConnection *conn,
+ WockyXmppNode *mailbox)
+{
+ GHashTableIter iter;
+ GPtrArray *mails_removed;
+ MailThreadCollector collector;
+ const gchar *url;
+
+ collector.conn = conn;
+ collector.old_mails = conn->unread_mails;
+ conn->unread_mails = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, (GDestroyNotify) g_hash_table_unref);
+ collector.mails_added = g_ptr_array_new ();
+
+ url = wocky_xmpp_node_get_attribute (mailbox, "url");
+ g_free (conn->inbox_url);
+ conn->inbox_url = g_strdup (url);
+
+ /* Store new mails */
+ wocky_xmpp_node_each_child (mailbox, mail_thread_info_each, &collector);
+
+ /* Generate the list of removed thread IDs */
+ mails_removed = g_ptr_array_new_with_free_func (g_free);
+
+ if (collector.old_mails != NULL)
+ {
+ gpointer key;
+
+ g_hash_table_iter_init (&iter, collector.old_mails);
+
+ while (g_hash_table_iter_next (&iter, &key, NULL))
+ {
+ gchar *tid = key;
+ g_ptr_array_add (mails_removed, g_strdup (tid));
+ }
+
+ g_hash_table_unref (collector.old_mails);
+ }
+ g_ptr_array_add (mails_removed, NULL);
+
+ if (collector.mails_added->len > 0 || mails_removed->len > 0)
+ gabble_svc_connection_interface_mail_notification_emit_unread_mails_changed (
+ conn, g_hash_table_size (conn->unread_mails), collector.mails_added,
+ (const char **)mails_removed->pdata);
+
+ g_ptr_array_free (collector.mails_added, TRUE);
+ g_ptr_array_free (mails_removed, TRUE);
+}
+
+
+static void
+query_unread_mails_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GError *error = NULL;
+ WockyXmppNode *node;
+ WockyPorter *porter = WOCKY_PORTER (source_object);
+ WockyXmppStanza *reply = wocky_porter_send_iq_finish (porter, res, &error);
+
+ if (error != NULL)
+ {
+ DEBUG ("Failed retreive unread emails information: %s", error->message);
+ g_error_free (error);
+ goto end;
+ }
+
+ DEBUG ("Got unread mail details");
+
+ node = wocky_xmpp_node_get_child (reply->node, "mailbox");
+
+ if (node != NULL)
+ {
+ GabbleConnection *conn = GABBLE_CONNECTION (user_data);
+ store_unread_mails (conn, node);
+ }
+
+end:
+ if (reply != NULL)
+ g_object_unref (reply);
+}
+
+
+static void
+update_unread_mails (GabbleConnection *conn)
+{
+ WockyXmppStanza *query;
+ WockyPorter *porter = wocky_session_get_porter (conn->session);
+
+ DEBUG ("Updating unread mails information");
+
+ query = wocky_xmpp_stanza_build (WOCKY_STANZA_TYPE_IQ,
+ WOCKY_STANZA_SUB_TYPE_GET, NULL, NULL,
+ WOCKY_NODE, "query",
+ WOCKY_NODE_XMLNS, NS_GOOGLE_MAIL_NOTIFY,
+ WOCKY_NODE_END,
+ WOCKY_STANZA_END);
+ wocky_porter_send_iq_async (porter, query, NULL, query_unread_mails_cb, conn);
+ g_object_unref (query);
+}
+
+
+static gboolean
+new_mail_handler (WockyPorter *porter,
+ WockyXmppStanza *stanza,
+ gpointer user_data)
+{
+ GabbleConnection *conn = GABBLE_CONNECTION (user_data);
+
+ if (g_hash_table_size (conn->mail_subscribers) > 0)
+ {
+ DEBUG ("Got Google <new-mail> notification");
+ update_unread_mails (conn);
+ }
+
+ return TRUE;
+}
+
+
+static void
+connection_status_changed (GabbleConnection *conn,
+ TpConnectionStatus status,
+ TpConnectionStatusReason reason,
+ gpointer user_data)
+{
+ if (status == TP_CONNECTION_STATUS_CONNECTED
+ && conn->features & GABBLE_CONNECTION_FEATURES_GOOGLE_MAIL_NOTIFY)
+ {
+ DEBUG ("Connected, registering Google 'new-mail' notification");
+
+ conn->new_mail_handler_id =
+ wocky_porter_register_handler (wocky_session_get_porter (conn->session),
+ WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_SET,
+ NULL, WOCKY_PORTER_HANDLER_PRIORITY_NORMAL,
+ new_mail_handler, conn,
+ WOCKY_NODE, "new-mail",
+ WOCKY_NODE_XMLNS, NS_GOOGLE_MAIL_NOTIFY,
+ WOCKY_NODE_END,
+ WOCKY_STANZA_END);
+ }
+}
+
+
+void
+conn_mail_notif_init (GabbleConnection *conn)
+{
+ conn->mail_subscribers = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, NULL);
+ conn->inbox_url = NULL;
+ conn->unread_mails = NULL;
+
+ g_signal_connect (conn, "status-changed",
+ G_CALLBACK (connection_status_changed), conn);
+}
+
+
+static gboolean
+foreach_cancel_watch (gpointer key,
+ gpointer value,
+ gpointer user_data)
+{
+ const gchar *subscriber = key;
+ GabbleConnection *conn = GABBLE_CONNECTION (user_data);
+
+ tp_dbus_daemon_cancel_name_owner_watch (conn->daemon,
+ subscriber, subscriber_name_owner_changed, conn);
+
+ return TRUE;
+}
+
+
+void
+conn_mail_notif_dispose (GabbleConnection *conn)
+{
+ if (conn->mail_subscribers)
+ {
+ g_hash_table_foreach_remove (conn->mail_subscribers,
+ foreach_cancel_watch, conn);
+ g_hash_table_unref (conn->mail_subscribers);
+ conn->mail_subscribers = NULL;
+ }
+
+ g_free (conn->inbox_url);
+ conn->inbox_url = NULL;
+
+ if (conn->unread_mails != NULL)
+ g_hash_table_unref (conn->unread_mails);
+
+ conn->unread_mails = NULL;
+
+ if (conn->new_mail_handler_id != 0)
+ {
+ WockyPorter *porter = wocky_session_get_porter (conn->session);
+ wocky_porter_unregister_handler (porter, conn->new_mail_handler_id);
+ conn->new_mail_handler_id = 0;
+ }
+}
+
+
+void
+conn_mail_notif_iface_init (gpointer g_iface,
+ gpointer iface_data)
+{
+ GabbleSvcConnectionInterfaceMailNotificationClass *klass = g_iface;
+
+#define IMPLEMENT(x) gabble_svc_connection_interface_mail_notification_implement_##x (\
+ klass, gabble_mail_notification_##x)
+ IMPLEMENT (subscribe);
+ IMPLEMENT (unsubscribe);
+ IMPLEMENT (request_inbox_url);
+ IMPLEMENT (request_mail_url);
+#undef IMPLEMENT
+}
+
+
+static GPtrArray *
+get_unread_mails (GabbleConnection *conn)
+{
+ GPtrArray *mails = g_ptr_array_new ();
+ GHashTableIter iter;
+ gpointer value;
+
+ if (conn->unread_mails != NULL)
+ {
+ g_hash_table_iter_init (&iter, conn->unread_mails);
+
+ while (g_hash_table_iter_next (&iter, NULL, &value))
+ {
+ GHashTable *mail = value;
+ g_ptr_array_add (mails, mail);
+ }
+ }
+
+ return mails;
+}
+
+
+void
+conn_mail_notif_properties_getter (GObject *object,
+ GQuark interface,
+ GQuark name,
+ GValue *value,
+ gpointer getter_data)
+{
+ static GQuark prop_quarks[NUM_OF_PROP] = {0};
+ GabbleConnection *conn = GABBLE_CONNECTION (object);
+
+ if (G_UNLIKELY (prop_quarks[0] == 0))
+ {
+ prop_quarks[PROP_MAIL_NOTIFICATION_FLAGS] =
+ g_quark_from_static_string ("MailNotificationFlags");
+ prop_quarks[PROP_UNREAD_MAIL_COUNT] =
+ g_quark_from_static_string ("UnreadMailCount");
+ prop_quarks[PROP_UNREAD_MAILS] =
+ g_quark_from_static_string ("UnreadMails");
+ prop_quarks[PROP_MAIL_ADDRESS] =
+ g_quark_from_static_string ("MailAddress");
+ }
+
+ DEBUG ("MailNotification get property %s", g_quark_to_string (name));
+
+ if (name == prop_quarks[PROP_MAIL_NOTIFICATION_FLAGS])
+ {
+ if (conn->features & GABBLE_CONNECTION_FEATURES_GOOGLE_MAIL_NOTIFY)
+ g_value_set_uint (value,
+ GABBLE_MAIL_NOTIFICATION_FLAG_SUPPORTS_UNREAD_MAIL_COUNT
+ | GABBLE_MAIL_NOTIFICATION_FLAG_SUPPORTS_UNREAD_MAILS
+ | GABBLE_MAIL_NOTIFICATION_FLAG_SUPPORTS_REQUEST_INBOX_URL
+ | GABBLE_MAIL_NOTIFICATION_FLAG_SUPPORTS_REQUEST_MAIL_URL
+ | GABBLE_MAIL_NOTIFICATION_FLAG_THREAD_BASED
+ );
+ else
+ g_value_set_uint (value, 0);
+ }
+ else if (name == prop_quarks[PROP_UNREAD_MAIL_COUNT])
+ {
+ g_value_set_uint (value,
+ conn->unread_mails ? g_hash_table_size (conn->unread_mails) : 0);
+ }
+ else if (name == prop_quarks[PROP_UNREAD_MAILS])
+ {
+ GPtrArray *mails = get_unread_mails (conn);
+ g_value_set_boxed (value, mails);
+ g_ptr_array_free (mails, TRUE);
+ }
+ else if (name == prop_quarks[PROP_MAIL_ADDRESS])
+ {
+ TpBaseConnection *base = TP_BASE_CONNECTION (object);
+ TpHandleRepoIface *contact_handles =
+ tp_base_connection_get_handles (base, TP_HANDLE_TYPE_CONTACT);
+ TpHandle self = tp_base_connection_get_self_handle (base);
+ const gchar *bare_jid = tp_handle_inspect (contact_handles, self);
+
+ /* After some testing I found that the bare jid (username@stream_server)
+ * always represent the e-mail address on Google account. */
+ g_value_set_string (value, bare_jid);
+ }
+ else
+ {
+ g_assert_not_reached ();
+ }
+}
diff --git a/src/conn-mail-notif.h b/src/conn-mail-notif.h
new file mode 100644
index 000000000..b1212e2bf
--- /dev/null
+++ b/src/conn-mail-notif.h
@@ -0,0 +1,38 @@
+/*
+ * conn-mail-notif.h - Header for Gabble connection mail notification interface
+ * Copyright (C) 2009 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 __CONN_EMAIL_NOTIF_H__
+#define __CONN_EMAIL_NOTIF_H__
+
+#include <glib-object.h>
+
+#include "connection.h"
+
+G_BEGIN_DECLS
+
+void conn_mail_notif_init (GabbleConnection *conn);
+void conn_mail_notif_dispose (GabbleConnection *conn);
+void conn_mail_notif_iface_init (gpointer g_iface, gpointer iface_data);
+void conn_mail_notif_properties_getter (GObject *object, GQuark interface,
+ GQuark name, GValue *value, gpointer getter_data);
+
+G_END_DECLS
+
+#endif /* __CONN_EMAIL_NOTIF_H__ */
+
diff --git a/src/connection.c b/src/connection.c
index d0643e2e1..250475d79 100644
--- a/src/connection.c
+++ b/src/connection.c
@@ -55,6 +55,7 @@
#include "conn-location.h"
#include "conn-presence.h"
#include "conn-sidecars.h"
+#include "conn-mail-notif.h"
#include "conn-olpc.h"
#include "debug.h"
#include "disco.h"
@@ -95,7 +96,7 @@ G_DEFINE_TYPE_WITH_CODE(GabbleConnection,
conn_avatars_iface_init);
G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_CAPABILITIES,
capabilities_service_iface_init);
- G_IMPLEMENT_INTERFACE(TP_TYPE_SVC_DBUS_PROPERTIES,
+ G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_DBUS_PROPERTIES,
tp_dbus_properties_mixin_iface_init);
G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_CONNECTION_INTERFACE_CONTACTS,
tp_contacts_mixin_iface_init);
@@ -118,6 +119,8 @@ G_DEFINE_TYPE_WITH_CODE(GabbleConnection,
olpc_gadget_iface_init);
G_IMPLEMENT_INTERFACE (GABBLE_TYPE_SVC_CONNECTION_FUTURE,
conn_future_iface_init);
+ G_IMPLEMENT_INTERFACE (GABBLE_TYPE_SVC_CONNECTION_INTERFACE_MAIL_NOTIFICATION,
+ conn_mail_notif_iface_init);
)
/* properties */
@@ -333,6 +336,7 @@ gabble_connection_constructor (GType type,
conn_olpc_activity_properties_init (self);
conn_location_init (self);
conn_sidecars_init (self);
+ conn_mail_notif_init (self);
tp_contacts_mixin_add_contact_attributes_iface (G_OBJECT (self),
TP_IFACE_CONNECTION_INTERFACE_CAPABILITIES,
@@ -421,11 +425,19 @@ gabble_connection_constructed (GObject *object)
static void
gabble_connection_init (GabbleConnection *self)
{
+ GError *error = NULL;
GabbleConnectionPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
GABBLE_TYPE_CONNECTION, GabbleConnectionPrivate);
DEBUG("Initializing (GabbleConnection *)%p", self);
+ self->daemon = tp_dbus_daemon_dup (&error);
+
+ if (self->daemon == NULL)
+ {
+ g_error ("Failed to connect to dbus daemon: %s", error->message);
+ }
+
self->priv = priv;
self->lmconn = lm_connection_new ();
@@ -724,6 +736,13 @@ gabble_connection_class_init (GabbleConnectionClass *gabble_connection_class)
{ "DecloakAutomatically", TWICE ("decloak-automatically") },
{ NULL }
};
+ static TpDBusPropertiesMixinPropImpl mail_notif_props[] = {
+ { "MailNotificationFlags", NULL, NULL },
+ { "UnreadMailCount", NULL, NULL },
+ { "UnreadMails", NULL, NULL },
+ { "MailAddress", NULL, NULL },
+ { NULL }
+ };
static TpDBusPropertiesMixinIfaceImpl prop_interfaces[] = {
{ GABBLE_IFACE_OLPC_GADGET,
conn_olpc_gadget_properties_getter,
@@ -745,6 +764,11 @@ gabble_connection_class_init (GabbleConnectionClass *gabble_connection_class)
tp_dbus_properties_mixin_setter_gobject_properties,
decloak_props,
},
+ { GABBLE_IFACE_CONNECTION_INTERFACE_MAIL_NOTIFICATION,
+ conn_mail_notif_properties_getter,
+ NULL,
+ mail_notif_props,
+ },
{ NULL }
};
@@ -1006,6 +1030,8 @@ gabble_connection_dispose (GObject *object)
g_hash_table_destroy (self->avatar_requests);
+ conn_mail_notif_dispose (self);
+
g_assert (priv->iq_disco_cb == NULL);
g_assert (priv->iq_unknown_cb == NULL);
g_assert (priv->olpc_msg_cb == NULL);
@@ -1079,6 +1105,12 @@ gabble_connection_dispose (GObject *object)
conn_sidecars_dispose (self);
+ if (self->daemon != NULL)
+ {
+ g_object_unref (self->daemon);
+ self->daemon = NULL;
+ }
+
if (G_OBJECT_CLASS (gabble_connection_parent_class)->dispose)
G_OBJECT_CLASS (gabble_connection_parent_class)->dispose (object);
}
@@ -2544,6 +2576,8 @@ connection_disco_cb (GabbleDisco *disco,
conn->features |= GABBLE_CONNECTION_FEATURES_PRESENCE_INVISIBLE;
else if (0 == strcmp (var, NS_PRIVACY))
conn->features |= GABBLE_CONNECTION_FEATURES_PRIVACY;
+ else if (0 == strcmp (var, NS_GOOGLE_MAIL_NOTIFY))
+ conn->features |= GABBLE_CONNECTION_FEATURES_GOOGLE_MAIL_NOTIFY;
}
}
@@ -2559,6 +2593,14 @@ connection_disco_cb (GabbleDisco *disco,
tp_base_connection_add_interfaces ((TpBaseConnection *) conn, ifaces);
}
+ if (conn->features & GABBLE_CONNECTION_FEATURES_GOOGLE_MAIL_NOTIFY)
+ {
+ const gchar *ifaces[] =
+ { GABBLE_IFACE_CONNECTION_INTERFACE_MAIL_NOTIFICATION, NULL };
+
+ tp_base_connection_add_interfaces ((TpBaseConnection *) conn, ifaces);
+ }
+
/* send presence to the server to indicate availability */
/* TODO: some way for the user to set this */
if (!_gabble_connection_signal_own_presence (conn, NULL, &error))
@@ -3287,7 +3329,7 @@ gabble_connection_send_presence (GabbleConnection *conn,
if (LM_MESSAGE_SUB_TYPE_SUBSCRIBE == sub_type)
lm_message_node_add_own_nick (message->node, conn);
- if (status != NULL && status[0] != '\0')
+ if (!CHECK_STR_EMPTY(status))
lm_message_node_add_child (message->node, "status", status);
result = _gabble_connection_send (conn, message, error);
diff --git a/src/connection.h b/src/connection.h
index 6bce3053c..f198741ec 100644
--- a/src/connection.h
+++ b/src/connection.h
@@ -28,6 +28,7 @@
#include <telepathy-glib/contacts-mixin.h>
#include <telepathy-glib/presence-mixin.h>
#include <telepathy-glib/dbus-properties-mixin.h>
+#include <telepathy-glib/dbus.h>
#include <wocky/wocky-session.h>
#include <wocky/wocky-pep-service.h>
@@ -74,6 +75,7 @@ typedef enum
GABBLE_CONNECTION_FEATURES_PRESENCE_INVISIBLE = 1 << 2,
GABBLE_CONNECTION_FEATURES_PRIVACY = 1 << 3,
GABBLE_CONNECTION_FEATURES_PEP = 1 << 4,
+ GABBLE_CONNECTION_FEATURES_GOOGLE_MAIL_NOTIFY = 1 << 5,
} GabbleConnectionFeatures;
typedef struct _GabbleConnectionPrivate GabbleConnectionPrivate;
@@ -120,6 +122,9 @@ struct _GabbleConnection {
TpPresenceMixin presence;
TpContactsMixin contacts;
+ /* DBus daemon instance */
+ TpDBusDaemon *daemon;
+
/* loudmouth connection */
LmConnection *lmconn;
WockySession *session;
@@ -187,6 +192,12 @@ struct _GabbleConnection {
/* gchar *interface → GList<DBusGMethodInvocation> */
GHashTable *pending_sidecars;
+ /* Mail Notification */
+ GHashTable *mail_subscribers;
+ gchar *inbox_url;
+ GHashTable *unread_mails;
+ guint new_mail_handler_id;
+
GabbleConnectionPrivate *priv;
};
diff --git a/src/debug.c b/src/debug.c
index ad180b556..55d0f88dd 100644
--- a/src/debug.c
+++ b/src/debug.c
@@ -39,6 +39,7 @@ static GDebugKey keys[] = {
{ "search", GABBLE_DEBUG_SEARCH },
{ "base-channel", GABBLE_DEBUG_BASE_CHANNEL },
{ "plugins", GABBLE_DEBUG_PLUGINS },
+ { "mail", GABBLE_DEBUG_MAIL_NOTIF },
{ 0, },
};
diff --git a/src/debug.h b/src/debug.h
index 5eacff62c..dcd2cb85a 100644
--- a/src/debug.h
+++ b/src/debug.h
@@ -32,7 +32,8 @@ typedef enum
GABBLE_DEBUG_FT = 1 << 18,
GABBLE_DEBUG_SEARCH = 1 << 19,
GABBLE_DEBUG_BASE_CHANNEL = 1 << 20,
- GABBLE_DEBUG_PLUGINS = 1 << 21
+ GABBLE_DEBUG_PLUGINS = 1 << 21,
+ GABBLE_DEBUG_MAIL_NOTIF = 1 << 22
} GabbleDebugFlags;
void gabble_debug_set_flags_from_env (void);
diff --git a/src/ft-channel.c b/src/ft-channel.c
index 4cf1be682..fa93abb8f 100644
--- a/src/ft-channel.c
+++ b/src/ft-channel.c
@@ -67,8 +67,6 @@ G_DEFINE_TYPE_WITH_CODE (GabbleFileTransferChannel, gabble_file_transfer_channel
file_transfer_iface_init);
);
-#define CHECK_STR_EMPTY(x) ((x) == NULL || (x)[0] == '\0')
-
#define GABBLE_UNDEFINED_FILE_SIZE G_MAXUINT64
static const char *gabble_file_transfer_channel_interfaces[] = { NULL };
diff --git a/src/jingle-session.c b/src/jingle-session.c
index d67ca6780..73f75b468 100644
--- a/src/jingle-session.c
+++ b/src/jingle-session.c
@@ -2038,7 +2038,7 @@ gabble_jingle_session_terminate (GabbleJingleSession *sess,
lm_message_node_add_child (r, reason_elt, NULL);
- if (text != NULL && *text != '\0')
+ if (!CHECK_STR_EMPTY(text))
lm_message_node_add_child (r, "text", text);
}
diff --git a/src/namespaces.h b/src/namespaces.h
index 50c6e322b..18b3feb5c 100644
--- a/src/namespaces.h
+++ b/src/namespaces.h
@@ -105,6 +105,7 @@
#define NS_X_CONFERENCE "jabber:x:conference"
#define NS_XMPP_STANZAS "urn:ietf:params:xml:ns:xmpp-stanzas"
#define NS_GEOLOC "http://jabber.org/protocol/geoloc"
+#define NS_GOOGLE_MAIL_NOTIFY "google:mail:notify"
#define NS_TEMPPRES "urn:xmpp:temppres:0"
diff --git a/src/util.h b/src/util.h
index aa6dbe67b..cd4de7b48 100644
--- a/src/util.h
+++ b/src/util.h
@@ -35,6 +35,8 @@
#include "types.h"
+#define CHECK_STR_EMPTY(x) ((x) == NULL || (x)[0] == '\0')
+
typedef GSList * NodeIter;
#define node_iter(node) (node->children)
#define node_iter_next(i) (g_slist_next (i))
diff --git a/src/vcard-manager.c b/src/vcard-manager.c
index 4353609f2..02877e040 100644
--- a/src/vcard-manager.c
+++ b/src/vcard-manager.c
@@ -810,7 +810,7 @@ observe_vcard (GabbleConnection *conn,
{
const gchar *fn = lm_message_node_get_value (fn_node);
- if (fn != NULL && *fn != '\0')
+ if (!CHECK_STR_EMPTY(fn))
{
field = "<FN>";
alias = g_strdup (fn);