diff options
author | Nicolas Dufresne <nicolas.dufresne@collabora.co.uk> | 2010-02-25 16:17:46 -0500 |
---|---|---|
committer | Nicolas Dufresne <nicolas.dufresne@collabora.co.uk> | 2010-02-25 16:17:46 -0500 |
commit | 8d43e64804d43aafe3ab6db69ecdd47395638955 (patch) | |
tree | f919157e63239ee3a32b7bf8eaf6cec45c6dbd1b /src | |
parent | 8d48b82762275383debd8197e63f3c78e05c2f06 (diff) | |
parent | a88baea0dd8f3144678175cb871c84576a511c77 (diff) | |
download | telepathy-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.am | 2 | ||||
-rw-r--r-- | src/conn-mail-notif.c | 757 | ||||
-rw-r--r-- | src/conn-mail-notif.h | 38 | ||||
-rw-r--r-- | src/connection.c | 46 | ||||
-rw-r--r-- | src/connection.h | 11 | ||||
-rw-r--r-- | src/debug.c | 1 | ||||
-rw-r--r-- | src/debug.h | 3 | ||||
-rw-r--r-- | src/ft-channel.c | 2 | ||||
-rw-r--r-- | src/jingle-session.c | 2 | ||||
-rw-r--r-- | src/namespaces.h | 1 | ||||
-rw-r--r-- | src/util.h | 2 | ||||
-rw-r--r-- | src/vcard-manager.c | 2 |
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); |