Commit fb8a394e authored by Philipp Hörist's avatar Philipp Hörist
Browse files

Drop Zeroconf support

- Feature is broken since mid 2020
- No demand since then to fix it
parent 580c5eaa
......@@ -21,7 +21,6 @@ ### Runtime Requirements
### Optional Runtime Requirements
- python3-pil (pillow) for support of webp avatars
- gir1.2-avahi-0.6 for zeroconf on Linux or [pybonjour](https://dev.gajim.org/lovetox/pybonjour-python3) on Windows/macOS
- gir1.2-gspell-1 and hunspell-LANG where lang is your locale eg. en, fr etc
- gir1.2-secret-1 for GNOME Keyring or KDE support as password storage
- D-Bus running to have gajim-remote working
......
......@@ -30,7 +30,6 @@
<li>Support for multiple accounts</li>
<li>Group multiple contacts from one friend to a single Meta-Contact</li>
<li>XML console to see what's happening on the protocol layer</li>
<li>Serverless messaging (Bonjour/Zeroconf)</li>
<li>Support for service discovery including nodes and search for users</li>
<li>Even more features via plugins</li>
</ul>
......
......@@ -4,7 +4,7 @@ Name=Gajim
GenericName=XMPP Chat Client
Comment=A fully-featured XMPP chat client
#Translators: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon!
Keywords=chat;messaging;im;xmpp;bonjour;voip;
Keywords=chat;messaging;im;xmpp;voip;
Exec=gajim %u
#Translators: Do NOT translate or transliterate this text (this is an icon file name)!
Icon=org.gajim.Gajim
......
......@@ -196,9 +196,6 @@ def clear(self):
@command
@doc(_("Send a ping to the contact"))
def ping(self):
if self.account == app.ZEROCONF_ACC_NAME:
raise CommandError(
_('Command is not supported for zeroconf accounts'))
app.connections[self.account].get_module('Ping').send_ping(self.contact)
@command
......@@ -451,10 +448,6 @@ def unblock(self, who):
@command
@doc(_("Send a ping to the contact"))
def ping(self, nick):
if self.account == app.ZEROCONF_ACC_NAME:
raise CommandError(
_('Command is not supported for zeroconf accounts'))
client = app.get_client(self.account)
groupchat_contact = client.get_module('Contacts').get_contact(
self.room_jid, groupchat=True)
......
......@@ -117,9 +117,6 @@ def __init__(self):
task_manager = None
# zeroconf account name
ZEROCONF_ACC_NAME = 'Local'
# These will be set in app.gui_interface.
idlequeue = cast(IdleQueue, None)
socks5queue = None
......@@ -129,8 +126,6 @@ def __init__(self):
gsound_ctx = None
_dependencies = {
'AVAHI': False,
'PYBONJOUR': False,
'FARSTREAM': False,
'GST': False,
'AV': False,
......@@ -161,9 +156,6 @@ def get_client(account: str) -> types.Client:
def is_installed(dependency: str) -> bool:
if dependency == 'ZEROCONF':
# Alias for checking zeroconf libs
return _dependencies['AVAHI'] or _dependencies['PYBONJOUR']
return _dependencies[dependency]
......@@ -195,20 +187,6 @@ def disable_dependency(dependency: str) -> None:
def detect_dependencies() -> None:
import gi
# ZEROCONF
try:
import pybonjour # pylint: disable=unused-import
_dependencies['PYBONJOUR'] = True
except Exception:
pass
try:
gi.require_version('Avahi', '0.6')
from gi.repository import Avahi # pylint: disable=unused-import
_dependencies['AVAHI'] = True
except Exception:
pass
try:
gi.require_version('Gst', '1.0')
gi.require_version('GstPbutils', '1.0')
......@@ -397,14 +375,10 @@ def get_accounts_sorted() -> list[str]:
'''
account_list = settings.get_accounts()
account_list.sort(key=str.lower)
if 'Local' in account_list:
account_list.remove('Local')
account_list.insert(0, 'Local')
return account_list
def get_enabled_accounts_with_labels(
exclude_local: bool = True,
connected_only: bool = False,
private_storage_only: bool = False) -> list[list[str]]:
"""
......@@ -413,8 +387,6 @@ def get_enabled_accounts_with_labels(
"""
accounts: list[list[str]] = []
for acc in connections:
if exclude_local and account_is_zeroconf(acc):
continue
if connected_only and not account_is_connected(acc):
continue
if private_storage_only and not account_supports_private_storage(acc):
......@@ -430,10 +402,6 @@ def get_account_label(account: str) -> str:
return settings.get_account_setting(account, 'account_label') or account
def account_is_zeroconf(account: str) -> bool:
return connections[account].is_zeroconf
def account_supports_private_storage(account: str) -> bool:
# If Delimiter module is not available we can assume
# Private Storage is not available
......@@ -457,11 +425,6 @@ def account_is_disconnected(account: str) -> bool:
return not account_is_connected(account)
def zeroconf_is_connected() -> bool:
return account_is_connected(ZEROCONF_ACC_NAME) and \
settings.get_account_setting(ZEROCONF_ACC_NAME, 'is_zeroconf')
def get_transport_name_from_jid(
jid: str,
use_config_setting: bool = True) -> Optional[str]:
......
......@@ -81,7 +81,6 @@ def __init__(self, account: str) -> None:
self._connect_machine_calls = 0
self.addressing_supported = False
self.is_zeroconf = False
self.pep = {}
self.roster_supported = True
......
......@@ -582,13 +582,6 @@ class SecCatalogReceived(ApplicationEvent):
catalog: dict[str, Any]
@dataclass
class RawPresenceReceived(ApplicationEvent):
name: str = field(init=False, default='raw-pres-received')
conn: 'Client'
stanza: Any
@dataclass
class PresenceReceived(ApplicationEvent):
name: str = field(init=False, default='presence-received')
......
......@@ -114,19 +114,16 @@ def pass_disco(self, info):
default=self._account, testit=testit)
raise nbxmpp.NodeProcessed
def _ft_get_receiver_jid(self, file_props):
if self._account == 'Local':
return file_props.receiver.jid
@staticmethod
def _ft_get_receiver_jid(file_props):
return file_props.receiver.jid + '/' + file_props.receiver.resource
def _ft_get_from(self, iq_obj):
if self._account == 'Local':
return iq_obj.getFrom()
@staticmethod
def _ft_get_from(iq_obj):
return helpers.get_full_jid_from_iq(iq_obj)
def _ft_get_streamhost_jid_attr(self, streamhost):
if self._account == 'Local':
return streamhost.getAttr('jid')
@staticmethod
def _ft_get_streamhost_jid_attr(streamhost):
return helpers.parse_jid(streamhost.getAttr('jid'))
def send_file_approval(self, file_props):
......
......@@ -24,7 +24,6 @@
from gajim.common import app
from gajim.common import idle
from gajim.common.events import PresenceReceived
from gajim.common.events import RawPresenceReceived
from gajim.common.events import ShowChanged
from gajim.common.events import SubscribePresenceReceived
from gajim.common.events import SubscribedPresenceReceived
......@@ -101,12 +100,6 @@ def _presence_received(self, _con, stanza, properties):
self._log.info('Error: %s %s', properties.jid, properties.error)
return
if self._account == 'Local':
app.ged.raise_event(
RawPresenceReceived(conn=self._con,
stanza=stanza))
return
if properties.is_self_presence:
app.ged.raise_event(ShowChanged(account=self._account,
show=properties.show.value))
......
......@@ -310,6 +310,9 @@ def _migrate_old_config(self) -> None:
group_chat_settings)
for account, settings in account_settings.items():
if account == 'Local':
# Zeroconf support was dropped so don’t migrate the account
continue
self.add_account(account)
self._account_settings[account]['account'] = settings
self._account_settings[account]['contact'] = contact_settings
......
......@@ -35,7 +35,6 @@
# pylint: disable=unused-import
from gajim.common.client import Client
from nbxmpp.client import Client as xmppClient
from gajim.common.zeroconf.connection_zeroconf import ConnectionZeroconf
from gajim.common.modules.contacts import CommonContact
from gajim.common.modules.contacts import BareContact
from gajim.common.modules.contacts import ResourceContact
......@@ -49,7 +48,7 @@
InterfaceT = Union['Interface']
ConnectionT = Union['Client', 'ConnectionZeroconf']
ConnectionT = Union['Client']
CSSConfigT = Union['CSSConfig']
# PEP
......
This diff is collapsed.
# Contributors for this file:
# - Yann Leboulanger <asterix@lagaule.org>
# - Nikos Kouremenos <nkour@jabber.org>
# - Dimitur Kirov <dkirov@gmail.com>
# - Travis Shirk <travis@pobox.com>
# - Stefan Bethge <stefan@lanpartei.de>
#
# 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 <http://www.gnu.org/licenses/>.
import time
import logging
from gajim.common import app
from gajim.common.helpers import AdditionalDataDict
from gajim.common.modules.util import get_eme_message
from gajim.common.modules.misc import parse_correction
from gajim.common.modules.misc import parse_oob
from gajim.common.modules.misc import parse_xhtml
log = logging.getLogger('gajim.c.z.connection_handlers_zeroconf')
class NetworkEvent:
pass
class ConnectionHandlersZeroconf:
def _messageCB(self, con, stanza, properties):
"""
Called when we receive a message
"""
if properties.type.is_error:
return
log.info('Zeroconf MessageCB')
# Don’t trust from attr set by sender
stanza.setFrom(con._owner.to)
app.ged.raise_event(NetworkEvent(
'raw-message-received',
conn=self,
stanza=stanza,
account=self.name))
type_ = stanza.getType()
if type_ is None:
type_ = 'normal'
id_ = stanza.getID()
fjid = str(stanza.getFrom())
jid, resource = app.get_room_and_nick_from_fjid(fjid)
msgtxt = stanza.getBody()
session = self.get_or_create_session(fjid, properties.thread)
if properties.thread and not session.received_thread_id:
session.received_thread_id = True
timestamp = time.time()
session.last_receive = timestamp
additional_data = AdditionalDataDict()
parse_oob(properties, additional_data)
parse_xhtml(properties, additional_data)
if properties.is_encrypted:
additional_data['encrypted'] = properties.encrypted.additional_data
else:
if properties.eme is not None:
msgtxt = get_eme_message(properties.eme)
event_attr = {
'conn': self,
'stanza': stanza,
'account': self.name,
'additional_data': additional_data,
'timestamp': time.time(),
'fjid': fjid,
'jid': jid,
'resource': resource,
'unique_id': id_,
'correct_id': parse_correction(properties),
'msgtxt': msgtxt,
'session': session,
'gc_control': None,
'popup': False,
'msg_log_id': None,
'displaymarking': None,
'stanza_id': id_,
'properties': properties,
}
app.ged.raise_event(
NetworkEvent('decrypted-message-received', **event_attr))
def _message_error_received(self, _con, _stanza, properties):
log.info(properties.error)
app.storage.archive.set_message_error(app.get_jid_from_account(self.name),
properties.jid,
properties.id,
properties.error)
app.ged.raise_event(
NetworkEvent('message-error',
account=self.name,
jid=properties.jid,
message_id=properties.id,
error=properties.error))
This diff is collapsed.
# Copyright (C) 2006 Stefan Bethge <stefan@lanpartei.de>
#
# 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 <http://www.gnu.org/licenses/>.
from gajim.common.zeroconf.zeroconf import Constant, ConstantRI
class Roster:
def __init__(self, zeroconf):
self._data = None
self.zeroconf = zeroconf # our zeroconf instance
self.version = ''
def update_roster(self):
for val in self.zeroconf.get_contacts().values():
self.setItem(val[Constant.NAME])
def getRoster(self):
if self._data is None:
self._data = {}
self.update_roster()
return self
def getDiffs(self):
"""
Update the roster with new data and return dict with jid -> new status
pairs to do notifications and stuff
"""
diffs = {}
old_data = self._data.copy()
self.update_roster()
for key in old_data.keys():
if key in self._data:
if old_data[key] != self._data[key]:
diffs[key] = self._data[key]['status']
return diffs
def setItem(self, jid, name='', groups=''):
contact = self.zeroconf.get_contact(jid)
if not contact:
return
addresses = []
i = 0
for ri in contact[Constant.RESOLVED_INFO]:
addresses += [{}]
addresses[i]['host'] = ri[ConstantRI.HOST]
addresses[i]['address'] = ri[ConstantRI.ADDRESS]
addresses[i]['port'] = ri[ConstantRI.PORT]
i += 1
txt = contact[Constant.TXT]
self._data[jid] = {}
self._data[jid]['ask'] = 'none'
self._data[jid]['subscription'] = 'both'
self._data[jid]['groups'] = []
self._data[jid]['resources'] = {}
self._data[jid]['addresses'] = addresses
txt_dict = self.zeroconf.txt_array_to_dict(txt)
status = txt_dict.get('status', '')
if not status:
status = 'avail'
nm = txt_dict.get('1st', '')
if 'last' in txt_dict:
if nm != '':
nm += ' '
nm += txt_dict['last']
if nm:
self._data[jid]['name'] = nm
else:
self._data[jid]['name'] = jid
if status == 'avail':
status = 'online'
self._data[jid]['txt_dict'] = txt_dict
if 'msg' not in self._data[jid]['txt_dict']:
self._data[jid]['txt_dict']['msg'] = ''
self._data[jid]['status'] = status
self._data[jid]['show'] = status
def setItemMulti(self, items):
for i in items:
self.setItem(jid=i['jid'], name=i['name'], groups=i['groups'])
def delItem(self, jid):
if jid in self._data:
del self._data[jid]
def getItem(self, jid):
if jid in self._data:
return self._data[jid]
def __getitem__(self, jid):
return self._data[jid]
def __setitem__(self, jid, value):
self._data[jid] = value
def getItems(self):
# Return list of all [bare] JIDs that the roster currently tracks.
return self._data.keys()
def keys(self):
return self._data.keys()
def getRaw(self):
return self._data
def getResources(self, jid):
return {}
def getGroups(self, jid):
return self._data[jid]['groups']
def getName(self, jid):
if jid in self._data:
return self._data[jid]['name']
def getStatus(self, jid):
if jid in self._data:
return self._data[jid]['status']
def getMessage(self, jid):
if jid in self._data:
return self._data[jid]['txt_dict']['msg']
def getShow(self, jid):
return self.getStatus(jid)
def getPriority(self, jid):
return 5
def getSubscription(self, jid):
return 'both'
def Subscribe(self, jid):
pass
def Unsubscribe(self, jid):
pass
def Authorize(self, jid):
pass
def Unauthorize(self, jid):
pass
def copy(self):
return self._data.copy()
# Copyright (C) 2006 Stefan Bethge <stefan@lanpartei.de>
#
# 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 <http://www.gnu.org/licenses/>.
from typing import Any # pylint: disable=unused-import
from enum import IntEnum, unique
@unique
class Constant(IntEnum):
NAME = 0
DOMAIN = 1
RESOLVED_INFO = 2
BARE_NAME = 3
TXT = 4
@unique
class ConstantRI(IntEnum):
INTERFACE = 0
PROTOCOL = 1
HOST = 2
APROTOCOL = 3
ADDRESS = 4
PORT = 5
def test_avahi():
try:
import gi
gi.require_version('Avahi', '0.6')
from gi.repository import Avahi # pylint: disable=unused-import
except (ImportError, ValueError):
return False
return True
def test_bonjour():
try:
import pybonjour # pylint: disable=unused-import
except Exception:
return False
return True
def test_zeroconf():
return test_avahi() or test_bonjour()
if test_avahi():
from gajim.common.zeroconf import zeroconf_avahi
Zeroconf = zeroconf_avahi.Zeroconf # type: Any
elif test_bonjour():
from gajim.common.zeroconf import zeroconf_bonjour
Zeroconf = zeroconf_bonjour.Zeroconf
This diff is collapsed.
# Copyright (C) 2018 Philipp Hörist <philipp AT hoerist.com>
#
# This file is part of Gajim.
#
# Gajim is free software; you can redistribute it and/or modify