diff options
author | Seif Lotfy <seif@lotfy.com> | 2012-05-30 15:37:03 +0200 |
---|---|---|
committer | Seif Lotfy <seif@lotfy.com> | 2012-05-30 15:37:03 +0200 |
commit | 6c2ece0ba90e07fcc73046f196f6fca4ebdb803f (patch) | |
tree | 175dd1ffaa51883f9341a27bae602bbe76897044 | |
parent | 011cb860671cc734f1d2aca37f77f2fd16885d32 (diff) |
Add Telepathy observertelepathy-observer
-rw-r--r-- | configure.ac | 4 | ||||
-rw-r--r-- | src/Makefile.am | 4 | ||||
-rw-r--r-- | src/telepathy-observer.vala | 443 | ||||
-rw-r--r-- | src/zeitgeist-datahub.vala | 1 |
4 files changed, 451 insertions, 1 deletions
diff --git a/configure.ac b/configure.ac index a6269ba..5c8e58e 100644 --- a/configure.ac +++ b/configure.ac @@ -65,8 +65,10 @@ dnl ============================================== MIN_GLIB_VERSION=2.26.0 MIN_GTK_VERSION=2.16.0 MIN_ZEITGEIST_VERSION=0.3.3 +MIN_TP_GLIB_VERSION=0.18.0 +MIN_JSON_GLIB_VERSION=0.14.2 -LIBRARY_MODULES="glib-2.0 >= $MIN_GLIB_VERSION gobject-2.0 gio-2.0 gio-unix-2.0 zeitgeist-1.0 >= $MIN_ZEITGEIST_VERSION" +LIBRARY_MODULES="glib-2.0 >= $MIN_GLIB_VERSION gobject-2.0 gio-2.0 gio-unix-2.0 zeitgeist-1.0 >= $MIN_ZEITGEIST_VERSION telepathy-glib >= $MIN_TP_GLIB_VERSION json-glib-1.0 >= $MIN_JSON_GLIB_VERSION" PKG_CHECK_MODULES(DATAHUB_MODULES, [$LIBRARY_MODULES]) PKG_CHECK_MODULES(GTK, [gtk+-2.0 >= $MIN_GTK_VERSION]) diff --git a/src/Makefile.am b/src/Makefile.am index a7e9363..de5d208 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,11 +1,14 @@ AM_CPPFLAGS = \ -include $(CONFIG_HEADER) \ + $(pkg_check_modules) \ $(NULL) VALAFLAGS = \ --pkg gio-2.0 \ --pkg gio-unix-2.0 \ --pkg gtk+-2.0 \ + --pkg json-glib-1.0 \ + --pkg telepathy-glib \ --pkg zeitgeist-1.0 \ glib-extra.vapi \ $(top_srcdir)/config.vapi \ @@ -23,6 +26,7 @@ zeitgeist_datahub_SOURCES = \ downloads-directory-provider.vala \ kde-recent-document-provider.vala \ recent-manager-provider.vala \ + telepathy-observer.vala \ utils.vala \ zeitgeist-datahub.vala \ $(optional_zeitgeist_datahub_SOURCES) \ diff --git a/src/telepathy-observer.vala b/src/telepathy-observer.vala new file mode 100644 index 0000000..9124742 --- /dev/null +++ b/src/telepathy-observer.vala @@ -0,0 +1,443 @@ +/* + * Zeitgeist + * + * Copyright (C) 2012 Collabora Ltd. + * Authored by: Seif Lotfy <seif.lotfy@collabora.co.uk> + * Copyright (C) 2012 Eslam Mostafa <cseslam@gmail.com> + * + * This program 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 3 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * + */ + +using Zeitgeist; +using TelepathyGLib; +using Json; + +public class TelepathyObserver : DataProvider +{ + + private const string actor = "dbus://org.freedesktop.Telepathy.Logger.service"; + private const string tp_account_path = "x-telepathy-account-path:%s"; + private const string tp_identifier = "x-telepathy-identifier:%s"; + private const string ft_json_domain = "http://zeitgeist-project.com/1.0/telepathy/filetransfer"; + + private TelepathyGLib.DBusDaemon dbus = null; + private TelepathyGLib.AutomaticClientFactory factory = null; + private TelepathyGLib.SimpleObserver observer = null; + private HashTable<Channel, Timer> call_timers = null; + + public TelepathyObserver (DataHub datahub) throws GLib.Error + { + stdout.printf ("===== SET UP =====\n"); + GLib.Object (unique_id: "com.zeitgeist-project,datahub,telepathy-observer", + name: "Telepathy Observer", + description: "Logs IM, call and filetransfer from telepathy", + datahub: datahub); + + } + + construct + { + stdout.printf ("===== SET UP =====\n"); + call_timers = new HashTable<Channel, Timer> (str_hash, str_equal); + dbus = TelepathyGLib.DBusDaemon.dup (); + factory = new TelepathyGLib.AutomaticClientFactory (dbus); + + Quark[] channel_quark = {TelepathyGLib.Channel.get_feature_quark_contacts ()}; + TelepathyGLib.ContactFeature[] contact_quark = {TelepathyGLib.ContactFeature.ALIAS}; + + factory.add_channel_features (channel_quark); + factory.add_contact_features (contact_quark); + } + + // if vala didn't have bug in construct-only properties, the properties + // would be construct-only + public override string unique_id { get; construct set; } + public override string name { get; construct set; } + public override string description { get; construct set; } + + public override DataHub datahub { get; construct set; } + public override bool enabled { get; set; default = true; } + public override bool register { get; construct set; default = true; } + + private void print_event (Event event) + { + stdout.printf("Event:\n"); + stdout.printf(" - timestamp:%s\n", (string)event.get_timestamp ()); + stdout.printf(" - actor:%s\n", event.get_actor ()); + stdout.printf(" - interpretation:%s\n", event.get_interpretation ()); + stdout.printf(" - manifestation:%s\n", event.get_manifestation ()); + stdout.printf(" - origin:%s\n", event.get_origin ()); + stdout.printf(" - subjects:%i\n", event.num_subjects ()); + for (var i=0; i<event.num_subjects (); i++) + { + var subject = event.get_subject(i); + stdout.printf(" - subjects: %i\n", i); + stdout.printf(" - uri: %s\n", subject.get_uri ()); + stdout.printf(" - interpretation: %s\n", subject.get_interpretation ()); + stdout.printf(" - manifestation: %s\n", subject.get_manifestation ()); + stdout.printf(" - mimetype: %s\n", subject.get_mimetype ()); + stdout.printf(" - origin: %s\n", subject.get_origin ()); + stdout.printf(" - text: %s\n", subject.get_text ()); + stdout.printf(" - storage: %s\n", subject.get_storage ()); + } + if (event.get_payload() != null) + stdout.printf(" - payload:%s\n", (string) event.get_payload().data); + } + + private Event create_text_event (Account account, Channel channel) + { + var target = channel.get_target_contact (); + var obj_path = account.get_object_path (); + obj_path = this.tp_account_path.printf(obj_path[TelepathyGLib.ACCOUNT_OBJECT_PATH_BASE.length: + obj_path.length]); + Event event_template = new Event.full ( + ZG_ACCESS_EVENT, + ZG_USER_ACTIVITY, + this.actor, + null, + null); + event_template.set_origin (obj_path); + if (!channel.requested) + event_template.set_manifestation (ZG_WORLD_ACTIVITY); + // Create IM subject for the event + event_template.add_subject ( + new Subject.full ( + "", + NMO_IMMESSAGE, + NFO_SOFTWARE_SERVICE, + "plain/text", + this.tp_identifier.printf(target.get_identifier ()), + target.get_alias (), + "net") + ); + // Create Contact subject for the event + event_template.add_subject ( + new Subject.full ( + this.tp_identifier.printf(target.get_identifier ()), + NCO_CONTACT, + NCO_CONTACT_LIST_DATA_OBJECT, + "", + this.tp_identifier.printf(target.get_identifier ()), + target.get_alias (), + "net") + ); + return event_template; + } + + private void observe_text_channel (SimpleObserver observer, Account account, + Connection connection, Channel b_channel, + ChannelDispatchOperation? dispatch_operation, + List<ChannelRequest> requests, + ObserveChannelsContext context) + { + TextChannel channel = (TextChannel) b_channel; + var target = channel.get_target_contact (); + if (target != null) + { + var event_template = this.create_text_event (account, channel); + this.print_event (event_template); + foreach (var message in channel.get_pending_messages ()) + { + if (!message.is_delivery_report ()) + { + event_template = this.create_text_event (account, channel); + event_template.set_interpretation (ZG_RECEIVE_EVENT); + event_template.set_manifestation (ZG_WORLD_ACTIVITY); + this.print_event (event_template); + } + } + channel.invalidated.connect (() => { + event_template = this.create_text_event (account, channel); + event_template.set_interpretation (ZG_LEAVE_EVENT); + this.print_event (event_template); + }); + channel.message_received.connect (() => { + event_template = this.create_text_event (account, channel); + event_template.set_interpretation (ZG_RECEIVE_EVENT); + event_template.set_manifestation (ZG_WORLD_ACTIVITY); + this.print_event (event_template); + }); + channel.message_sent.connect (() => { + event_template = this.create_text_event (account, channel); + event_template.set_interpretation (ZG_SEND_EVENT); + event_template.set_manifestation (ZG_USER_ACTIVITY); + this.print_event (event_template); + }); + } + } + + private Event? create_call_event (Account account, CallChannel channel) + { + var targets = channel.get_members (); + if (targets == null) + { + return null; + } + var obj_path = account.get_object_path (); + obj_path = this.tp_account_path.printf(obj_path [TelepathyGLib.ACCOUNT_OBJECT_PATH_BASE.length: + obj_path.length]); + Event event_template = new Event.full ( + ZG_ACCESS_EVENT, + ZG_USER_ACTIVITY, + this.actor, + null, + obj_path); + if (!channel.requested) + event_template.set_manifestation (ZG_WORLD_ACTIVITY); + var i = 0; + foreach (var target in targets.get_keys()) + { + if (i == 0) + { + event_template.add_subject ( + new Subject.full ( + "", + NFO_AUDIO, + NFO_MEDIA_STREAM, + "x-telepathy/call", + this.tp_identifier.printf (target.get_identifier ()), + target.get_alias (), + "net") + ); + } + event_template.add_subject ( + new Subject.full ( + this.tp_identifier.printf(target.get_identifier ()), + NCO_CONTACT, + NCO_CONTACT_LIST_DATA_OBJECT, + "", + this.tp_identifier.printf(target.get_identifier ()), + target.get_alias (), + "net") + ); + i++; + } + return event_template; + } + + private void observe_call_channel (SimpleObserver observer, Account account, + Connection connection, Channel b_channel, + ChannelDispatchOperation? dispatch_operation, + List<ChannelRequest> requests, + ObserveChannelsContext context) + { + stdout.printf ("======================= CALL CHANNEL =======================\n"); + CallChannel channel = (CallChannel) b_channel; + if (channel.state == TelepathyGLib.CallState.INITIALISED) + { + var event_template = this.create_call_event (account, channel); + event_template.set_interpretation (ZG_CREATE_EVENT); + if (channel.requested == false) + event_template.set_manifestation (ZG_WORLD_ACTIVITY); + Timer t = new Timer (); + call_timers.set (channel, (owned) t); + this.print_event (event_template); + } + channel.state_changed.connect (() => + { + CallFlags flags; + HashTable<weak void*,weak void*> details; + TelepathyGLib.CallStateReason reason; + CallState state = channel.get_state (out flags, out details, out reason); + stdout.printf("STATE CHANGED ===> %u\n", state); + if ((state == 5 || state ==6)) + { + var event_template = this.create_call_event (account, channel); + event_template.set_interpretation (ZG_CREATE_EVENT); + if (channel.requested == false) + event_template.set_manifestation (ZG_WORLD_ACTIVITY); + Timer t = new Timer (); + call_timers.insert (channel, (owned) t); + event_template = this.create_call_event (account, channel); + if (reason.actor != channel.connection.get_self_handle ()) + event_template.set_manifestation (ZG_WORLD_ACTIVITY); + + if (state == 5) + { + event_template.set_interpretation (ZG_ACCESS_EVENT); + call_timers.get(b_channel).start(); + } + + else if (state == 6) + { + event_template.set_interpretation (ZG_LEAVE_EVENT); + if (reason.reason == TelepathyGLib.CallStateChangeReason.REJECTED) + event_template.set_interpretation (ZG_DENY_EVENT); + else if (reason.reason == TelepathyGLib.CallStateChangeReason.NO_ANSWER) + event_template.set_interpretation (ZG_EXPIRE_EVENT); + var duration = call_timers.get(b_channel).elapsed (); + call_timers.lookup (b_channel).stop; + call_timers.remove (b_channel); + //TODO: Add payloads + } + this.print_event (event_template); + } + }); + } + + private void observe_ft_channel (SimpleObserver observer, Account account, + Connection connection, Channel b_channel, + ChannelDispatchOperation? dispatch_operation, + List<ChannelRequest> requests, + ObserveChannelsContext context) + { + FileTransferChannel channel = (FileTransferChannel) b_channel; + channel.notify["state"].connect (() => { + if (channel.state == 4 || channel.state == 5) + { + var target = channel.get_target_contact (); + var attr = "%s, %s, %s".printf (FileAttribute.STANDARD_DISPLAY_NAME, + FileAttribute.STANDARD_CONTENT_TYPE, FileAttribute.STANDARD_SIZE); + var info = channel.file.query_info (attr, 0, null); + var obj_path = account.get_object_path (); + obj_path = this.tp_account_path.printf("%s", + obj_path [TelepathyGLib.ACCOUNT_OBJECT_PATH_BASE.length: + obj_path.length]); + var event_template = new Event (); + if (channel.requested) + { + event_template.set_interpretation (ZG_SEND_EVENT); + event_template.set_manifestation (ZG_USER_ACTIVITY); + } + else + { + event_template.set_interpretation (ZG_RECEIVE_EVENT); + event_template.set_manifestation (ZG_WORLD_ACTIVITY); + } + event_template.set_actor (this.actor); + event_template.set_origin (obj_path); + //============================================// + var subj = new Subject (); + subj.set_uri (channel.file.get_uri ()); + subj.set_interpretation (interpretation_for_mimetype (info.get_content_type ())); + subj.set_manifestation (NFO_FILE_DATA_OBJECT); + subj.set_text (info.get_display_name ()); + subj.set_mimetype (info.get_content_type ()); + //TODO: create if else + subj.set_origin (this.tp_identifier.printf (target.get_identifier ())); + event_template.add_subject (subj); + //===========================================// + event_template.add_subject ( + new Subject.full (this.tp_identifier.printf(target.get_identifier ()), + NCO_CONTACT, + NCO_CONTACT_LIST_DATA_OBJECT, + "", + this.tp_identifier.printf(target.get_identifier ()), + target.get_alias (), + "net")); + //TODO: Add payloads + var gen = new Generator(); + var root = new Json.Node(NodeType.OBJECT); + var object = new Json.Object(); + root.set_object(object); + gen.set_root(root); + + var details_obj = new Json.Object (); + TelepathyGLib.FileTransferStateChangeReason reason; + var state = channel.get_state (out reason); + details_obj.set_int_member ("state", state); + details_obj.set_int_member ("reason", reason); + details_obj.set_boolean_member ("requested", channel.requested); + if (channel.requested == true) + { + details_obj.set_string_member ("sender", obj_path); + details_obj.set_string_member ("receiver", this.tp_identifier.printf(target.get_identifier ())); + } + else + { + details_obj.set_string_member ("sender", this.tp_identifier.printf(target.get_identifier ())); + details_obj.set_string_member ("receiver", obj_path); + } + details_obj.set_string_member ("mimetype", info.get_content_type ()); + details_obj.set_int_member ("date", channel.get_date ().to_unix ()); + details_obj.set_string_member ("description", channel.get_description ()); + details_obj.set_double_member ("size", (int64)channel.get_size ()); + details_obj.set_string_member ("service", channel.get_service_name ()); + details_obj.set_string_member ("uri", channel.file.get_uri()); + size_t length; + object.set_object_member (ft_json_domain, details_obj); + string payload_string = gen.to_data(out length); + event_template.set_payload (new GLib.ByteArray.take (payload_string.data)); + this.print_event (event_template); + } + }); + } + + private void observe_channels (SimpleObserver observer, Account account, + Connection connection, List<Channel> channels, + ChannelDispatchOperation? dispatch_operation, + List<ChannelRequest> requests, + ObserveChannelsContext context) + { + try + { + foreach (var channel in channels) + { + if (channel is TelepathyGLib.TextChannel) + this.observe_text_channel (observer, account, connection, channel, + dispatch_operation, requests, context); + else if (channel is TelepathyGLib.CallChannel) + this.observe_call_channel (observer, account, connection, channel, + dispatch_operation, requests, context); + else if (channel is TelepathyGLib.FileTransferChannel) + this.observe_ft_channel (observer, account, connection, channel, + dispatch_operation, requests, context); + } + } + finally + { + context.accept (); + } + } + + public override void start () + { + stdout.printf("=== START ===\n"); + observer = new TelepathyGLib.SimpleObserver.with_factory (factory, + true, + "Zeitgeist", + false, + observe_channels); + + // Add Call Channel Filters + HashTable<string,Value?> call_filter = new HashTable<string,Value?> (str_hash, str_equal); + call_filter.insert (TelepathyGLib.PROP_CHANNEL_CHANNEL_TYPE, + TelepathyGLib.IFACE_CHANNEL_TYPE_CALL); + call_filter.insert (TelepathyGLib.PROP_CHANNEL_TARGET_HANDLE_TYPE, 1); //FIXME: use proper Telepathy constant + observer.add_observer_filter (call_filter); + + // Add Text Channel Filters + HashTable<string,Value?> text_filter = new HashTable<string,Value?> (str_hash, str_equal); + text_filter.insert (TelepathyGLib.PROP_CHANNEL_CHANNEL_TYPE, + TelepathyGLib.IFACE_CHANNEL_TYPE_TEXT); + text_filter.insert (TelepathyGLib.PROP_CHANNEL_TARGET_HANDLE_TYPE, 1); //FIXME: use proper Telepathy constant + observer.add_observer_filter (text_filter); + + // Add FileTransfer Channel Filters + HashTable<string,Value?> ft_filter = new HashTable<string,Value?> (str_hash, str_equal); + ft_filter.insert (TelepathyGLib.PROP_CHANNEL_CHANNEL_TYPE, + TelepathyGLib.IFACE_CHANNEL_TYPE_FILE_TRANSFER); + ft_filter.insert (TelepathyGLib.PROP_CHANNEL_TARGET_HANDLE_TYPE, 1); //FIXME: use proper Telepathy constant + observer.add_observer_filter (ft_filter); + + observer.register (); + } + + public override void stop () + { + observer.unregister (); + } +} diff --git a/src/zeitgeist-datahub.vala b/src/zeitgeist-datahub.vala index dea323b..a336cbb 100644 --- a/src/zeitgeist-datahub.vala +++ b/src/zeitgeist-datahub.vala @@ -111,6 +111,7 @@ public class DataHub : Object, DataHubService */ providers.prepend (new RecentManagerGtk (this)); providers.prepend (new RecentDocumentsKDE (this)); + providers.prepend (new TelepathyObserver (this)); if (Config.DOWNLOADS_MONITOR_ENABLED) providers.prepend (new DownloadsDirectoryMonitor (this)); |