diff --git a/src/common/config.py b/src/common/config.py index ae2f74373..94078dffc 100644 --- a/src/common/config.py +++ b/src/common/config.py @@ -350,6 +350,7 @@ class Config: 'answer_receipts' : [opt_bool, True, _('Answer to receipt requests')], 'request_receipt' : [opt_bool, True, _('Sent receipt requests')], 'publish_tune': [opt_bool, False], + 'publish_location': [opt_bool, False], 'subscribe_mood': [opt_bool, True], 'subscribe_activity': [opt_bool, True], 'subscribe_tune': [opt_bool, True], diff --git a/src/common/connection.py b/src/common/connection.py index 0d370d8b8..e3a5b7b4f 100644 --- a/src/common/connection.py +++ b/src/common/connection.py @@ -653,8 +653,8 @@ class Connection(CommonConnection, ConnectionHandlers): self.last_history_time = {} self.password = passwords.get_password(name) - # Used to ask privacy only once at connection self.music_track_info = 0 + self.location_info = {} self.pubsub_supported = False self.pubsub_publish_options_supported = False # Do we auto accept insecure connection diff --git a/src/common/helpers.py b/src/common/helpers.py index cb1dc9f24..2636fbdfe 100644 --- a/src/common/helpers.py +++ b/src/common/helpers.py @@ -1290,6 +1290,8 @@ def update_optional_features(account = None): gajim.gajim_optional_features[a].append(xmpp.NS_ACTIVITY + '+notify') if gajim.config.get_per('accounts', a, 'publish_tune'): gajim.gajim_optional_features[a].append(xmpp.NS_TUNE) + if gajim.config.get_per('accounts', a, 'publish_location'): + gajim.gajim_optional_features[a].append(xmpp.NS_LOCATION) if gajim.config.get_per('accounts', a, 'subscribe_tune'): gajim.gajim_optional_features[a].append(xmpp.NS_TUNE + '+notify') if gajim.config.get_per('accounts', a, 'subscribe_nick'): diff --git a/src/common/location_listener.py b/src/common/location_listener.py new file mode 100644 index 000000000..a7e06c0f4 --- /dev/null +++ b/src/common/location_listener.py @@ -0,0 +1,116 @@ +# -*- coding: utf-8 -*- +## src/common/location_listener.py +## +## Copyright (C) 2009 Yann Leboulanger +## +## This file is part of Gajim. +## +## Gajim 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; version 3 only. +## +## Gajim 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 Gajim. If not, see . +## + +from common import gajim +from common import pep +from common import dbus_support +if dbus_support.supported: + import dbus + import dbus.glib + +class LocationListener: + _instance = None + @classmethod + def get(cls): + if cls._instance is None: + cls._instance = cls() + return cls._instance + + def __init__(self): + self._data = {} + + def get_data(self): + self._get_address() + self._get_position() + + def _get_address(self): + bus = dbus.SessionBus() + if 'org.freedesktop.Geoclue.Master' not in bus.list_names(): + self._on_geoclue_address_changed(0, {}, 0) + return + obj = bus.get_object('org.freedesktop.Geoclue.Master', + '/org/freedesktop/Geoclue/Master') + # get MasterClient path + path = obj.Create() + # get MasterClient + cli = bus.get_object('org.freedesktop.Geoclue.Master', path) + cli.AddressStart() + # Check that there is a provider + name, description, service, path = cli.GetAddressProvider() + if path: + timestamp, address, accuracy = cli.GetAddress() + self._on_geoclue_address_changed(timestamp, address, accuracy) + + def _get_position(self): + bus = dbus.SessionBus() + if 'org.freedesktop.Geoclue.Master' not in bus.list_names(): + self._on_geoclue_position_changed([], 0, None, None, 0) + return + obj = bus.get_object('org.freedesktop.Geoclue.Master', + '/org/freedesktop/Geoclue/Master') + # get MasterClient path + path = obj.Create() + # get MasterClient + cli = bus.get_object('org.freedesktop.Geoclue.Master', path) + cli.PositionStart() + # Check that there is a provider + name, description, service, path = cli.GetPositionProvider() + if path: + fields, timestamp, lat, lon, accuray = cli.GetPosition() + self._on_geoclue_position_changed(fields, timestamp, lat, lon, + accuracy) + + def start(self): + bus = dbus.SessionBus() + # Geoclue + bus.add_signal_receiver(self._on_geoclue_address_changed, + 'AddressChanged', 'org.freedesktop.Geoclue.Address') + bus.add_signal_receiver(self._on_geoclue_address_changed, + 'PositionChanged', 'org.freedesktop.Geoclue.Position') + + def shut_down(self): + pass + + def _on_geoclue_address_changed(self, timestamp, address, accuracy): + # update data with info we just received + for field in pep.LOCATION_DATA: + self._data[field] = address.get(field, self._data.get(field, None)) + self._send_location() + + def _on_geoclue_position_changed(self, fields, timestamp, lat, lon, + accuracy): + # update data with info we just received + if lat: + self._data['lat'] = lat + if lon: + self._data['lon'] = lon + self._send_location() + + def _send_location(self): + accounts = gajim.connections.keys() + for acct in accounts: + if not gajim.account_is_connected(acct): + continue + if not gajim.config.get_per('accounts', acct, 'publish_location'): + continue + if gajim.connections[acct].location_info == self._data: + continue + gajim.connections[acct].send_location(self._data) + gajim.connections[acct].location_info = self._data diff --git a/src/common/pep.py b/src/common/pep.py index 2a4a064b0..929bf3ab4 100644 --- a/src/common/pep.py +++ b/src/common/pep.py @@ -468,6 +468,11 @@ class UserLocationPEP(AbstractPEP): retracted = items.getTag('retract') or not location_dict return (location_dict, retracted) + def _update_account(self, account): + AbstractPEP._update_account(self, account) + con = gajim.connections[account].location_info = \ + self._pep_specific_data + def asPixbufIcon(self): path = gtkgui_helpers.get_icon_path('gajim-earth') return gtk.gdk.pixbuf_new_from_file(path) @@ -606,4 +611,19 @@ class ConnectionPEP(object): self.send_nickname(None) self._pubsub_connection.send_pb_retract('', xmpp.NS_NICK, '0') + def send_location(self, info): + if not self.pep_supported: + return + item = xmpp.Node('geoloc', {'xmlns': xmpp.NS_LOCATION}) + for field in LOCATION_DATA: + if info.get(field, None): + i = item.addChild(field) + i.addData(info[field]) + self._pubsub_connection.send_pb_publish('', xmpp.NS_LOCATION, item, '0') + + def retract_location(self): + if not self.pep_supported: + return + self._pubsub_connection.send_pb_retract('', xmpp.NS_LOCATION, '0') + # vim: se ts=3: diff --git a/src/gui_interface.py b/src/gui_interface.py index 337e53924..b6e47f151 100644 --- a/src/gui_interface.py +++ b/src/gui_interface.py @@ -50,6 +50,7 @@ from common import gajim from common import dbus_support if dbus_support.supported: from music_track_listener import MusicTrackListener + from common.location_listener import LocationListener import dbus import gtkgui_helpers @@ -1547,6 +1548,10 @@ class Interface: if gajim.connections[account].pep_supported and dbus_support.supported \ and gajim.config.get_per('accounts', account, 'publish_tune'): self.enable_music_listener() + # enable location listener + if gajim.connections[account].pep_supported and dbus_support.supported \ + and gajim.config.get_per('accounts', account, 'publish_location'): + self.enable_location_listener() def handle_event_metacontacts(self, account, tags_list): gajim.contacts.define_metacontacts(account, tags_list) @@ -2785,6 +2790,15 @@ class Interface: listener.disconnect(self.music_track_changed_signal) self.music_track_changed_signal = None + def enable_location_listener(self): + listener = LocationListener.get() + listener.get_data() + listener.start() + + def disable_location_listener(self): + listener = LocationListener.get() + listener.shut_down() + def music_track_changed(self, unused_listener, music_track_info, account=None): if not account: accounts = gajim.connections.keys() diff --git a/src/roster_window.py b/src/roster_window.py index cedf24d67..71c80e333 100644 --- a/src/roster_window.py +++ b/src/roster_window.py @@ -3433,6 +3433,22 @@ class RosterWindow: helpers.update_optional_features(account) + def on_publish_location_toggled(self, widget, account): + active = widget.get_active() + gajim.config.set_per('accounts', account, 'publish_location', active) + if active: + gajim.interface.enable_location_listener() + else: + gajim.connections[account].retract_location() + # disable music listener only if no other account uses it + for acc in gajim.connections: + if gajim.config.get_per('accounts', acc, 'publish_location'): + break + else: + gajim.interface.disable_location_listener() + + helpers.update_optional_features(account) + def on_pep_services_menuitem_activate(self, widget, account): if 'pep_services' in gajim.interface.instances[account]: gajim.interface.instances[account]['pep_services'].window.present() @@ -4993,6 +5009,8 @@ class RosterWindow: if gajim.connections[account].pep_supported: have_tune = gajim.config.get_per('accounts', account, 'publish_tune') + have_location = gajim.config.get_per('accounts', account, + 'publish_location') pep_submenu = gtk.Menu() pep_menuitem.set_submenu(pep_submenu) item = gtk.CheckMenuItem(_('Publish Tune')) @@ -5003,6 +5021,15 @@ class RosterWindow: item.set_active(have_tune) item.connect('toggled', self.on_publish_tune_toggled, account) + item = gtk.CheckMenuItem(_('Publish Location')) + pep_submenu.append(item) + if not dbus_support.supported: + item.set_sensitive(False) + else: + item.set_active(have_location) + item.connect('toggled', self.on_publish_location_toggled, + account) + pep_config = gtk.ImageMenuItem(_('Configure Services...')) item = gtk.SeparatorMenuItem() pep_submenu.append(item)