Welcome to mirror list, hosted at ThFree Co, Russian Federation.

dev.gajim.org/gajim/gajim.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md1
-rw-r--r--data/org.gajim.Gajim.appdata.xml.in1
-rw-r--r--data/org.gajim.Gajim.desktop.in2
-rw-r--r--gajim/command_system/implementation/standard.py7
-rw-r--r--gajim/common/app.py37
-rw-r--r--gajim/common/client.py1
-rw-r--r--gajim/common/events.py7
-rw-r--r--gajim/common/modules/bytestream.py15
-rw-r--r--gajim/common/modules/presence.py7
-rw-r--r--gajim/common/settings.py3
-rw-r--r--gajim/common/types.py3
-rw-r--r--gajim/common/zeroconf/__init__.py0
-rw-r--r--gajim/common/zeroconf/client_zeroconf.py857
-rw-r--r--gajim/common/zeroconf/connection_handlers_zeroconf.py125
-rw-r--r--gajim/common/zeroconf/connection_zeroconf.py603
-rw-r--r--gajim/common/zeroconf/roster_zeroconf.py160
-rw-r--r--gajim/common/zeroconf/zeroconf.py63
-rw-r--r--gajim/common/zeroconf/zeroconf_avahi.py552
-rw-r--r--gajim/common/zeroconf/zeroconf_avahi_const.py58
-rw-r--r--gajim/common/zeroconf/zeroconf_bonjour.py458
-rw-r--r--gajim/data/gui/zeroconf_information_window.ui398
-rw-r--r--gajim/gtk/accounts.py121
-rw-r--r--gajim/gtk/adhoc_muc.py4
-rw-r--r--gajim/gtk/application.py3
-rw-r--r--gajim/gtk/builder.pyi36
-rw-r--r--gajim/gtk/controls/chat.py9
-rw-r--r--gajim/gtk/features.py8
-rw-r--r--gajim/gtk/main.py4
-rw-r--r--gajim/gtk/menus.py8
-rw-r--r--gajim/gtk/status_icon.py3
-rw-r--r--gajim/gtk/zeroconf_vcard.py111
-rw-r--r--gajim/gui_interface.py83
-rw-r--r--gajim/plugins/pluginmanager.py4
-rw-r--r--gajim/remote_control.py9
-rw-r--r--mypy.ini3
-rw-r--r--win/_base.sh1
-rw-r--r--win/dev_env.sh1
37 files changed, 40 insertions, 3726 deletions
diff --git a/README.md b/README.md
index 6cf8c273f..c1982d828 100644
--- a/README.md
+++ b/README.md
@@ -21,7 +21,6 @@
### 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
diff --git a/data/org.gajim.Gajim.appdata.xml.in b/data/org.gajim.Gajim.appdata.xml.in
index 7e9cd255e..480415620 100644
--- a/data/org.gajim.Gajim.appdata.xml.in
+++ b/data/org.gajim.Gajim.appdata.xml.in
@@ -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>
diff --git a/data/org.gajim.Gajim.desktop.in b/data/org.gajim.Gajim.desktop.in
index c68cc657b..a718cf977 100644
--- a/data/org.gajim.Gajim.desktop.in
+++ b/data/org.gajim.Gajim.desktop.in
@@ -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
diff --git a/gajim/command_system/implementation/standard.py b/gajim/command_system/implementation/standard.py
index bd4c28dd3..e20064e45 100644
--- a/gajim/command_system/implementation/standard.py
+++ b/gajim/command_system/implementation/standard.py
@@ -196,9 +196,6 @@ class StandardCommonChatCommands(CommandContainer):
@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 @@ class StandardGroupChatCommands(CommandContainer):
@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)
diff --git a/gajim/common/app.py b/gajim/common/app.py
index 88a6e5c14..146c42cf0 100644
--- a/gajim/common/app.py
+++ b/gajim/common/app.py
@@ -117,9 +117,6 @@ cert_store = cast('CertificateStore', None)
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 @@ gupnp_igd = None
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]:
diff --git a/gajim/common/client.py b/gajim/common/client.py
index 5a484c963..1032c399c 100644
--- a/gajim/common/client.py
+++ b/gajim/common/client.py
@@ -81,7 +81,6 @@ class Client(Observable):
self._connect_machine_calls = 0
self.addressing_supported = False
- self.is_zeroconf = False
self.pep = {}
self.roster_supported = True
diff --git a/gajim/common/events.py b/gajim/common/events.py
index 56a09f273..be0d8a665 100644
--- a/gajim/common/events.py
+++ b/gajim/common/events.py
@@ -583,13 +583,6 @@ class SecCatalogReceived(ApplicationEvent):
@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')
account: str
diff --git a/gajim/common/modules/bytestream.py b/gajim/common/modules/bytestream.py
index a7ea908a4..09634c65b 100644
--- a/gajim/common/modules/bytestream.py
+++ b/gajim/common/modules/bytestream.py
@@ -114,19 +114,16 @@ class Bytestream(BaseModule):
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):
diff --git a/gajim/common/modules/presence.py b/gajim/common/modules/presence.py
index f23bc8f1f..1d8e97628 100644
--- a/gajim/common/modules/presence.py
+++ b/gajim/common/modules/presence.py
@@ -24,7 +24,6 @@ from nbxmpp.const import PresenceType
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 @@ class Presence(BaseModule):
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))
diff --git a/gajim/common/settings.py b/gajim/common/settings.py
index f2320d8e6..26eb0ddc7 100644
--- a/gajim/common/settings.py
+++ b/gajim/common/settings.py
@@ -310,6 +310,9 @@ class Settings:
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
diff --git a/gajim/common/types.py b/gajim/common/types.py
index 0f39629ea..6a10bef11 100644
--- a/gajim/common/types.py
+++ b/gajim/common/types.py
@@ -35,7 +35,6 @@ if TYPE_CHECKING:
# 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 @@ if TYPE_CHECKING:
InterfaceT = Union['Interface']
-ConnectionT = Union['Client', 'ConnectionZeroconf']
+ConnectionT = Union['Client']
CSSConfigT = Union['CSSConfig']
# PEP
diff --git a/gajim/common/zeroconf/__init__.py b/gajim/common/zeroconf/__init__.py
deleted file mode 100644
index e69de29bb..000000000
--- a/gajim/common/zeroconf/__init__.py
+++ /dev/null
diff --git a/gajim/common/zeroconf/client_zeroconf.py b/gajim/common/zeroconf/client_zeroconf.py
deleted file mode 100644
index 35231236d..000000000
--- a/gajim/common/zeroconf/client_zeroconf.py
+++ /dev/null
@@ -1,857 +0,0 @@
-# Copyright (C) 2006 Stefan Bethge <stefan@lanpartei.de>
-# 2006 Dimitur Kirov <dkirov@gmail.com>
-#
-# 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 socket
-import ssl
-import errno
-import sys
-import os
-import logging
-from unittest.mock import Mock
-
-import nbxmpp
-from nbxmpp import old_dispatcher as dispatcher
-from nbxmpp import simplexml
-from nbxmpp.namespaces import Namespace
-from nbxmpp.structs import StanzaHandler
-from nbxmpp.plugin import PlugIn
-from nbxmpp.idlequeue import IdleObject
-from nbxmpp.util import generate_id
-
-from gajim.common import app
-from gajim.common.zeroconf import zeroconf
-from gajim.common.zeroconf import roster_zeroconf
-
-log = logging.getLogger('gajim.c.z.client_zeroconf')
-
-
-MAX_BUFF_LEN = 65536
-TYPE_SERVER, TYPE_CLIENT = range(2)
-
-# wait XX sec to establish a connection
-CONNECT_TIMEOUT_SECONDS = 10
-
-# after XX sec with no activity, close the stream
-ACTIVITY_TIMEOUT_SECONDS = 30
-
-class ZeroconfListener(IdleObject):
- def __init__(self, port, conn_holder):
- """
- Handle all incoming connections on ('0.0.0.0', port)
- """
- self.port = port
- self.queue_idx = -1
- #~ self.queue = None
- self.started = False
- self._sock = None
- self.fd = -1
- self.caller = conn_holder.caller
- self.conn_holder = conn_holder
-
- def bind(self):
- flags = socket.AI_PASSIVE
- if hasattr(socket, 'AI_ADDRCONFIG'):
- flags |= socket.AI_ADDRCONFIG
- ai = socket.getaddrinfo(None, self.port, socket.AF_UNSPEC,
- socket.SOCK_STREAM, 0, flags)[0]
- self._serv = socket.socket(ai[0], ai[1])
- self._serv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
- self._serv.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
- self._serv.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
- if os.name == 'nt':
- if sys.getwindowsversion().major >= 6: # Win Vista +
- # 47 is socket.IPPROTO_IPV6
- # 27 is socket.IPV6_V6ONLY under windows, but not defined ...
- self._serv.setsockopt(41, 27, 0)
- # will fail when port is busy, or we don't have rights to bind
- try:
- self._serv.bind((ai[4][0], self.port))
- except Exception:
- # unable to bind, show error dialog
- return None
- self._serv.listen(socket.SOMAXCONN)
- self._serv.setblocking(False)
- self.fd = self._serv.fileno()
- app.idlequeue.plug_idle(self, False, True)
- self.started = True
-
- def pollend(self):
- """
- Called when we stop listening on (host, port)
- """
- self.disconnect()
-
- def pollin(self):
- """
- Accept a new incoming connection and notify queue
- """
- sock = self.accept_conn()
- # loop through roster to find who has connected to us
- from_jid = None
- ipaddr = sock[1][0]
- for jid in self.conn_holder.getRoster().keys():
- entry = self.conn_holder.getRoster().getItem(jid)
- for address in entry['addresses']:
- if address['address'] == ipaddr:
- from_jid = jid
- break
- P2PClient(sock[0], [{'host': ipaddr, 'address': ipaddr, 'port': sock[1][1]}], self.conn_holder, [], from_jid)
-
- def disconnect(self, message=''):
- """
- Free all resources, we are not listening anymore
- """
- log.info('Disconnecting ZeroconfListener: %s', message)
- app.idlequeue.remove_timeout(self.fd)
- app.idlequeue.unplug_idle(self.fd)
- self.fd = -1
- self.started = False
- try:
- self._serv.close()
- except socket.error:
- pass
- self.conn_holder.kill_all_connections()
-
- def accept_conn(self):
- """
- Accept a new incoming connection
- """
- _sock = self._serv.accept()
- _sock[0].setblocking(False)
- return _sock
-
-class P2PClient(IdleObject):
- def __init__(self, _sock, addresses, conn_holder, stanzaqueue, to=None,
- on_ok=None, on_not_ok=None):
- self._owner = self
- self.Namespace = 'jabber:client'
- self.protocol_type = 'XMPP'
- self.defaultNamespace = self.Namespace
- self.Smacks = Mock()
- self._component = 0
- self._registered_name = None
- self._caller = conn_holder.caller
- self.conn_holder = conn_holder
- self.stanzaqueue = stanzaqueue
- self.to = to
- #self.Server = addresses[0]['host']
- self.on_ok = on_ok
- self.on_not_ok = on_not_ok
- self.Connection = None
- self.sock_hash = None
- if _sock:
- self.sock_type = TYPE_SERVER
- else:
- self.sock_type = TYPE_CLIENT
- self.fd = -1
- conn = P2PConnection('', _sock, addresses, self._caller,
- self.on_connect, self)
- self.Server = conn.host # set Server to the last host name / address tried
- if not self.conn_holder:
- # An error occurred, disconnect() has been called
- if on_not_ok:
- on_not_ok('Connection to host could not be established.')
- return
- self.sock_hash = conn._sock.__hash__
- self.fd = conn.fd
- self.conn_holder.add_connection(self, self.Server, conn.port, self.to)
- # count messages in queue
- for val in self.stanzaqueue:
- stanza, is_message = val
- if is_message:
- if self.fd == -1:
- if on_not_ok:
- on_not_ok(
- 'Connection to host could not be established.')
- return
- thread_id = stanza.getThread()
- id_ = stanza.getID()
- if not id_:
- id_ = generate_id()
- if self.fd in self.conn_holder.ids_of_awaiting_messages:
- self.conn_holder.ids_of_awaiting_messages[self.fd].append((
- id_, thread_id))
- else:
- self.conn_holder.ids_of_awaiting_messages[self.fd] = [(id_,
- thread_id)]
-
- self.on_responses = {}
-
- def get_bound_jid(self):
- return self._caller.get_own_jid()
-
- def add_stanza(self, stanza, is_message=False):
- if self.Connection:
- if self.Connection.state == -1:
- return False
- self.send(stanza, is_message)
- else:
- self.stanzaqueue.append((stanza, is_message))
-
- if is_message:
- thread_id = stanza.getThread()
- id_ = stanza.getID()
- if not id_:
- id_ = generate_id()
- if self.fd in self.conn_holder.ids_of_awaiting_messages:
- self.conn_holder.ids_of_awaiting_messages[self.fd].append((id_,
- thread_id))
- else:
- self.conn_holder.ids_of_awaiting_messages[self.fd] = [(id_,
- thread_id)]
-
- return True
-
- def on_message_sent(self, connection_id):
- id_, _thread_id = \
- self.conn_holder.ids_of_awaiting_messages[connection_id].pop(0)
- if self.on_ok:
- self.on_ok(id_)
- # use on_ok only on first message. For others it's called in
- # ClientZeroconf
- self.on_ok = None
-
- def on_connect(self, conn):
- self.Connection = conn
- self.Connection.PlugIn(self)
- dispatcher.Dispatcher().PlugIn(self)
- self._register_handlers()
-
- def StreamInit(self):
- """
- Send an initial stream header
- """
- self.Dispatcher.Stream = simplexml.NodeBuilder()
- self.Dispatcher.Stream._dispatch_depth = 2
- self.Dispatcher.Stream.dispatch = self.Dispatcher.dispatch
- self.Dispatcher.Stream.stream_header_received = self._check_stream_start
- self.Dispatcher.Stream.features = None
- if self.sock_type == TYPE_CLIENT:
- self.send_stream_header()
-
- def send_stream_header(self):
- self.Dispatcher._metastream = nbxmpp.Node('stream:stream')
- self.Dispatcher._metastream.setNamespace(self.Namespace)
- self.Dispatcher._metastream.setAttr('version', '1.0')
- self.Dispatcher._metastream.setAttr('xmlns:stream', Namespace.STREAMS)
- self.Dispatcher._metastream.setAttr('from',
- self.conn_holder.zeroconf.name)
- if self.to:
- self.Dispatcher._metastream.setAttr('to', self.to)
- self.Dispatcher.send("<?xml version='1.0'?>%s>" % str(
- self.Dispatcher._metastream)[:-2])
-
- def _check_stream_start(self, ns, tag, attrs):
- if ns != Namespace.STREAMS or tag != 'stream':
- log.error('Incorrect stream start: (%s,%s).Terminating!',
- tag, ns)
- self.Connection.disconnect()
- if self.on_not_ok:
- self.on_not_ok('Connection to host could not be established: '
- 'Incorrect answer from server.')
- return
- if self.sock_type == TYPE_SERVER:
- if 'from' in attrs:
- self.to = attrs['from']
- self.send_stream_header()
- if 'version' in attrs and attrs['version'] == '1.0':
- # other part supports stream features
- features = nbxmpp.Node('stream:features')
- self.Dispatcher.send(features)
- while self.stanzaqueue:
- stanza, is_message = self.stanzaqueue.pop(0)
- self.send(stanza, is_message)
- elif self.sock_type == TYPE_CLIENT:
- while self.stanzaqueue:
- stanza, is_message = self.stanzaqueue.pop(0)
- self.send(stanza, is_message)
-
- def on_disconnect(self):
- if self.conn_holder:
- if self.fd in self.conn_holder.ids_of_awaiting_messages:
- del self.conn_holder.ids_of_awaiting_messages[self.fd]
- self.conn_holder.remove_connection(self.sock_hash)
- if 'Dispatcher' in self.__dict__:
- self._caller._unregister_new_handlers(self)
- self.Dispatcher.PlugOut()
- if 'P2PConnection' in self.__dict__:
- self.P2PConnection.PlugOut()
- self.Connection = None
- self._caller = None
- self.conn_holder = None
-
- def force_disconnect(self):
- if self.Connection:
- self.disconnect()
- else:
- self.on_disconnect()
-
- def _on_receive_document_attrs(self, data):
- if data:
- self.Dispatcher.ProcessNonBlocking(data)
- if not hasattr(self, 'Dispatcher') or \
- self.Dispatcher.Stream._document_attrs is None:
- return
- self.onreceive(None)
- if 'version' in self.Dispatcher.Stream._document_attrs and \
- self.Dispatcher.Stream._document_attrs['version'] == '1.0':
- #~ self.onreceive(self._on_receive_stream_features)
- #XXX continue with TLS
- return
- self.onreceive(None)
- return True
-
- def remove_timeout(self):
- pass
-
- def _register_handlers(self):
- self._caller.peerhost = self.Connection._sock.getsockname()
-
- self.RegisterHandler(*StanzaHandler(name='message',
- callback=self._caller._messageCB))
- self.RegisterHandler(*StanzaHandler(name='message',
- typ='error',
- callback=self._caller._message_error_received))
-
- self._caller._register_new_handlers(self)
-
-
-class P2PConnection(IdleObject, PlugIn):
- def __init__(self, sock_hash, _sock, addresses=None, caller=None,
- on_connect=None, client=None):
- IdleObject.__init__(self)
- self._owner = client
- PlugIn.__init__(self)
- self.sendqueue = []
- self.sendbuff = None
- self.buff_is_message = False
- self._sock = _sock
- self.sock_hash = None
- self.addresses = addresses
- self.on_connect = on_connect
- self.client = client
- self.writable = False
- self.readable = False
- self._exported_methods = [self.send, self.disconnect, self.onreceive]
- self.on_receive = None
- if _sock:
- self.host = addresses[0]['host']
- self.port = addresses[0]['port']
- self._sock = _sock
- self.state = 1
- self._sock.setblocking(False)
- self.fd = self._sock.fileno()
- self.on_connect(self)
- else:
- self.state = 0
- self.addresses_ = self.addresses
- self.get_next_addrinfo()
-
- def get_next_addrinfo(self):
- address = self.addresses_.pop(0)
- self.host = address['host']
- self.port = address['port']
- try:
- self.ais = socket.getaddrinfo(address['host'], address['port'], socket.AF_UNSPEC,
- socket.SOCK_STREAM)
- except socket.gaierror as e:
- log.info('Lookup failure for %s: %s[%s]', self.host, e[1],
- repr(e[0]), exc_info=True)
- if self.addresses_:
- return self.get_next_addrinfo()
- else:
- self.connect_to_next_ip()
-
- def connect_to_next_ip(self):
- if not self.ais:
- log.error('Connection failure to %s', str(self.host), exc_info=True)
- if self.addresses_:
- return self.get_next_addrinfo()
- self.disconnect()
- return
- ai = self.ais.pop(0)
- log.info('Trying to connect to %s through %s:%s',
- str(self.host), ai[4][0], ai[4][1], exc_info=True)
- try:
- self._sock = socket.socket(*ai[:3])
- self._sock.setblocking(False)
- self._server = ai[4]
- except socket.error:
- if sys.exc_value[0] != errno.EINPROGRESS:
- # for all errors, we try other addresses
- self.connect_to_next_ip()
- return
- self.fd = self._sock.fileno()
- app.idlequeue.plug_idle(self, True, False)
- self.set_timeout(CONNECT_TIMEOUT_SECONDS)
- self.do_connect()
-
- def set_timeout(self, timeout):
- app.idlequeue.remove_timeout(self.fd)
- if self.state >= 0:
- app.idlequeue.set_read_timeout(self.fd, timeout)
-
- def plugin(self, owner):
- self.onreceive(owner._on_receive_document_attrs)
- self._plug_idle()
- return True
-
- def plugout(self):
- """
- Disconnect from the remote server and unregister self.disconnected
- method from the owner's dispatcher
- """
- self.disconnect()
- self._owner = None
-
- def onreceive(self, recv_handler):
- if not recv_handler:
- if hasattr(self._owner, 'Dispatcher'):
- self.on_receive = self._owner.Dispatcher.ProcessNonBlocking
- else:
- self.on_receive = None
- return
- _tmp = self.on_receive
- # make sure this cb is not overridden by recursive calls
- if not recv_handler(None) and _tmp == self.on_receive:
- self.on_receive = recv_handler
-
- def send(self, packet, is_message=False, now=False):
- """
- Append stanza to the queue of messages to be send if now is False, else
- send it instantly
- """
- if self.state <= 0:
- return
-
- r = str(packet).encode('utf-8')
-
- if now:
- self.sendqueue.insert(0, (r, is_message))
- self._do_send()
- else:
- self.sendqueue.append((r, is_message))
- self._plug_idle()
-
- def read_timeout(self):
- ids = self.client.conn_holder.ids_of_awaiting_messages
- if self.fd in ids and ids[self.fd]:
- for (_id, thread_id) in ids[self.fd]:
- if hasattr(self._owner, 'Dispatcher'):
- self._owner.Dispatcher.Event('', 'DATA ERROR', (
- self.client.to, thread_id))
- else:
- self._owner.on_not_ok('connection timeout')
- ids[self.fd] = []
- self.pollend()
-
- def do_connect(self):
- errnum = 0
- try:
- self._sock.connect(self._server[:2])
- self._sock.setblocking(False)
- except Exception as ee:
- errnum = ee.errno
- errstr = ee.strerror
- errors = (errno.EINPROGRESS, errno.EALREADY, errno.EWOULDBLOCK)
- if 'WSAEINVAL' in errno.__dict__:
- errors += (errno.WSAEINVAL,)
- if errnum in errors:
- return
-
- # win32 needs this
- if errnum not in (0, 10056, errno.EISCONN) or self.state != 0:
- log.error('Could not connect to %s: %s [%s]', str(self.host),
- errnum, errstr)
- self.connect_to_next_ip()
- return
-
- # socket is already connected
- self._sock.setblocking(False)
- self.state = 1 # connected
- # we are connected
- self.on_connect(self)
-
- def pollout(self):
- if self.state == 0:
- self.do_connect()
- return
- app.idlequeue.remove_timeout(self.fd)
- self._do_send()
-
- def pollend(self):
- if self.state == 0: # error in connect()?
- #self.disconnect()
- self.connect_to_next_ip()
- else:
- self.state = -1
- self.disconnect()
-
- def pollin(self):
- """
- Reads all pending incoming data. Call owner's disconnected() method if
- appropriate
- """
- received = ''
- errnum = 0
- try:
- # get as many bites, as possible, but not more than RECV_BUFSIZE
- received = self._sock.recv(MAX_BUFF_LEN)
- except Exception as e:
- errnum = e.errno
- # "received" will be empty anyhow
- if errnum == ssl.SSL_ERROR_WANT_READ:
- pass
- elif errnum in [errno.ECONNRESET, errno.ENOTCONN, errno.ESHUTDOWN]:
- self.pollend()
- # don’t process result, in case it will raise an error
- return
- elif not received:
- if errnum != ssl.SSL_ERROR_EOF:
- # 8 EOF occurred in violation of protocol
- self.pollend()
- if self.state >= 0:
- self.disconnect()
- return
-
- if self.state < 0:
- return
-
- received = received.decode('utf-8')
-
- if self.on_receive:
- if self._owner.sock_type == TYPE_CLIENT:
- self.set_timeout(ACTIVITY_TIMEOUT_SECONDS)
- if received.strip():
- log.debug('received: %s', received)
- if hasattr(self._owner, 'Dispatcher'):
- self._owner.Dispatcher.Event('', 'DATA RECEIVED', received)
- self.on_receive(received)
- else:
- # This should never happed, so we need the debug
- log.error('Unhandled data received: %s', received)
- self.disconnect()
- return True
-
- def disconnect(self, message=''):
- """
- Close the socket
- """
- app.idlequeue.remove_timeout(self.fd)
- app.idlequeue.unplug_idle(self.fd)
- try:
- self._sock.shutdown(socket.SHUT_RDWR)
- self._sock.close()
- except socket.error:
- # socket is already closed
- pass
- self.fd = -1
- self.state = -1
- if self._owner:
- self._owner.on_disconnect()
-
- def _do_send(self):
- if not self.sendbuff:
- if not self.sendqueue:
- return None # nothing to send
- self.sendbuff, self.buff_is_message = self.sendqueue.pop(0)
- self.sent_data = self.sendbuff
- try:
- send_count = self._sock.send(self.sendbuff)
- if send_count:
- self.sendbuff = self.sendbuff[send_count:]
- if not self.sendbuff and not self.sendqueue:
- if self.state < 0:
- app.idlequeue.unplug_idle(self.fd)
- self._on_send()
- self.disconnect()
- return
- # we are not waiting for write
- self._plug_idle()
- self._on_send()
-
- except socket.error as e:
- if e.errno == ssl.SSL_ERROR_WANT_WRITE:
- return True
- if self.state < 0:
- self.disconnect()
- return
- self._on_send_failure()
- return
- if self._owner.sock_type == TYPE_CLIENT:
- self.set_timeout(ACTIVITY_TIMEOUT_SECONDS)
- return True
-
- def _plug_idle(self):
- readable = self.state != 0
- writable = self.sendqueue or self.sendbuff
- if self.writable != writable or self.readable != readable:
- app.idlequeue.plug_idle(self, writable, readable)
-
-
- def _on_send(self):
- if self.sent_data and self.sent_data.strip():
- log.debug('sent: %s', self.sent_data)
- if hasattr(self._owner, 'Dispatcher'):
- self._owner.Dispatcher.Event(
- '', 'DATA SENT', self.sent_data.decode('utf-8'))
- self.sent_data = None
- if self.buff_is_message:
- self._owner.on_message_sent(self.fd)
- self.buff_is_message = False
-
- def _on_send_failure(self):
- log.error('Socket error while sending data')
- self._owner.on_disconnect()
- self.sent_data = None
-
-class ClientZeroconf:
- def __init__(self, caller):
- self.caller = caller
- self.zeroconf = None
- self.roster = None
- self.last_msg = ''
- self.connections = {}
- self.recipient_to_hash = {}
- self.ip_to_hash = {}
- self.hash_to_port = {}
- self.listener = None
- self.ids_of_awaiting_messages = {}
- self.disconnect_handlers = []
- self.disconnecting = False
-
- def connect(self, show, msg):
- self.port = self.start_listener(self.caller.port)
- if not self.port:
- return False
- self.zeroconf_init(show, msg)
- if not self.zeroconf.connect():
- self.disconnect()
- return None
- self.roster = roster_zeroconf.Roster(self.zeroconf)
- return True
-
- def remove_announce(self):
- if self.zeroconf:
- return self.zeroconf.remove_announce()
-
- def announce(self):
- if self.zeroconf:
- return self.zeroconf.announce()
-
- def set_show_msg(self, show, msg):
- if self.zeroconf:
- self.zeroconf.txt['msg'] = msg
- self.last_msg = msg
- return self.zeroconf.update_txt(show)
-
- def resolve_all(self):
- if self.zeroconf:
- return self.zeroconf.resolve_all()
-
- def reannounce(self, txt):
- self.remove_announce()
- self.zeroconf.txt = txt
- self.zeroconf.port = self.port
- self.zeroconf.username = self.caller.username
- return self.announce()
-
- def zeroconf_init(self, show, msg):
- self.zeroconf = zeroconf.Zeroconf(self.caller._on_new_service,
- self.caller._on_remove_service, self.caller._on_name_conflictCB,
- self.caller._on_disconnect, self.caller._on_error,
- self.caller.username, self.caller.host, self.port)
- self.zeroconf.txt['msg'] = msg
- self.zeroconf.txt['status'] = show
- self.zeroconf.txt['1st'] = self.caller.first
- self.zeroconf.txt['last'] = self.caller.last
- self.zeroconf.txt['jid'] = self.caller.jabber_id
- self.zeroconf.txt['email'] = self.caller.email
- self.zeroconf.username = self.caller.username
- self.zeroconf.host = self.caller.host
- self.zeroconf.port = self.port
- self.last_msg = msg
-
- def disconnect(self):
- # to avoid recursive calls
- if self.disconnecting:
- return
- if self.listener:
- self.listener.disconnect()
- self.listener = None
- if self.zeroconf:
- self.zeroconf.disconnect()
- self.zeroconf = None
- if self.roster:
- self.roster.zeroconf = None
- self.roster._data = None
- self.roster = None
- self.disconnecting = True
- for i in reversed(self.disconnect_handlers):
- log.debug('Calling disconnect handler %s', i)
- i()
- self.disconnecting = False
-
- def start_disconnect(self):
- self.disconnect()
-
- def kill_all_connections(self):
- for connection in list(self.connections.values()):
- connection.force_disconnect()
-
- def add_connection(self, connection, ip, port, recipient):
- sock_hash = connection.sock_hash
- if sock_hash not in self.connections:
- self.connections[sock_hash] = connection
- self.ip_to_hash[ip] = sock_hash
- self.hash_to_port[sock_hash] = port
- if recipient:
- self.recipient_to_hash[recipient] = sock_hash
-
- def remove_connection(self, sock_hash):
- if sock_hash in self.connections:
- del self.connections[sock_hash]
- for i, v in self.recipient_to_hash.items():
- if v == sock_hash:
- self.recipient_to_hash.pop(i)
- break
- for i, v in self.ip_to_hash:
- if v == sock_hash:
- self.ip_to_hash.pop(i)
- break
- if sock_hash in self.hash_to_port:
- del self.hash_to_port[sock_hash]
-
- def start_listener(self, port):
- for p in range(port, port + 5):
- self.listener = ZeroconfListener(p, self)
- self.listener.bind()
- if self.listener.started:
- return p
- self.listener = None
- return False
-
- def getRoster(self):
- if self.roster:
- return self.roster.getRoster()
- return {}
-
- def send(self, stanza, is_message=False, now=False, on_ok=None,
- on_not_ok=None):
- to = stanza.getTo()
- if to is None:
- # Can’t send undirected stanza over Zeroconf.
- return -1
- to = to.bare
- stanza.setFrom(self.roster.zeroconf.name)
-
- try:
- item = self.roster[to]
- except KeyError:
- # Contact offline
- return -1
-
- # look for hashed connections
- if to in self.recipient_to_hash:
- conn = self.connections[self.recipient_to_hash[to]]
- id_ = stanza.getID() or ''
- if conn.add_stanza(stanza, is_message):
- if on_ok:
- on_ok(id_)
- return
-
- the_address = None
- for address in item['addresses']:
- if address['address'] in self.ip_to_hash:
- the_address = address
- if the_address and the_address['address'] in self.ip_to_hash:
- hash_ = self.ip_to_hash[the_address['address']]
- if self.hash_to_port[hash_] == the_address['port']:
- conn = self.connections[hash_]
- id_ = stanza.getID() or ''
- if conn.add_stanza(stanza, is_message):
- if on_ok:
- on_ok(id_)
- return
-
- # otherwise open new connection
- if not stanza.getID():
- stanza.setID('zero')
- addresses_ = []
- for address in item['addresses']:
- addresses_ += [{'host': address['address'], 'address': address['address'], 'port': address['port']}]
- P2PClient(None, addresses_, self,
- [(stanza, is_message)], to, on_ok=on_ok, on_not_ok=on_not_ok)
-
- def RegisterDisconnectHandler(self, handler):
- """
- Register handler that will be called on disconnect
- """
- self.disconnect_handlers.append(handler)
-
- def UnregisterDisconnectHandler(self, handler):
- """
- Unregister handler that is called on disconnect
- """
- self.disconnect_handlers.remove(handler)
-
- def SendAndWaitForResponse(self, stanza, timeout=None, func=None,
- args=None):
- """
- Send stanza and wait for recipient's response to it. Will call
- transports on_timeout callback if response is not retrieved in time
-
- Be aware: Only timeout of latest call of SendAndWait is active.
- """
-# if timeout is None:
-# timeout = DEFAULT_TIMEOUT_SECONDS
- def on_ok(_waitid):
-# if timeout:
-# self._owner.set_timeout(timeout)
- to = stanza.getTo()
- to = app.get_jid_without_resource(to)
-
- try:
- item = self.roster[to]
- except KeyError:
- # Contact offline
- item = None
-
- conn = None
- if to in self.recipient_to_hash:
- conn = self.connections[self.recipient_to_hash[to]]
- elif item:
- the_address = None
- for address in item['addresses']:
- if address['address'] in self.ip_to_hash:
- the_address = address
- if the_address and the_address['address'] in self.ip_to_hash:
- hash_ = self.ip_to_hash[the_address['address']]
- if self.hash_to_port[hash_] == the_address['port']:
- conn = self.connections[hash_]
- if func:
- conn.Dispatcher.on_responses[_waitid] = (func, args)
- conn.onreceive(conn.Dispatcher._WaitForData)
- conn.Dispatcher._expected[_waitid] = None
- self.send(stanza, on_ok=on_ok)
-
- def SendAndCallForResponse(self, stanza, func=None, args=None):
- """
- Put stanza on the wire and call back when recipient replies. Additional
- callback arguments can be specified in args.
- """
- self.SendAndWaitForResponse(stanza, 0, func, args)
diff --git a/gajim/common/zeroconf/connection_handlers_zeroconf.py b/gajim/common/zeroconf/connection_handlers_zeroconf.py
deleted file mode 100644
index 834b43286..000000000
--- a/gajim/common/zeroconf/connection_handlers_zeroconf.py
+++ /dev/null
@@ -1,125 +0,0 @@
-# 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))
diff --git a/gajim/common/zeroconf/connection_zeroconf.py b/gajim/common/zeroconf/connection_zeroconf.py
deleted file mode 100644
index 382b8f04c..000000000
--- a/gajim/common/zeroconf/connection_zeroconf.py
+++ /dev/null
@@ -1,603 +0,0 @@
-# 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>
-#
-# Copyright (C) 2003-2014 Yann Leboulanger <asterix@lagaule.org>
-# Copyright (C) 2003-2004 Vincent Hanquez <tab@snarc.org>
-# Copyright (C) 2006 Nikos Kouremenos <nkour@jabber.org>
-# Dimitur Kirov <dkirov@gmail.com>
-# Travis Shirk <travis@pobox.com>
-# Norman Rasmussen <norman@rasmussen.co.za>
-# 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 socket
-import getpass
-import logging
-import time
-
-import nbxmpp
-from gi.repository import GLib
-
-from gajim.common import app
-from gajim.common import modules
-from gajim.common import helpers
-from gajim.common import idle
-from gajim.common.i18n import _
-from gajim.common.const import ClientState
-from gajim.common.zeroconf import client_zeroconf
-from gajim.common.zeroconf import zeroconf
-from gajim.common.zeroconf.connection_handlers_zeroconf import ConnectionHandlersZeroconf
-
-log = logging.getLogger('gajim.c.connection_zeroconf')
-
-
-class NetworkEvent:
- pass
-
-class OurShowEvent(NetworkEvent):
-
- name = 'our-show'
-
- def init(self):
- self.reconnect = False
-
-
-class ConnectionLostEvent(NetworkEvent):
-
- name = 'connection-lost'
-
- def generate(self):
- app.ged.raise_event(OurShowEvent(
- None,
- conn=self.conn,
- show='offline'))
- return True
-
-
-class ConnectionZeroconf(ConnectionHandlersZeroconf):
- def __init__(self, name):
- ConnectionHandlersZeroconf.__init__(self)
- # system username
- self.username = None
- self.server_resource = '' # zeroconf has no resource, fake an empty one
- self.call_resolve_timeout = False
- # we don't need a password, but must be non-empty
- self.password = 'zeroconf'
- self.autoconnect = False
- self.httpupload = False
-
- self.name = name
- self._modules = {}
- self.connection = None # xmpppy ClientCommon instance
- self.is_zeroconf = False
- self.password = None
- self.server_resource = helpers.get_resource(self.name)
- self.priority = app.get_priority(name, 'offline')
- self.time_to_reconnect = None
- self._reconnect_timer_source = None
-
- self._state = ClientState.DISCONNECTED
- self._status = 'offline'
- self._status_message = ''
-
- # If handlers have been registered
- self.handlers_registered = False
-
- self.pep = {}
-
- self.roster_supported = True
-
- self._stun_servers = [] # STUN servers of our jabber server
-
- # Tracks the calls of the connect_machine() method
- self._connect_machine_calls = 0
-
- self.get_config_values_or_default()
-
- self.is_zeroconf = True
-
- # Register all modules
- modules.register_modules(self)
-
- def _set_state(self, state):
- log.info('State: %s', state)
- self._state = state
-
- @property
- def state(self):
- return self._state
-
- @property
- def status(self):
- return self._status
-
- @property
- def status_message(self):
- return self._status_message
-
- def _register_new_handlers(self, con):
- for handler in modules.get_handlers(self):
- if len(handler) == 5:
- name, func, typ, ns, priority = handler
- con.RegisterHandler(name, func, typ, ns, priority=priority)
- else:
- con.RegisterHandler(*handler)
- self.handlers_registered = True
-
- def _unregister_new_handlers(self, con):
- if not con:
- return
- for handler in modules.get_handlers(self):
- if len(handler) > 4:
- handler = handler[:4]
- con.UnregisterHandler(*handler)
- self.handlers_registered = False
-
- def get_module(self, name):
- return modules.get(self.name, name)
-
- def quit(self, kill_core):
- if kill_core and app.account_is_connected(self.name):
- self.disconnect(reconnect=False)
-
- def new_account(self, name, config, sync=False):
- """
- To be implemented by derived classes
- """
- raise NotImplementedError
-
- def _on_new_account(self, con=None, con_type=None):
- """
- To be implemented by derived classes
- """
- raise NotImplementedError
-
- def change_status(self, show, msg, auto=False):
- if not msg:
- msg = ''
-
- self._status = show
- self._status_message = msg
-
- if self._state.is_disconnected:
- if show == 'offline':
- return
-
- self.server_resource = helpers.get_resource(self.name)
- self.connect_and_init(show, msg)
- return
-
- if self._state.is_connecting or self._state.is_reconnect_scheduled:
- if show == 'offline':
- self.disconnect(reconnect=False)
- elif self._state.is_reconnect_scheduled:
- self.reconnect()
- return
-
- # We are connected
- if show == 'offline':
- presence = self.get_module('Presence').get_presence(
- typ='unavailable',
- status=msg,
- caps=False)
-
- self.connection.send(presence, now=True)
- self.disconnect(reconnect=False)
- return
-
- idle_time = None
- if auto:
- if app.is_installed('IDLE') and app.settings.get('autoaway'):
- idle_sec = idle.Monitor.get_idle_sec()
- idle_time = time.strftime(
- '%Y-%m-%dT%H:%M:%SZ',
- time.gmtime(time.time() - idle_sec))
-
- self._update_status(show, msg, idle_time=idle_time)
-
- def get_config_values_or_default(self):
- """
- Get name, host, port from config, or create zeroconf account with default
- values
- """
- self.host = socket.gethostname()
- app.settings.set_account_setting(app.ZEROCONF_ACC_NAME,
- 'hostname',
- self.host)
- self.port = app.settings.get_account_setting(app.ZEROCONF_ACC_NAME,
- 'custom_port')
- self.autoconnect = app.settings.get_account_setting(
- app.ZEROCONF_ACC_NAME, 'autoconnect')
- self.sync_with_global_status = app.settings.get_account_setting(
- app.ZEROCONF_ACC_NAME, 'sync_with_global_status')
- self.first = app.settings.get_account_setting(app.ZEROCONF_ACC_NAME,
- 'zeroconf_first_name')
- self.last = app.settings.get_account_setting(app.ZEROCONF_ACC_NAME,
- 'zeroconf_last_name')
- self.jabber_id = app.settings.get_account_setting(app.ZEROCONF_ACC_NAME,
- 'zeroconf_jabber_id')
- self.email = app.settings.get_account_setting(app.ZEROCONF_ACC_NAME,
- 'zeroconf_email')
-
- if not self.username:
- self.username = getpass.getuser()
- app.settings.set_account_setting(app.ZEROCONF_ACC_NAME,
- 'name',
- self.username)
- else:
- self.username = app.settings.get_account_setting(
- app.ZEROCONF_ACC_NAME, 'name')
-
- def get_own_jid(self, *args, **kwargs):
- return nbxmpp.JID.from_string(self.username + '@' + self.host)
-
- def reconnect(self):
- # Do not try to reco while we are already trying
- self.time_to_reconnect = None
- log.debug('reconnect')
-
- self.disconnect()
- self.change_status(self._status, self._status_message)
-
- def disable_account(self):
- self.disconnect()
-
- def _on_resolve_timeout(self):
- if self._state.is_connected:
- if not self.connection.resolve_all():
- self.disconnect()
- return False
- diffs = self.roster.getDiffs()
- for key in diffs:
- self.roster.setItem(key)
- app.ged.raise_event(NetworkEvent(
- 'roster-info', conn=self, jid=key,
- nickname=self.roster.getName(key), sub='both',
- ask='no', groups=self.roster.getGroups(key),
- avatar_sha=None))
- self._on_presence(key)
- #XXX open chat windows don't get refreshed (full name), add that
- return self.call_resolve_timeout
-
- # callbacks called from zeroconf
- def _on_new_service(self, jid):
- self.roster.setItem(jid)
- app.ged.raise_event(NetworkEvent(
- 'roster-info', conn=self, jid=jid,
- nickname=self.roster.getName(jid), sub='both',
- ask='no', groups=self.roster.getGroups(jid),
- avatar_sha=None))
- self._on_presence(jid)
-
- def _on_remove_service(self, jid):
- self.roster.delItem(jid)
- # 'NOTIFY' (account, (jid, status, status message, resource, priority,
- # timestamp))
- self._on_presence(jid, show='offline', status='')
-
- def _on_presence(self, jid, show=None, status=None):
- if status is None:
- status = self.roster.getMessage(jid)
- if show is None:
- show = self.roster.getStatus(jid)
-
- ptype = 'unavailable' if show == 'offline' else None
-
- event_attrs = {
- 'conn': self,
- 'prio': 0,
- 'need_add_in_roster': False,
- 'popup': False,
- 'ptype': ptype,
- 'jid': jid,
- 'resource': 'local',
- 'id_': None,
- 'fjid': jid,
- 'timestamp': 0,
- 'avatar_sha': None,
- 'user_nick': '',
- 'idle_time': None,
- 'show': show,
- 'new_show': show,
- 'old_show': 0,
- 'status': status,
- 'contact_list': [],
- 'contact': None,
- }
-
- event_ = NetworkEvent('presence-received', **event_attrs)
-
- self._update_contact(event_)
-
- app.ged.raise_event(event_)
-
- def _update_contact(self, event):
- jid = event.jid
-
- status_strings = ['offline', 'error', 'online', 'chat', 'away',
- 'xa', 'dnd']
-
- event.new_show = status_strings.index(event.show)
-
- contact = app.contacts.get_contact_strict(self.name, jid, '')
- if contact is None:
- contact = app.contacts.get_contact_strict(self.name, jid, 'local')
-
- if contact.show in status_strings:
- event.old_show = status_strings.index(contact.show)
-
- # Update contact with presence data
- contact.resource = 'local'
- contact.show = event.show
- contact.status = event.status
- contact.priority = event.prio
- contact.idle_time = event.idle_time
-
- event.contact = contact
-
- # It's not an agent
- if event.old_show == 0 and event.new_show > 1:
- if not jid in app.newly_added[self.name]:
- app.newly_added[self.name].append(jid)
- if jid in app.to_be_removed[self.name]:
- app.to_be_removed[self.name].remove(jid)
- elif event.old_show > 1 and event.new_show == 0 and self._state.is_connected:
- if not jid in app.to_be_removed[self.name]:
- app.to_be_removed[self.name].append(jid)
- if jid in app.newly_added[self.name]:
- app.newly_added[self.name].remove(jid)
-
- if event.ptype == 'unavailable':
- # TODO: This causes problems when another
- # resource signs off!
- self.get_module('Bytestream').stop_all_active_file_transfers(contact)
-
- def _on_name_conflictCB(self, alt_name):
- self.disconnect()
- app.ged.raise_event(OurShowEvent(None, conn=self,
- show='offline'))
- app.ged.raise_event(
- NetworkEvent('zeroconf-name-conflict',
- conn=self,
- alt_name=alt_name))
-
- def _on_error(self, message):
- log.warning('avahi error: %s', message)
-
- def connect(self, show='online', msg=''):
- self.get_config_values_or_default()
- if not self.connection:
- self.connection = client_zeroconf.ClientZeroconf(self)
- if not zeroconf.test_zeroconf():
- app.ged.raise_event(OurShowEvent(None, conn=self,
- show='offline'))
- self._status = 'offline'
- app.ged.raise_event(ConnectionLostEvent(None,
- conn=self, title=_('Could not connect to "%s"') % self.name,
- msg=_('Please check if Avahi or Bonjour is installed.')))
- self.disconnect()
- return
- result = self.connection.connect(show, msg)
- if not result:
- app.ged.raise_event(OurShowEvent(None, conn=self,
- show='offline'))
- self._status = 'offline'
- if result is False:
- app.ged.raise_event(ConnectionLostEvent(None,
- conn=self, title=_('Could not start local service'),
- msg=_('Unable to bind to port %d.') % self.port))
- else: # result is None
- app.ged.raise_event(ConnectionLostEvent(None,
- conn=self, title=_('Could not start local service'),
- msg=_('Please check if avahi/bonjour-daemon is running.')))
- self.disconnect()
- return
- else:
- self.connection.announce()
- self.roster = self.connection.getRoster()
- app.ged.raise_event(NetworkEvent('roster-received', conn=self,
- roster=self.roster.copy(), received_from_server=True))
-
- # display contacts already detected and resolved
- for jid in self.roster.keys():
- app.ged.raise_event(NetworkEvent(
- 'roster-info', conn=self, jid=jid,
- nickname=self.roster.getName(jid), sub='both',
- ask='no', groups=self.roster.getGroups(jid),
- avatar_sha=None))
- self._on_presence(jid)
-
- self._status = show
-
- # refresh all contacts data every five seconds
- self.call_resolve_timeout = True
- GLib.timeout_add_seconds(5, self._on_resolve_timeout)
- return True
-
- def disconnect(self, reconnect=True, immediately=True):
- log.info('Start disconnecting zeroconf')
- if reconnect:
- self.time_to_reconnect = 5
- else:
- self.time_to_reconnect = None
-
- self._set_state(ClientState.DISCONNECTED)
- if self.connection:
- self.connection.disconnect()
- self.connection = None
- # stop calling the timeout
- self.call_resolve_timeout = False
- app.ged.raise_event(OurShowEvent(None, conn=self,
- show='offline'))
-
- def _on_disconnect(self):
- self._set_state(ClientState.DISCONNECTED)
- app.ged.raise_event(OurShowEvent(None, conn=self,
- show='offline'))
-
- def reannounce(self):
- if self._state.is_connected:
- txt = {}
- txt['1st'] = app.settings.get_account_setting(
- app.ZEROCONF_ACC_NAME, 'zeroconf_first_name')
- txt['last'] = app.settings.get_account_setting(
- app.ZEROCONF_ACC_NAME, 'zeroconf_last_name')
- txt['jid'] = app.settings.get_account_setting(
- app.ZEROCONF_ACC_NAME, 'zeroconf_jabber_id')
- txt['email'] = app.settings.get_account_setting(
- app.ZEROCONF_ACC_NAME, 'zeroconf_email')
- self.connection.reannounce(txt)
-
- def update_details(self) -> None:
- if self.connection:
- port = app.settings.get_account_setting(app.ZEROCONF_ACC_NAME,
- 'custom_port')
- if port != self.port:
- self.port = port
- last_msg = self.connection.last_msg
- self.disconnect()
- if not self.connect(self._status, last_msg):
- return
- self.connection.announce()
- else:
- self.reannounce()
-
- def connect_and_init(self, show, msg):
- # to check for errors from zeroconf
- check = True
- if not self.connect(show, msg):
- return
-
- check = self.connection.announce()
-
- # stay offline when zeroconf does something wrong
- if check:
- self._set_state(ClientState.CONNECTED)
- app.ged.raise_event(NetworkEvent('signed-in', conn=self))
- app.ged.raise_event(OurShowEvent(None, conn=self,
- show=show))
- else:
- # show notification that avahi or system bus is down
- self._set_state(ClientState.DISCONNECTED)
- app.ged.raise_event(OurShowEvent(None, conn=self,
- show='offline'))
- self._status = 'offline'
- app.ged.raise_event(ConnectionLostEvent(None, conn=self,
- title=_('Could not change status of account "%s"') % self.name,
- msg=_('Please check if avahi-daemon is running.')))
-
- def _update_status(self, show, msg, idle_time=None):
- if self.connection.set_show_msg(show, msg):
- app.ged.raise_event(OurShowEvent(None, conn=self,
- show=show))
- else:
- # show notification that avahi or system bus is down
- app.ged.raise_event(OurShowEvent(None, conn=self,
- show='offline'))
- self._status = 'offline'
- app.ged.raise_event(ConnectionLostEvent(None, conn=self,
- title=_('Could not change status of account "%s"') % self.name,
- msg=_('Please check if avahi-daemon is running.')))
-
- def send_message(self, message):
- stanza = self.get_module('Message').build_message_stanza(message)
- message.stanza = stanza
-
- if message.contact is None:
- # Only Single Message should have no contact
- self._send_message(message)
- return
-
- method = message.contact.settings.get('encryption')
- if not method:
- self._send_message(message)
- return
-
- app.plugin_manager.extension_point('encrypt%s' % method,
- self,
- message,
- self._send_message)
-
- def _send_message(self, message):
- def on_send_ok(stanza_id):
- app.ged.raise_event(
- NetworkEvent('messasge-sent',
- jid=message.jid,
- **vars(message)))
- self.get_module('Message').log_message(message)
-
- def on_send_not_ok(reason):
- reason += ' ' + _('Your message could not be sent.')
- app.ged.raise_event(NetworkEvent(
- 'zeroconf-error',
- account=self.name,
- jid=message.jid,
- message=reason))
-
- ret = self.connection.send(
- message.stanza, message.message is not None,
- on_ok=on_send_ok, on_not_ok=on_send_not_ok)
- message.timestamp = time.time()
-
- if ret == -1:
- # Contact Offline
- error_message = _(
- 'Contact is offline. Your message could not be sent.')
- app.ged.raise_event(NetworkEvent(
- 'zeroconf-error',
- account=self.name,
- jid=message.jid,
- message=error_message))
- return
-
- def send_stanza(self, stanza):
- # send a stanza untouched
- if not self.connection:
- return
- if not isinstance(stanza, nbxmpp.Node):
- stanza = nbxmpp.Protocol(node=stanza)
- self.connection.send(stanza)
-
- def _event_dispatcher(self, realm, event, data):
- if realm == '':
- if event == 'STANZA RECEIVED':
- app.ged.raise_event(
- NetworkEvent('stanza-received',
- conn=self,
- stanza_str=str(data)))
- elif event == 'DATA SENT':
- app.ged.raise_event(
- NetworkEvent('stanza-sent',
- conn=self,
- stanza_str=str(data)))
-
- if event == nbxmpp.transports.DATA_ERROR:
- frm = data[0]
- error_message = _(
- 'Connection to host could not be established: '
- 'Timeout while sending data.')
- app.ged.raise_event(NetworkEvent(
- 'zeroconf-error',
- account=self.name,
- jid=frm,
- message=error_message))
-
- def cleanup(self):
- pass
diff --git a/gajim/common/zeroconf/roster_zeroconf.py b/gajim/common/zeroconf/roster_zeroconf.py
deleted file mode 100644
index b4d66d7bc..000000000
--- a/gajim/common/zeroconf/roster_zeroconf.py
+++ /dev/null
@@ -1,160 +0,0 @@
-# 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()
diff --git a/gajim/common/zeroconf/zeroconf.py b/gajim/common/zeroconf/zeroconf.py
deleted file mode 100644
index 674367ee0..000000000
--- a/gajim/common/zeroconf/zeroconf.py
+++ /dev/null
@@ -1,63 +0,0 @@
-# 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
diff --git a/gajim/common/zeroconf/zeroconf_avahi.py b/gajim/common/zeroconf/zeroconf_avahi.py
deleted file mode 100644
index 49640d798..000000000
--- a/gajim/common/zeroconf/zeroconf_avahi.py
+++ /dev/null
@@ -1,552 +0,0 @@
-# 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/>.
-
-import logging
-
-from gi.repository import Avahi
-from gi.repository import Gio
-from gi.repository import GLib
-
-from gajim.common.i18n import _
-from gajim.common.zeroconf.zeroconf import Constant, ConstantRI
-from gajim.common.zeroconf.zeroconf_avahi_const import DBUS_NAME
-from gajim.common.zeroconf.zeroconf_avahi_const import DBUS_INTERFACE_SERVER
-from gajim.common.zeroconf.zeroconf_avahi_const import DBUS_INTERFACE_ENTRY_GROUP
-from gajim.common.zeroconf.zeroconf_avahi_const import DBUS_INTERFACE_DOMAIN_BROWSER
-from gajim.common.zeroconf.zeroconf_avahi_const import ServerState
-from gajim.common.zeroconf.zeroconf_avahi_const import EntryGroup
-from gajim.common.zeroconf.zeroconf_avahi_const import DomainBrowser
-from gajim.common.zeroconf.zeroconf_avahi_const import Protocol
-from gajim.common.zeroconf.zeroconf_avahi_const import Interface
-
-log = logging.getLogger('gajim.c.z.zeroconf_avahi')
-
-
-class Zeroconf:
- def __init__(self, new_service_cb, remove_service_cb, name_conflict_cb,
- disconnected_cb, error_cb, name, host, port):
- self.domain = None # specific domain to browse
- self.stype = '_presence._tcp'
- self.port = port # listening port that gets announced
- self.username = name
- self.host = host
- self.txt = {}
- self.name = None
-
- self.connected = False
- self.announced = False
-
- #XXX these CBs should be set to None when we destroy the object
- # (go offline), because they create a circular reference
- self._new_service_cb = new_service_cb
- self._remove_service_cb = remove_service_cb
- self._name_conflict_cb = name_conflict_cb
- self._disconnected_cb = disconnected_cb
- self._error_cb = error_cb
-
- self._server = None
- self._avahi_client = None
- self._service_browser = None
- self._domain_browser = None
- self._entrygroup = None
-
- self._sb_connections = []
- self._connections = {}
-
- self._contacts = {}
- self._invalid_self_contact = {}
-
- @staticmethod
- def _call(proxy, method_name):
- try:
- output = proxy.call_sync(
- method_name, None, Gio.DBusCallFlags.NONE, -1, None)
- if output:
- return output[0]
- except GLib.Error as error:
- log.debug(error)
- return None
-
- def _error_callback(self, error):
- log.debug(error)
- # timeouts are non-critical
- if str(error) != 'Timeout reached':
- self.disconnect()
- self._disconnected_cb()
-
- def _new_service_callback(self, _browser, interface, protocol, name, stype,
- domain, _flags):
- log.debug('Found service %s in domain %s on %i.%i.',
- name, domain, interface, protocol)
- if not self.connected:
- return
-
- # synchronous resolving
- try:
- output = self._server.call_sync(
- 'ResolveService',
- GLib.Variant('(iisssiu)', (interface, protocol, name, stype,
- domain, Protocol.UNSPEC, 0)),
- Gio.DBusCallFlags.NONE, -1, None)
- self.service_resolved_callback(*output)
- except GLib.Error as error:
- log.debug('Error while resolving: %s', error)
-
- def _remove_service_callback(self, _browser, interface, protocol, name,
- _stype, domain, _flags):
- log.debug('Service %s in domain %s on %i.%i disappeared.',
- name, domain, interface, protocol)
- if not self.connected:
- return
- if name == self.name:
- return
-
- for key in list(self._contacts.keys()):
- val = self._contacts[key]
- if val[Constant.BARE_NAME] == name:
- # try to reduce instead of delete first
- resolved_info = val[Constant.RESOLVED_INFO]
- if len(resolved_info) > 1:
- for i, _info in enumerate(resolved_info):
- if resolved_info[i][ConstantRI.INTERFACE] == interface and resolved_info[i][ConstantRI.PROTOCOL] == protocol:
- del self._contacts[key][Constant.RESOLVED_INFO][i]
- # if still something left, don't remove
- if len(self._contacts[key][Constant.RESOLVED_INFO]) > 1:
- return
- del self._contacts[key]
- self._remove_service_cb(key)
- return
-
- def _new_service_type(self, interface, protocol, stype, domain, _flags):
- # Are we already browsing this domain for this type?
- if self._service_browser:
- return
-
- self._service_browser = Avahi.ServiceBrowser.new_full(
- interface, protocol, stype, domain, 0)
-
- self._avahi_client = Avahi.Client(flags=0,)
- self._avahi_client.start()
- con = self._service_browser.connect('new_service',
- self._new_service_callback)
- self._sb_connections.append(con)
- con = self._service_browser.connect('removed_service',
- self._remove_service_callback)
- self._sb_connections.append(con)
- con = self._service_browser.connect('failure', self._error_callback)
- self._sb_connections.append(con)
-
- self._service_browser.attach(self._avahi_client)
-
- def _new_domain_callback(self, interface, protocol, domain, _flags):
- if domain != 'local':
- self._browse_domain(interface, protocol, domain)
-
- @staticmethod
- def txt_array_to_dict(txt_array):
- txt_dict = {}
- for array in txt_array:
- item = bytes(array)
- item = item.decode('utf-8')
- item = item.split('=', 1)
-
- if item[0] and (item[0] not in txt_dict):
- if len(item) == 1:
- txt_dict[item[0]] = None
- else:
- txt_dict[item[0]] = item[1]
-
- return txt_dict
-
- @staticmethod
- def dict_to_txt_array(txt_dict):
- array = []
-
- for key, value in txt_dict.items():
- item = '%s=%s' % (key, value)
- item = item.encode('utf-8')
- array.append(item)
-
- return array
-
- def service_resolved_callback(self, interface, protocol, name, _stype,
- domain, host, aprotocol, address, port, txt,
- _flags):
- log.debug('Service data for service %s in domain %s on %i.%i:',
- name, domain, interface, protocol)
- log.debug('Host %s (%s), port %i, TXT data: %s',
- host, address, port, self.txt_array_to_dict(txt))
- if not self.connected:
- return
- bare_name = name
- if name.find('@') == -1:
- name = name + '@' + name
-
- # we don't want to see ourselves in the list
- if name != self.name:
- resolved_info = [(interface, protocol, host,
- aprotocol, address, int(port))]
- if name in self._contacts:
- # Decide whether to try to merge with existing resolved info:
- old_name, old_domain, old_resolved_info, old_bare_name, _old_txt = self._contacts[name]
- if name == old_name and domain == old_domain and bare_name == old_bare_name:
- # Seems similar enough, try to merge resolved info:
- for i, _info in enumerate(old_resolved_info):
- # for now, keep a single record for each (interface, protocol) pair
- #
- # Note that, theoretically, we could both get IPv4 and
- # IPv6 aprotocol responses via the same protocol,
- # so this probably needs to be revised again.
- if old_resolved_info[i][0:2] == (interface, protocol):
- log.debug('Deleting resolved info for interface %s',
- old_resolved_info[i])
- del old_resolved_info[i]
- break
- resolved_info = resolved_info + old_resolved_info
- log.debug('Collected resolved info is now: %s',
- resolved_info)
- self._contacts[name] = (name, domain, resolved_info, bare_name, txt)
- self._new_service_cb(name)
- else:
- # remember data
- # In case this is not our own record but of another
- # gajim instance on the same machine,
- # it will be used when we get a new name.
- self._invalid_self_contact[name] = (
- name,
- domain,
- (interface, protocol, host, aprotocol, address, int(port)),
- bare_name,
- txt)
-
- def _service_resolved_all_callback(self, _interface, _protocol, name,
- _stype, _domain, _host, _aprotocol,
- _address, _port, txt, _flags):
- if not self.connected:
- return
-
- if name.find('@') == -1:
- name = name + '@' + name
- # update TXT data only, as intended according to resolve_all comment
- old_contact = self._contacts[name]
- self._contacts[name] = old_contact[0:Constant.TXT] + (txt,) + old_contact[Constant.TXT+1:]
-
- def _service_add_fail_callback(self, err):
- log.debug('Error while adding service. %s', str(err))
- if 'Local name collision' in str(err):
- alternative_name = self._server.call_sync(
- 'GetAlternativeServiceName',
- GLib.Variant('(s)', (self.username,)),
- Gio.DBusCallFlags.NONE, -1, None)
- self._name_conflict_cb(alternative_name[0])
- return
- self._error_cb(_('Error while adding service. %s') % str(err))
- self.disconnect()
-
- def _server_state_changed_callback(self, _connection, _sender_name,
- _object_path, _interface_name,
- _signal_name, parameters):
- state, _ = parameters
- log.debug('server state changed to %s', state)
- if state == ServerState.RUNNING:
- self._create_service()
- elif state in (ServerState.COLLISION,
- ServerState.REGISTERING):
- self.disconnect()
- if self._entrygroup:
- self._call(self._entrygroup, 'Reset')
-
- def _entrygroup_state_changed_callback(self, _connection, _sender_name,
- _object_path, _interface_name,
- _signal_name, parameters):
- state, _ = parameters
- # the name is already present, so recreate
- if state == EntryGroup.COLLISION:
- log.debug('zeroconf.py: local name collision')
- self._service_add_fail_callback('Local name collision')
- elif state == EntryGroup.FAILURE:
- self.disconnect()
- self._call(self._entrygroup, 'Reset')
- log.debug('zeroconf.py: ENTRY_GROUP_FAILURE reached (that '
- 'should not happen)')
-
- @staticmethod
- def _replace_show(show):
- if show in ['chat', 'online', '']:
- return 'avail'
- if show == 'xa':
- return 'away'
- return show
-
- def avahi_txt(self):
- return self.dict_to_txt_array(self.txt)
-
- def _create_service(self):
- try:
- if not self._entrygroup:
- # create an EntryGroup for publishing
- object_path = self._server.call_sync(
- 'EntryGroupNew', None, Gio.DBusCallFlags.NONE, -1, None)
-
- self._entrygroup = Gio.DBusProxy.new_for_bus_sync(
- Gio.BusType.SYSTEM, Gio.DBusProxyFlags.NONE, None,
- DBUS_NAME, *object_path, DBUS_INTERFACE_ENTRY_GROUP, None)
-
- connection = self._entrygroup.get_connection()
- subscription = connection.signal_subscribe(
- DBUS_NAME, DBUS_INTERFACE_ENTRY_GROUP, 'StateChanged',
- *object_path, None, Gio.DBusSignalFlags.NONE,
- self._entrygroup_state_changed_callback)
-
- self._connections[connection] = [subscription]
-
- txt = {}
-
- # remove empty keys
- for key, val in self.txt.items():
- if val:
- txt[key] = val
-
- txt['port.p2pj'] = self.port
- txt['version'] = 1
- txt['txtvers'] = 1
-
- # replace gajim's show messages with compatible ones
- if 'status' in self.txt:
- txt['status'] = self._replace_show(self.txt['status'])
- else:
- txt['status'] = 'avail'
-
- self.txt = txt
- log.debug('Publishing service %s of type %s',
- self.name, self.stype)
-
- try:
- self._entrygroup.call_sync(
- 'AddService',
- GLib.Variant('(iiussssqaay)', (Interface.UNSPEC,
- Protocol.UNSPEC, 0,
- self.name, self.stype, '',
- '', self.port,
- self.avahi_txt())),
- Gio.DBusCallFlags.NONE, -1, None)
- except GLib.Error as error:
- self._service_add_fail_callback(error)
- return False
-
- try:
- self._entrygroup.call_sync('Commit', None,
- Gio.DBusCallFlags.NONE, -1, None)
- except GLib.Error:
- pass
-
- return True
- except GLib.Error as error:
- log.debug(error)
- return False
-
- def announce(self):
- if not self.connected:
- return False
-
- state = self._server.call_sync(
- 'GetState', None, Gio.DBusCallFlags.NONE, -1, None)
-
- if state[0] == ServerState.RUNNING:
- if self._create_service():
- self.announced = True
- return True
- return False
- return None
-
- def remove_announce(self):
- if self.announced is False:
- return False
-
- if self._call(self._entrygroup, 'GetState') != EntryGroup.FAILURE:
- self._call(self._entrygroup, 'Reset')
- self._call(self._entrygroup, 'Free')
- self._entrygroup = None
- self.announced = False
-
- return True
- return False
-
- def _browse_domain(self, interface, protocol, domain):
- self._new_service_type(interface, protocol, self.stype, domain, '')
-
- def _avahi_dbus_connect_cb(self, connection, sender_name, object_path,
- interface_name, signal_name, parameters):
- name, old_owner, new_owner = parameters
- if name == DBUS_NAME:
- if new_owner and not old_owner:
- log.debug('We are connected to avahi-daemon')
- else:
- log.debug('Lost connection to avahi-daemon')
- self.disconnect()
- if self._disconnected_cb:
- self._disconnected_cb()
-
- def _connect_dbus(self):
- try:
- proxy = Gio.DBusProxy.new_for_bus_sync(
- Gio.BusType.SYSTEM, Gio.DBusProxyFlags.NONE, None,
- 'org.freedesktop.DBus', '/org/freedesktop/DBus',
- 'org.freedesktop.DBus', None)
-
- connection = proxy.get_connection()
- subscription = connection.signal_subscribe(
- 'org.freedesktop.DBus', 'org.freedesktop.DBus',
- 'NameOwnerChanged', '/org/freedesktop/DBus', None,
- Gio.DBusSignalFlags.NONE, self._avahi_dbus_connect_cb)
- self._connections[connection] = [subscription]
- except GLib.Error as error:
- log.debug(error)
- return False
- else:
- return True
-
- def _connect_avahi(self):
- if not self._connect_dbus():
- return False
-
- if self._server:
- return True
- try:
- self._server = Gio.DBusProxy.new_for_bus_sync(
- Gio.BusType.SYSTEM, Gio.DBusProxyFlags.NONE, None,
- DBUS_NAME, '/', DBUS_INTERFACE_SERVER, None)
-
- connection = self._server.get_connection()
- subscription = connection.signal_subscribe(
- DBUS_NAME, DBUS_INTERFACE_SERVER, 'StateChanged', '/', None,
- Gio.DBusSignalFlags.NONE, self._server_state_changed_callback)
- self._connections[connection] = [subscription]
- except Exception as error:
- # Avahi service is not present
- self._server = None
- log.debug(error)
- return False
- else:
- return True
-
- def connect(self):
- self.name = self.username + '@' + self.host # service name
- if not self._connect_avahi():
- return False
-
- self.connected = True
- # start browsing
- if self.domain is None:
- # Explicitly browse .local
- self._browse_domain(
- Interface.UNSPEC, Protocol.UNSPEC, 'local')
-
- # Browse for other browsable domains
- object_path = self._server.call_sync(
- 'DomainBrowserNew',
- GLib.Variant('(iisiu)', (Interface.UNSPEC, Protocol.UNSPEC, '',
- DomainBrowser.BROWSE, 0)),
- Gio.DBusCallFlags.NONE, -1, None)
-
- self._domain_browser = Gio.DBusProxy.new_for_bus_sync(
- Gio.BusType.SYSTEM, Gio.DBusProxyFlags.NONE, None, DBUS_NAME,
- *object_path, DBUS_INTERFACE_DOMAIN_BROWSER, None)
-
- connection = self._domain_browser.get_connection()
- subscription = connection.signal_subscribe(
- DBUS_NAME, DBUS_INTERFACE_DOMAIN_BROWSER, 'ItemNew',
- *object_path, None, Gio.DBusSignalFlags.NONE,
- self._new_domain_callback)
- self._connections[connection] = [subscription]
-
- subscription = connection.signal_subscribe(
- DBUS_NAME, DBUS_INTERFACE_DOMAIN_BROWSER, 'Failure',
- *object_path, None, Gio.DBusSignalFlags.NONE,
- self._error_callback)
- self._connections[connection].append(subscription)
- else:
- self._browse_domain(
- Interface.UNSPEC, Protocol.UNSPEC, self.domain)
-
- return True
-
- def disconnect(self):
- if self.connected:
- self.connected = False
- for connection, subscriptions in self._connections.items():
- for subscription in subscriptions:
- connection.signal_unsubscribe(subscription)
- for con in self._sb_connections:
- self._service_browser.disconnect(con)
- if self._domain_browser:
- self._call(self._domain_browser, 'Free')
- self.remove_announce()
- self._server = None
- self._service_browser = None
- self._domain_browser = None
-
- # refresh txt data of all contacts manually (no callback available)
- def resolve_all(self):
- if not self.connected:
- return False
- for val in self._contacts.values():
- # get txt data from last recorded resolved info
- # TODO: Better try to get it from last IPv6 mDNS, then last IPv4?
- ri = val[Constant.RESOLVED_INFO][0]
- output = self._server.call_sync(
- 'ResolveService',
- GLib.Variant('(iisssiu)', (ri[ConstantRI.INTERFACE],
- ri[ConstantRI.PROTOCOL],
- val[Constant.BARE_NAME],
- self.stype, val[Constant.DOMAIN],
- Protocol.UNSPEC,
- 0)),
- Gio.DBusCallFlags.NONE,
- -1,
- None
- )
- self._service_resolved_all_callback(*output)
-
- return True
-
- def get_contacts(self):
- return self._contacts
-
- def get_contact(self, jid):
- if not jid in self._contacts:
- return None
- return self._contacts[jid]
-
- def update_txt(self, show=None):
- if show:
- self.txt['status'] = self._replace_show(show)
-
- txt = self.avahi_txt()
- if self.connected and self._entrygroup:
- try:
- self._entrygroup.call_sync(
- 'UpdateServiceTxt',
- GLib.Variant('(iiusssaay)', (Interface.UNSPEC,
- Protocol.UNSPEC, 0, self.name,
- self.stype, '', txt)),
- Gio.DBusCallFlags.NONE, -1, None)
- except GLib.Error as error:
- self._error_callback(error)
- return False
-
- return True
- return False
diff --git a/gajim/common/zeroconf/zeroconf_avahi_const.py b/gajim/common/zeroconf/zeroconf_avahi_const.py
deleted file mode 100644
index e0bd61e8f..000000000
--- a/gajim/common/zeroconf/zeroconf_avahi_const.py
+++ /dev/null
@@ -1,58 +0,0 @@
-# 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
-# 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 enum import IntEnum
-
-
-DBUS_NAME = "org.freedesktop.Avahi"
-DBUS_INTERFACE_SERVER = DBUS_NAME + ".Server"
-DBUS_INTERFACE_ENTRY_GROUP = DBUS_NAME + ".EntryGroup"
-DBUS_INTERFACE_DOMAIN_BROWSER = DBUS_NAME + ".DomainBrowser"
-
-
-class ServerState(IntEnum):
- INVALID = 0
- REGISTERING = 1
- RUNNING = 2
- COLLISION = 3
- FAILURE = 4
-
-
-class EntryGroup(IntEnum):
- UNCOMMITTED = 0
- REGISTERING = 1
- ESTABLISHED = 2
- COLLISION = 3
- FAILURE = 4
-
-
-class DomainBrowser(IntEnum):
- BROWSE = 0
- BROWSE_DEFAULT = 1
- REGISTER = 2
- REGISTER_DEFAULT = 3
- BROWSE_LEGACY = 4
-
-
-class Protocol(IntEnum):
- UNSPEC = -1
- INET = 0
- INET6 = 1
-
-
-class Interface(IntEnum):
- UNSPEC = -1
diff --git a/gajim/common/zeroconf/zeroconf_bonjour.py b/gajim/common/zeroconf/zeroconf_bonjour.py
deleted file mode 100644
index d9489ec6e..000000000
--- a/gajim/common/zeroconf/zeroconf_bonjour.py
+++ /dev/null
@@ -1,458 +0,0 @@
-# Copyright (C) 2006 Stefan Bethge <stefan@lanpartei.de>
-# Copyright (C) 2006 Philipp Hörist <philipp@hoerist.com>
-#
-# 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 logging
-import select
-import re
-
-from gajim.common.i18n import _
-from gajim.common.zeroconf.zeroconf import Constant
-
-
-log = logging.getLogger('gajim.c.z.zeroconf_bonjour')
-
-try:
- from pybonjour import kDNSServiceErr_NoError
- from pybonjour import kDNSServiceErr_ServiceNotRunning
- from pybonjour import kDNSServiceErr_NameConflict
- from pybonjour import kDNSServiceInterfaceIndexAny
- from pybonjour import kDNSServiceType_TXT
- from pybonjour import kDNSServiceFlagsAdd
- from pybonjour import kDNSServiceFlagsNoAutoRename
- from pybonjour import BonjourError
- from pybonjour import TXTRecord
- from pybonjour import DNSServiceUpdateRecord
- from pybonjour import DNSServiceResolve
- from pybonjour import DNSServiceProcessResult
- from pybonjour import DNSServiceGetAddrInfo
- from pybonjour import DNSServiceQueryRecord
- from pybonjour import DNSServiceBrowse
- from pybonjour import DNSServiceRegister
-except ImportError:
- pass
-
-resolve_timeout = 1
-
-
-class Zeroconf:
- def __init__(self, new_service_cb, remove_service_cb, name_conflict_cb,
- _disconnected_cb, error_cb, name, host, port):
- self.stype = '_presence._tcp'
- self.port = port # listening port that gets announced
- self.username = name
- self.host = host
- self.txt = {} # service data
- self.name = None
-
- self.connected = False
- self.announced = False
-
- # XXX these CBs should be set to None when we destroy the object
- # (go offline), because they create a circular reference
- self._new_service_cb = new_service_cb
- self._remove_service_cb = remove_service_cb
- self._name_conflict_cb = name_conflict_cb
- self._error_cb = error_cb
-
- self._service_sdref = None
- self._browse_sdref = None
-
- self._contacts = {} # all current local contacts with data
- self._invalid_self_contact = {}
- self._resolved_hosts = {}
- self._resolved = []
- self._queried = []
-
- def _browse_callback(self, _sdref, flags, interface, error_code,
- service_name, regtype, reply_domain):
- log.debug('Found service %s in domain %s on %i(type: %s).',
- service_name, reply_domain, interface, regtype)
- if not self.connected:
- return
- if error_code != kDNSServiceErr_NoError:
- log.debug('Error in browse_callback: %s', str(error_code))
- return
- if not flags & kDNSServiceFlagsAdd:
- self._remove_service_callback(service_name)
- return
-
- try:
- # asynchronous resolving
- resolve_sdref = None
- resolve_sdref = DNSServiceResolve(
- 0, interface, service_name,
- regtype, reply_domain, self._service_resolved_callback)
-
- while not self._resolved:
- ready = select.select([resolve_sdref], [], [], resolve_timeout)
- if resolve_sdref not in ready[0]:
- log.info('Resolve timed out')
- break
- DNSServiceProcessResult(resolve_sdref)
- else:
- self._resolved.pop()
-
- except BonjourError as error:
- log.info('Error when resolving DNS: %s', error)
-
- finally:
- if resolve_sdref:
- resolve_sdref.close()
-
- def _remove_service_callback(self, name):
- log.info('Service %s disappeared.', name)
- if not self.connected:
- return
- if name != self.name:
- for key in list(self._contacts.keys()):
- if self._contacts[key][Constant.NAME] == name:
- del self._contacts[key]
- self._remove_service_cb(key)
- return
-
- @staticmethod
- def txt_array_to_dict(txt):
- if not isinstance(txt, TXTRecord):
- txt = TXTRecord.parse(txt)
- return dict((v[0], v[1]) for v in txt)
-
- @staticmethod
- def _parse_name(fullname):
- log.debug('Parse name: %s', fullname)
- # TODO: do proper decoding...
- escaping = {r'\.': '.',
- r'\032': ' ',
- r'\064': '@',
- }
-
- # Split on '.' but do not split on '\.'
- result = re.split(r'(?<!\\\\)\.', fullname)
- name = result[0]
- protocol, domain = result[2:4]
-
- # Replace the escaped values
- for src, trg in escaping.items():
- name = name.replace(src, trg)
-
- bare_name = name
- if '@' not in name:
- name = name + '@' + name
- log.debug('End parse: %s %s %s %s',
- name, bare_name, protocol, domain)
-
- return name, bare_name, protocol, domain
-
- def _query_txt_callback(self, _sdref, _flags, _interface, error_code,
- hosttarget, _rrtype, _rrclass, rdata, _ttl):
-
- if error_code != kDNSServiceErr_NoError:
- log.error('Error in query_record_callback: %s', str(error_code))
- return
-
- name = self._parse_name(hosttarget)[0]
-
- if name != self.name:
- # update TXT data only, as intended according to
- # resolve_all comment
- old_contact = self._contacts[name]
- self._contacts[name] = old_contact[0:Constant.TXT] + (rdata,) + old_contact[Constant.TXT+1:]
- log.debug(self._contacts[name])
-
- self._queried.append(True)
-
- def _getaddrinfo_callback(self, _sdref, _flags, interface, error_code,
- hosttarget, address, _ttl):
- if error_code != kDNSServiceErr_NoError:
- log.error('Error in getaddrinfo_callback: %s', str(error_code))
- return
-
- fullname, port, txt_record = self._resolved_hosts[hosttarget]
-
- txt = TXTRecord.parse(txt_record)
- ip = address[1]
-
- name, bare_name, protocol, domain = self._parse_name(fullname)
-
- log.info('Service data for service %s on %i:',
- fullname, interface)
- log.info('Host %s, ip %s, port %i, TXT data: %s',
- hosttarget, ip, port, txt)
-
- if not self.connected:
- return
-
- # we don't want to see ourselves in the list
- if name != self.name:
- resolved_info = [(interface, protocol, hosttarget,
- fullname, ip, port)]
- self._contacts[name] = (name, domain, resolved_info,
- bare_name, txt_record)
-
- self._new_service_cb(name)
- else:
- # remember data
- # In case this is not our own record but of another
- # gajim instance on the same machine,
- # it will be used when we get a new name.
- self._invalid_self_contact[name] = \
- (name, domain,
- (interface, protocol, hosttarget, fullname, ip, port),
- bare_name, txt_record)
-
- self._queried.append(True)
-
- def _service_resolved_callback(self, _sdref, _flags, interface,
- error_code, fullname, hosttarget, port,
- txt_record):
- if error_code != kDNSServiceErr_NoError:
- log.error('Error in service_resolved_callback: %s', str(error_code))
- return
-
- self._resolved_hosts[hosttarget] = (fullname, port, txt_record)
-
- try:
- getaddrinfo_sdref = \
- DNSServiceGetAddrInfo(
- interfaceIndex=interface,
- hostname=hosttarget,
- callBack=self._getaddrinfo_callback)
-
- while not self._queried:
- ready = select.select(
- [getaddrinfo_sdref], [], [], resolve_timeout)
- if getaddrinfo_sdref not in ready[0]:
- log.warning('GetAddrInfo timed out')
- break
- DNSServiceProcessResult(getaddrinfo_sdref)
- else:
- self._queried.pop()
-
- except BonjourError as error:
- if error.error_code == kDNSServiceErr_ServiceNotRunning:
- log.info('Service not running')
- else:
- self._error_cb(_('Error while adding service. %s') % error)
-
- finally:
- if getaddrinfo_sdref:
- getaddrinfo_sdref.close()
-
- self._resolved.append(True)
-
- def service_added_callback(self, _sdref, _flags, error_code,
- _name, _regtype, _domain):
- if error_code == kDNSServiceErr_NoError:
- log.info('Service successfully added')
-
- elif error_code == kDNSServiceErr_NameConflict:
- log.error('Error while adding service. %s', error_code)
- self._name_conflict_cb(self._get_alternativ_name(self.username))
- else:
- error = _('Error while adding service. %s') % str(error_code)
- self._error_cb(error)
-
- @staticmethod
- def _get_alternativ_name(name):
- if name[-2] == '-':
- try:
- number = int(name[-1])
- except Exception:
- return '%s-1' % name
- return '%s-%s' % (name[:-2], number + 1)
- return '%s-1' % name
-
- @staticmethod
- def _replace_show(show):
- if show in ['chat', 'online', '']:
- return 'avail'
- if show == 'xa':
- return 'away'
- return show
-
- def _create_service(self):
- txt = {}
-
- # remove empty keys
- for key, val in self.txt.items():
- if val:
- txt[key] = val
-
- txt['port.p2pj'] = self.port
- txt['version'] = 1
- txt['txtvers'] = 1
-
- # replace gajim's show messages with compatible ones
- if 'status' in self.txt:
- txt['status'] = self._replace_show(self.txt['status'])
- else:
- txt['status'] = 'avail'
- self.txt = txt
- try:
- self._service_sdref = DNSServiceRegister(
- flags=kDNSServiceFlagsNoAutoRename,
- name=self.name,
- regtype=self.stype,
- port=self.port,
- txtRecord=TXTRecord(self.txt, strict=True),
- callBack=self.service_added_callback)
-
- log.info('Publishing service %s of type %s', self.name, self.stype)
-
- ready = select.select([self._service_sdref], [], [])
- if self._service_sdref in ready[0]:
- DNSServiceProcessResult(self._service_sdref)
-
- except BonjourError as error:
- if error.errorCode == kDNSServiceErr_ServiceNotRunning:
- log.info('Service not running')
- else:
- self._error_cb(_('Error while adding service. %s') % error)
- self.disconnect()
-
- def announce(self):
- if not self.connected:
- return False
-
- self._create_service()
- self.announced = True
- return True
-
- def remove_announce(self):
- if not self.announced:
- return False
-
- if self._service_sdref is None:
- return False
-
- try:
- self._service_sdref.close()
- self.announced = False
- return True
- except BonjourError as error:
- log.error('Error when removing announce: %s', error)
- return False
-
- def connect(self):
- self.name = self.username + '@' + self.host # service name
-
- self.connected = True
-
- # start browsing
- if self.browse_domain():
- return True
-
- self.disconnect()
- return False
-
- def disconnect(self):
- if self.connected:
- self.connected = False
- if self._browse_sdref is not None:
- self._browse_sdref.close()
- self._browse_sdref = None
- self.remove_announce()
-
- def browse_domain(self, domain=None):
- try:
- self._browse_sdref = DNSServiceBrowse(
- regtype=self.stype,
- domain=domain,
- callBack=self._browse_callback)
- log.info('Starting to browse .local')
- return True
- except BonjourError as error:
- if error.errorCode == kDNSServiceErr_ServiceNotRunning:
- log.info('Service not running')
- else:
- log.error('Error while browsing for services. %s', error)
- return False
-
- def browse_loop(self):
- try:
- ready = select.select([self._browse_sdref], [], [], 0)
- if self._browse_sdref in ready[0]:
- DNSServiceProcessResult(self._browse_sdref)
- except BonjourError as error:
- if error.errorCode == kDNSServiceErr_ServiceNotRunning:
- log.info('Service not running')
- return False
- log.error('Error while browsing for services. %s', error)
- return True
-
- # resolve_all() is called every X seconds and queries for new clients
- # and monitors TXT records for changed status
- def resolve_all(self):
- if not self.connected:
- return False
- # for now put here as this is synchronous
- if not self.browse_loop():
- return False
-
- # Monitor TXT Records with DNSServiceQueryRecord because
- # its more efficient (see pybonjour documentation)
- for val in self._contacts.values():
- bare_name = val[Constant.RESOLVED_INFO][0][Constant.BARE_NAME]
-
- try:
- query_sdref = None
- query_sdref = \
- DNSServiceQueryRecord(
- interfaceIndex=kDNSServiceInterfaceIndexAny,
- fullname=bare_name,
- rrtype=kDNSServiceType_TXT,
- callBack=self._query_txt_callback)
-
- while not self._queried:
- ready = select.select(
- [query_sdref], [], [], resolve_timeout)
- if query_sdref not in ready[0]:
- log.info('Query record timed out')
- break
- DNSServiceProcessResult(query_sdref)
- else:
- self._queried.pop()
-
- except BonjourError as error:
- if error.errorCode == kDNSServiceErr_ServiceNotRunning:
- log.info('Service not running')
- return False
- log.error('Error in query for TXT records. %s', error)
- finally:
- if query_sdref:
- query_sdref.close()
-
- return True
-
- def get_contacts(self):
- return self._contacts
-
- def get_contact(self, jid):
- if jid not in self._contacts:
- return None
- return self._contacts[jid]
-
- def update_txt(self, show=None):
- if show:
- self.txt['status'] = self._replace_show(show)
-
- txt = TXTRecord(self.txt, strict=True)
- try:
- DNSServiceUpdateRecord(self._service_sdref, None, 0, txt)
- except BonjourError as error:
- log.error('Error when updating TXT Record: %s', error)
- return False
- return True
diff --git a/gajim/data/gui/zeroconf_information_window.ui b/gajim/data/gui/zeroconf_information_window.ui
deleted file mode 100644
index 2d3f4453b..000000000
--- a/gajim/data/gui/zeroconf_information_window.ui
+++ /dev/null
@@ -1,398 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- Generated with glade 3.18.3 -->
-<interface>
- <requires lib="gtk+" version="3.12"/>
- <object class="GtkWindow" id="zeroconf_information_window">
- <property name="can_focus">False</property>
- <property name="border_width">12</property>
- <property name="title" translatable="yes">Contact Information</property>
- <property name="resizable">False</property>
- <property name="type_hint">dialog</property>
- <signal name="destroy" handler="on_zeroconf_information_window_destroy" swapped="no"/>
- <signal name="key-press-event" handler="on_zeroconf_information_window_key_press_event" swapped="no"/>
- <child>
- <object class="GtkBox" id="vbox1">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="orientation">vertical</property>
- <property name="spacing">12</property>
- <child>
- <object class="GtkLabel" id="nickname_label">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="xalign">0</property>
- <property name="selectable">True</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkNotebook" id="information_notebook">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <child>
- <object class="GtkBox" id="hbox3">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="border_width">6</property>
- <property name="spacing">12</property>
- <child>
- <object class="GtkGrid" id="grid1">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="row_spacing">6</property>
- <property name="column_spacing">6</property>
- <child>
- <object class="GtkLabel" id="label51">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="xalign">0</property>
- <property name="yalign">0</property>
- <property name="label" translatable="yes">Local jid:</property>
- </object>
- <packing>
- <property name="left_attach">0</property>
- <property name="top_attach">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="label53">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="xalign">0</property>
- <property name="yalign">0</property>
- <property name="label" translatable="yes">Resource:</property>
- </object>
- <packing>
- <property name="left_attach">0</property>
- <property name="top_attach">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="label54">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="xalign">0</property>
- <property name="yalign">0</property>
- <property name="label" translatable="yes">Status:</property>
- </object>
- <packing>
- <property name="left_attach">0</property>
- <property name="top_attach">2</property>
- </packing>
- </child>
- <child>
- <object class="GtkCheckButton" id="log_history_checkbutton">
- <property name="label" translatable="yes">_Store conversation history</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">False</property>
- <property name="use_underline">True</property>
- <property name="xalign">0</property>
- <property name="active">True</property>
- <property name="draw_indicator">True</property>
- </object>
- <packing>
- <property name="left_attach">0</property>
- <property name="top_attach">3</property>
- <property name="width">2</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="local_jid_label">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="hexpand">True</property>
- <property name="xalign">0</property>
- <property name="yalign">0</property>
- <property name="xpad">5</property>
- <property name="ypad">5</property>
- <property name="selectable">True</property>
- </object>
- <packing>
- <property name="left_attach">1</property>
- <property name="top_attach">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkEventBox" id="resource_prio_label_eventbox">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="visible_window">False</property>
- <child>
- <object class="GtkLabel" id="resource_prio_label">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="hexpand">True</property>
- <property name="xalign">0</property>
- <property name="yalign">0</property>
- <property name="xpad">5</property>
- <property name="ypad">5</property>
- <property name="selectable">True</property>
- </object>
- </child>
- </object>
- <packing>
- <property name="left_attach">1</property>
- <property name="top_attach">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkEventBox" id="status_label_eventbox">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <child>
- <object class="GtkLabel" id="status_label">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="hexpand">True</property>
- <property name="xalign">0</property>
- <property name="yalign">0</property>
- <property name="xpad">5</property>
- <property name="ypad">5</property>
- <property name="selectable">True</property>
- </object>
- </child>
- </object>
- <packing>
- <property name="left_attach">1</property>
- <property name="top_attach">2</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkBox" id="vbox2">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="orientation">vertical</property>
- <child>
- <object class="GtkEventBox" id="PHOTO_eventbox">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="visible_window">False</property>
- <child>
- <object class="GtkImage" id="PHOTO_image">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="yalign">0</property>
- </object>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <placeholder/>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- </child>
- <child type="tab">
- <object class="GtkLabel" id="label3">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="xalign">0</property>
- <property name="yalign">0</property>
- <property name="label" translatable="yes">Contact</property>
- </object>
- <packing>
- <property name="tab_fill">False</property>
- </packing>
- </child>
- <child>
- <object class="GtkGrid" id="grid2">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="border_width">6</property>
- <property name="row_spacing">6</property>
- <property name="column_spacing">6</property>
- <child>
- <object class="GtkLabel" id="label59">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="xalign">0</property>
- <property name="yalign">0</property>
- <property name="label" translatable="yes">First Name:</property>
- </object>
- <packing>
- <property name="left_attach">0</property>
- <property name="top_attach">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="label58">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="xalign">0</property>
- <property name="yalign">0</property>
- <property name="label" translatable="yes">Last Name:</property>
- </object>
- <packing>
- <property name="left_attach">0</property>
- <property name="top_attach">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="label55">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="xalign">0</property>
- <property name="yalign">0</property>
- <property name="label" translatable="yes">XMPP Address:</property>
- </object>
- <packing>
- <property name="left_attach">0</property>
- <property name="top_attach">2</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="label56">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="xalign">0</property>
- <property name="yalign">0</property>
- <property name="label" translatable="yes">E-Mail:</property>
- </object>
- <packing>
- <property name="left_attach">0</property>
- <property name="top_attach">3</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="first_name_label">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="hexpand">True</property>
- <property name="xalign">0</property>
- <property name="yalign">0</property>
- <property name="xpad">5</property>
- <property name="ypad">5</property>
- <property name="selectable">True</property>
- </object>
- <packing>
- <property name="left_attach">1</property>
- <property name="top_attach">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="last_name_label">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="hexpand">True</property>
- <property name="xalign">0</property>
- <property name="yalign">0</property>
- <property name="xpad">5</property>
- <property name="ypad">5</property>
- <property name="selectable">True</property>
- </object>
- <packing>
- <property name="left_attach">1</property>
- <property name="top_attach">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="jabber_id_label">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="hexpand">True</property>
- <property name="xalign">0</property>
- <property name="yalign">0</property>
- <property name="xpad">5</property>
- <property name="ypad">5</property>
- <property name="selectable">True</property>
- </object>
- <packing>
- <property name="left_attach">1</property>
- <property name="top_attach">2</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="email_label">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="hexpand">True</property>
- <property name="xalign">0</property>
- <property name="yalign">0</property>
- <property name="xpad">5</property>
- <property name="ypad">5</property>
- <property name="wrap">True</property>
- <property name="selectable">True</property>
- </object>
- <packing>
- <property name="left_attach">1</property>
- <property name="top_attach">3</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="position">1</property>
- </packing>
- </child>
- <child type="tab">
- <object class="GtkLabel" id="label57">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">Personal</property>
- </object>
- <packing>
- <property name="position">1</property>
- <property name="tab_fill">False</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkButtonBox" id="hbuttonbox1">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="layout_style">end</property>
- <child>
- <object class="GtkButton" id="close_button">
- <property name="label">gtk-close</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="can_default">True</property>
- <property name="receives_default">False</property>
- <property name="use_stock">True</property>
- <signal name="clicked" handler="on_close_button_clicked" swapped="no"/>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">0</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">2</property>
- </packing>
- </child>
- </object>
- </child>
- </object>
-</interface>
diff --git a/gajim/gtk/accounts.py b/gajim/gtk/accounts.py
index 160d4b497..5a82e7271 100644
--- a/gajim/gtk/accounts.py
+++ b/gajim/gtk/accounts.py
@@ -17,7 +17,6 @@ from __future__ import annotations
from typing import Any
from typing import cast
-import sys
import locale
import logging
from collections import defaultdict
@@ -33,7 +32,6 @@ from gajim.common import passwords
from gajim.common.events import AccountDisonnected
from gajim.common.i18n import _
from gajim.common.i18n import Q_
-from gajim.common.zeroconf.connection_zeroconf import ConnectionZeroconf
from .dialogs import DialogButton
from .dialogs import ConfirmationDialog
@@ -72,9 +70,6 @@ class AccountsWindow(Gtk.ApplicationWindow):
self.add(box)
for account in app.get_accounts_sorted():
- if account == 'Local':
- # Disable zeroconf support until its working again
- continue
self.add_account(account, initial=True)
self._menu.connect('menu-activated', self._on_menu_activated)
@@ -125,12 +120,6 @@ class AccountsWindow(Gtk.ApplicationWindow):
if not app.account_is_connected(account):
return
- if account == app.ZEROCONF_ACC_NAME:
- zeroconf_conn = cast(
- ConnectionZeroconf, app.connections[app.ZEROCONF_ACC_NAME])
- zeroconf_conn.update_details()
- return
-
def relog():
app.connections[account].disconnect(gracefully=True,
reconnect=True,
@@ -149,22 +138,14 @@ class AccountsWindow(Gtk.ApplicationWindow):
@staticmethod
def _get_relogin_settings(account: str) -> list[str]:
- if account == app.ZEROCONF_ACC_NAME:
- settings = [
- 'zeroconf_first_name',
- 'zeroconf_last_name',
- 'zeroconf_jabber_id',
- 'zeroconf_email'
- ]
- else:
- settings = [
- 'client_cert',
- 'proxy',
- 'resource',
- 'use_custom_host',
- 'custom_host',
- 'custom_port'
- ]
+ settings = [
+ 'client_cert',
+ 'proxy',
+ 'resource',
+ 'use_custom_host',
+ 'custom_host',
+ 'custom_port'
+ ]
values: list[Any] = []
for setting in settings:
@@ -173,10 +154,6 @@ class AccountsWindow(Gtk.ApplicationWindow):
@staticmethod
def on_remove_account(account: str) -> None:
- if app.settings.get_account_setting(account, 'is_zeroconf'):
- # Should never happen as button is insensitive
- return
-
open_window('RemoveAccount', account=account)
def remove_account(self, account: str) -> None:
@@ -270,8 +247,6 @@ class AccountMenu(Gtk.Box):
@staticmethod
def _sort_func(row1: AccountRow, row2: AccountRow) -> int:
- if row1.label == 'Local':
- return -1
return locale.strcoll(row1.label.lower(), row2.label.lower())
def add_account(self, row: AccountRow) -> None:
@@ -341,11 +316,10 @@ class AccountSubMenu(Gtk.ListBox):
self.add(AccountLabelMenuItem(self, self._account))
self.add(BackMenuItem())
self.add(PageMenuItem('general', _('General')))
- if account != 'Local':
- self.add(PageMenuItem('privacy', _('Privacy')))
- self.add(PageMenuItem('connection', _('Connection')))
- self.add(PageMenuItem('advanced', _('Advanced')))
- self.add(RemoveMenuItem())
+ self.add(PageMenuItem('privacy', _('Privacy')))
+ self.add(PageMenuItem('connection', _('Connection')))
+ self.add(PageMenuItem('advanced', _('Advanced')))
+ self.add(RemoveMenuItem())
@property
def account(self) -> str:
@@ -461,13 +435,10 @@ class Account:
self._menu = menu
self._settings = settings
- if account == app.ZEROCONF_ACC_NAME:
- self._settings.add_page(ZeroConfPage(account))
- else:
- self._settings.add_page(GeneralPage(account))
- self._settings.add_page(ConnectionPage(account))
- self._settings.add_page(PrivacyPage(account))
- self._settings.add_page(AdvancedPage(account))
+ self._settings.add_page(GeneralPage(account))
+ self._settings.add_page(ConnectionPage(account))
+ self._settings.add_page(PrivacyPage(account))
+ self._settings.add_page(AdvancedPage(account))
self._account_row = AccountRow(account)
self._menu.add_account(self._account_row)
@@ -535,17 +506,6 @@ class AccountRow(Gtk.ListBoxRow):
self._switch_state_label.set_valign(Gtk.Align.CENTER)
self._set_label(account_enabled)
- if (self._account == app.ZEROCONF_ACC_NAME and
- not app.is_installed('ZEROCONF')):
- self._switch.set_active(False)
- self.set_activatable(False)
- self.set_sensitive(False)
- if sys.platform in ('win32', 'darwin'):
- tooltip = _('Please check if Bonjour is installed.')
- else:
- tooltip = _('Please check if Avahi is installed.')
- self.set_tooltip_text(tooltip)
-
self._switch.connect(
'state-set', self._on_enable_switch, self._account)
@@ -946,55 +906,6 @@ class AdvancedPage(GenericSettingPage):
GenericSettingPage.__init__(self, account, settings)
-class ZeroConfPage(GenericSettingPage):
-
- name = 'general'
-
- def __init__(self, account: str) -> None:
-
- settings = [
- Setting(SettingKind.DIALOG, _('Profile'),
- SettingType.DIALOG,
- props={'dialog': ZeroconfProfileDialog}),
-
- Setting(SettingKind.SWITCH, _('Connect on startup'),
- SettingType.ACCOUNT_CONFIG, 'autoconnect',
- desc=_('Use environment variable')),
-
- Setting(SettingKind.SWITCH,
- _('Save conversations for all contacts'),
- SettingType.ACCOUNT_CONFIG, 'no_log_for',
- desc=_('Store conversations on the harddrive')),
-
- Setting(SettingKind.SWITCH, _('Global Status'),
- SettingType.ACCOUNT_CONFIG, 'sync_with_global_status',
- desc=_('Synchronize the status of all accounts')),
- ]
-
- GenericSettingPage.__init__(self, account, settings)
-
-
-class ZeroconfProfileDialog(SettingsDialog):
- def __init__(self, account: str, parent: Gtk.Window) -> None:
-
- settings = [
- Setting(SettingKind.ENTRY, _('First Name'),
- SettingType.ACCOUNT_CONFIG, 'zeroconf_first_name'),
-
- Setting(SettingKind.ENTRY, _('Last Name'),
- SettingType.ACCOUNT_CONFIG, 'zeroconf_last_name'),
-
- Setting(SettingKind.ENTRY, _('XMPP Address'),
- SettingType.ACCOUNT_CONFIG, 'zeroconf_jabber_id'),
-
- Setting(SettingKind.ENTRY, _('Email'),
- SettingType.ACCOUNT_CONFIG, 'zeroconf_email'),
- ]
-
- SettingsDialog.__init__(self, parent, _('Profile'),
- Gtk.DialogFlags.MODAL, settings, account)
-
-
class PriorityDialog(SettingsDialog):
def __init__(self, account: str, parent: Gtk.Window) -> None:
diff --git a/gajim/gtk/adhoc_muc.py b/gajim/gtk/adhoc_muc.py
index b90876fc5..fe26160d5 100644
--- a/gajim/gtk/adhoc_muc.py
+++ b/gajim/gtk/adhoc_muc.py
@@ -74,8 +74,6 @@ class AdhocMUC(Gtk.ApplicationWindow):
def _add_possible_invitees(self):
for acc in app.settings.get_active_accounts():
client = app.get_client(acc)
- if client.is_zeroconf:
- continue
for contact in client.get_module('Roster').iter_contacts():
jid = str(contact.jid)
# Add contact if it can be invited
@@ -92,7 +90,7 @@ class AdhocMUC(Gtk.ApplicationWindow):
def _is_invitable(self, contact):
# All contacts BUT the following can be invited:
- # ourself, gateway contacts, zeroconf contacts
+ # ourself, gateway contacts
return (str(contact.jid) != str(self.jid) and
contact.jid != str(self._client.get_own_jid().bare) and
str(contact.jid) != app.get_jid_from_account(self.account) and
diff --git a/gajim/gtk/application.py b/gajim/gtk/application.py
index 024dbbeee..f3ba7f9ac 100644
--- a/gajim/gtk/application.py
+++ b/gajim/gtk/application.py
@@ -425,9 +425,6 @@ class GajimApplication(Gtk.Application, CoreApplication):
def _get_account_actions(self,
account: str) -> list[tuple[str, Any, str, str]]:
- if account == 'Local':
- return []
-
# pylint: disable=line-too-long
return [
('-bookmarks', self._on_bookmarks_action, 'online', 's'),
diff --git a/gajim/gtk/builder.pyi b/gajim/gtk/builder.pyi
index 29770fd7d..7ddc321fc 100644
--- a/gajim/gtk/builder.pyi
+++ b/gajim/gtk/builder.pyi
@@ -131,40 +131,6 @@ class BookmarksBuilder(Builder):
autojoin: Gtk.CellRendererToggle
-class ZeroconfInformationWindowBuilder(Builder):
- zeroconf_information_window: Gtk.Window
- vbox1: Gtk.Box
- nickname_label: Gtk.Label
- information_notebook: Gtk.Notebook
- hbox3: Gtk.Box
- grid1: Gtk.Grid
- label51: Gtk.Label
- label53: Gtk.Label
- label54: Gtk.Label
- log_history_checkbutton: Gtk.CheckButton
- local_jid_label: Gtk.Label
- resource_prio_label_eventbox: Gtk.EventBox
- resource_prio_label: Gtk.Label
- status_label_eventbox: Gtk.EventBox
- status_label: Gtk.Label
- vbox2: Gtk.Box
- PHOTO_eventbox: Gtk.EventBox
- PHOTO_image: Gtk.Image
- label3: Gtk.Label
- grid2: Gtk.Grid
- label59: Gtk.Label
- label58: Gtk.Label
- label55: Gtk.Label
- label56: Gtk.Label
- first_name_label: Gtk.Label
- last_name_label: Gtk.Label
- jabber_id_label: Gtk.Label
- email_label: Gtk.Label
- label57: Gtk.Label
- hbuttonbox1: Gtk.ButtonBox
- close_button: Gtk.Button
-
-
class EmojiChooserBuilder(Builder):
box: Gtk.Box
search: Gtk.SearchEntry
@@ -988,8 +954,6 @@ def get_builder(file_name: Literal['profile.ui'], widgets: list[str] = ...) -> P
@overload
def get_builder(file_name: Literal['bookmarks.ui'], widgets: list[str] = ...) -> BookmarksBuilder: ...
@overload
-def get_builder(file_name: Literal['zeroconf_information_window.ui'], widgets: list[str] = ...) -> ZeroconfInformationWindowBuilder: ...
-@overload
def get_builder(file_name: Literal['emoji_chooser.ui'], widgets: list[str] = ...) -> EmojiChooserBuilder: ...
@overload
def get_builder(file_name: Literal['manage_pep_services_window.ui'], widgets: list[str] = ...) -> ManagePepServicesWindowBuilder: ...
diff --git a/gajim/gtk/controls/chat.py b/gajim/gtk/controls/chat.py
index 3d048bdcb..e8aa2fcf7 100644
--- a/gajim/gtk/controls/chat.py
+++ b/gajim/gtk/controls/chat.py
@@ -145,7 +145,7 @@ class ChatControl(BaseControl):
self.set_lock_image()
self.xml.encryption_menu.set_menu_model(get_encryption_menu(
- self.control_id, self._type, self.account == 'Local'))
+ self.control_id, self._type))
self.set_encryption_menu_icon()
self.msg_textview.grab_focus()
@@ -253,11 +253,8 @@ class ChatControl(BaseControl):
self._get_action('send-marker-').change_state(state)
# Convert to GC
- if app.settings.get_account_setting(self.account, 'is_zeroconf'):
- self._get_action('invite-contacts-').set_enabled(False)
- else:
- enabled = self.contact.supports(Namespace.MUC) and online
- self._get_action('invite-contacts-').set_enabled(enabled)
+ enabled = self.contact.supports(Namespace.MUC) and online
+ self._get_action('invite-contacts-').set_enabled(enabled)
# Information
self._get_action('information-').set_enabled(online)
diff --git a/gajim/gtk/features.py b/gajim/gtk/features.py
index 9b8952666..871945d2b 100644
--- a/gajim/gtk/features.py
+++ b/gajim/gtk/features.py
@@ -118,14 +118,6 @@ class Features(Gtk.ApplicationWindow):
_('Requires: libxss'),
_('No additional requirements'),
auto_status_enabled),
- Feature(_('Bonjour / Zeroconf (Serverless Chat)'),
- app.is_installed('ZEROCONF'),
- _('Enables Gajim to automatically detected clients in a '
- 'local network for serverless chats'),
- _('Requires: gir1.2-avahi-0.6'),
- _('Requires: pybonjour and bonjour SDK running (%(url)s)')
- % {'url': 'https://developer.apple.com/opensource/)'},
- None),
Feature(_('Location detection'),
app.is_installed('GEOCLUE'),
_('Enables Gajim to be location-aware, if the user decides '
diff --git a/gajim/gtk/main.py b/gajim/gtk/main.py
index 74c2f738e..1a8500ca3 100644
--- a/gajim/gtk/main.py
+++ b/gajim/gtk/main.py
@@ -663,9 +663,7 @@ class MainWindow(Gtk.ApplicationWindow, EventHelper):
@staticmethod
def _check_for_account() -> None:
accounts = app.settings.get_accounts()
- if (not accounts or accounts == ['Local'] and
- not app.settings.get_account_setting('Local', 'active')):
- # Either no account configured or only disabled Local account
+ if not accounts:
def _open_wizard():
open_window('AccountWizard')
diff --git a/gajim/gtk/menus.py b/gajim/gtk/menus.py
index 08ac32643..94b374096 100644
--- a/gajim/gtk/menus.py
+++ b/gajim/gtk/menus.py
@@ -226,9 +226,7 @@ def build_accounts_menu() -> None:
acc_menu.append_item(add_contact_item)
for acc in accounts_list:
label = escape_mnemonic(app.get_account_label(acc))
- if acc != 'Local':
- acc_menu.append_submenu(
- label, get_account_menu(acc))
+ acc_menu.append_submenu(label, get_account_menu(acc))
else:
acc_menu = get_account_menu(accounts_list[0])
modify_account_item = Gio.MenuItem.new(_('_Modify Account…'),
@@ -243,7 +241,6 @@ def build_accounts_menu() -> None:
def get_encryption_menu(control_id: str,
control_type: ControlType,
- zeroconf: bool = False
) -> Optional[Gio.Menu]:
menu = Gio.Menu()
menu.append(
@@ -255,9 +252,6 @@ def get_encryption_menu(control_id: str,
if control_type.is_privatechat:
if not hasattr(plugin, 'allow_privatechat'):
continue
- if zeroconf:
- if not hasattr(plugin, 'allow_zeroconf'):
- continue
menu_action = f'win.set-encryption-{control_id}::{name}'
menu.append(name, menu_action)
if menu.get_n_items() == 1:
diff --git a/gajim/gtk/status_icon.py b/gajim/gtk/status_icon.py
index ba22278b8..b3e770f52 100644
--- a/gajim/gtk/status_icon.py
+++ b/gajim/gtk/status_icon.py
@@ -183,7 +183,8 @@ class GtkMenuBackend(EventHelper):
if not app.settings.get('main_window_skip_taskbar'):
app.window.set_property('skip-taskbar-hint', False)
- app.window.present_with_time(Gtk.get_current_event_time())
+ # app.window.present_with_time(Gtk.get_current_event_time())
+ app.app.activate()
@staticmethod
def _on_preferences(_widget: Gtk.MenuItem) -> None:
diff --git a/gajim/gtk/zeroconf_vcard.py b/gajim/gtk/zeroconf_vcard.py
deleted file mode 100644
index 7b3d4ddf6..000000000
--- a/gajim/gtk/zeroconf_vcard.py
+++ /dev/null
@@ -1,111 +0,0 @@
-# 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 gi.repository import Gtk
-from gi.repository import Gdk
-
-from gajim.common import helpers
-from gajim.common import app
-from gajim.common.i18n import _
-
-from gajim.gui.builder import get_builder
-
-
-class ZeroconfVcardWindow:
- def __init__(self, contact, account, is_fake=False):
- # the contact variable is the jid if vcard is true
- self.xml = get_builder('zeroconf_information_window.ui')
- self.window = self.xml.get_object('zeroconf_information_window')
-
- self.contact = contact
- self.account = account
- self.is_fake = is_fake
-
- self.fill_contact_page()
- self.fill_personal_page()
-
- self.xml.connect_signals(self)
- self.window.show_all()
-
- def on_zeroconf_information_window_destroy(self, widget):
- del app.interface.instances[self.account]['infos'][self.contact.jid]
-
- def on_zeroconf_information_window_key_press_event(self, widget, event):
- if event.keyval == Gdk.KEY_Escape:
- self.window.destroy()
-
- def set_value(self, entry_name, value):
- try:
- if value and entry_name == 'URL_label':
- widget = Gtk.LinkButton(uri=value, label=value)
- widget.set_alignment(0, 0)
- table = self.xml.get_object('personal_info_table')
- table.attach(widget, 1, 3, 2, 1)
- else:
- self.xml.get_object(entry_name).set_text(value)
- except AttributeError:
- pass
-
- def fill_status_label(self):
- if self.xml.get_object('information_notebook').get_n_pages() < 2:
- return
- contact_list = app.contacts.get_contacts(self.account, self.contact.jid)
- # stats holds show and status message
- stats = ''
- one = True # Are we adding the first line ?
- if contact_list:
- for c in contact_list:
- if not one:
- stats += '\n'
- stats += helpers.get_uf_show(c.show)
- if c.status:
- stats += ': ' + c.status
- one = False
- else: # Maybe gc_vcard ?
- stats = helpers.get_uf_show(self.contact.show)
- if self.contact.status:
- stats += ': ' + self.contact.status
- status_label = self.xml.get_object('status_label')
- status_label.set_text(stats)
- status_label.set_tooltip_text(stats)
-
- def fill_contact_page(self):
- self.xml.get_object('local_jid_label').set_text(self.contact.jid)
-
- resources = '%s (%s)' % (self.contact.resource, str(
- self.contact.priority))
- uf_resources = self.contact.resource + _(' resource with priority ')\
- + str(self.contact.priority)
- if not self.contact.status:
- self.contact.status = ''
-
- self.xml.get_object('resource_prio_label').set_text(resources)
- resource_prio_label_eventbox = self.xml.get_object(
- 'resource_prio_label_eventbox')
- resource_prio_label_eventbox.set_tooltip_text(uf_resources)
-
- self.fill_status_label()
-
- def fill_personal_page(self):
- contact = app.connections[app.ZEROCONF_ACC_NAME].roster.getItem(self.contact.jid)
- for key in ('1st', 'last', 'jid', 'email'):
- if key not in contact['txt_dict']:
- contact['txt_dict'][key] = ''
- self.xml.get_object('first_name_label').set_text(contact['txt_dict']['1st'])
- self.xml.get_object('last_name_label').set_text(contact['txt_dict']['last'])
- self.xml.get_object('jabber_id_label').set_text(contact['txt_dict']['jid'])
- self.xml.get_object('email_label').set_text(contact['txt_dict']['email'])
-
- def on_close_button_clicked(self, widget):
- self.window.destroy()
diff --git a/gajim/gui_interface.py b/gajim/gui_interface.py
index fa1593994..f8447d3fe 100644
--- a/gajim/gui_interface.py
+++ b/gajim/gui_interface.py
@@ -71,7 +71,6 @@ from gajim.common.events import FileProgress
from gajim.common.events import FileError
from gajim.common.events import MucAdded
from gajim.common.events import PasswordRequired
-from gajim.common.zeroconf import connection_zeroconf
from gajim.common.helpers import ask_for_status_message
from gajim.common.structs import OutgoingMessage
from gajim.common.i18n import _
@@ -88,7 +87,6 @@ from gajim.common.modules.httpupload import HTTPFileTransfer
from gajim.gui.dialogs import DialogButton
from gajim.gui.dialogs import ErrorDialog
from gajim.gui.dialogs import ConfirmationDialog
-from gajim.gui.dialogs import InputDialog
from gajim.gui.filechoosers import FileChooserDialog
from gajim.gui.filetransfer import FileTransfersWindow
from gajim.gui.menus import build_accounts_menu
@@ -112,11 +110,6 @@ class Interface:
self.preview_manager = PreviewManager()
- for account in app.settings.get_accounts():
- if app.settings.get_account_setting(account, 'is_zeroconf'):
- app.ZEROCONF_ACC_NAME = account
- break
-
app.idlequeue = idlequeue.get_idlequeue()
# resolve and keep current record of resolved hosts
app.socks5queue = socks5.SocksQueue(
@@ -132,15 +125,8 @@ class Interface:
self._create_core_handlers_list()
self._register_core_handlers()
- # self.create_zeroconf_default_config()
- # if app.settings.get_account_setting(app.ZEROCONF_ACC_NAME, 'active') \
- # and app.is_installed('ZEROCONF'):
- # app.connections[app.ZEROCONF_ACC_NAME] = \
- # connection_zeroconf.ConnectionZeroconf(app.ZEROCONF_ACC_NAME)
-
for account in app.settings.get_accounts():
- if (not app.settings.get_account_setting(account, 'is_zeroconf') and
- app.settings.get_account_setting(account, 'active')):
+ if app.settings.get_account_setting(account, 'active'):
client = Client(account)
app.connections[account] = client
app.ged.register_event_handler(
@@ -171,7 +157,6 @@ class Interface:
'http-auth-received': [self.handle_event_http_auth],
'password-required': [self.handle_event_password_required],
'client-cert-passphrase': [self.handle_event_client_cert_passphrase],
- 'zeroconf-name-conflict': [self.handle_event_zc_name_conflict],
'signed-in': [self.handle_event_signed_in],
'presence-received': [self.handle_event_presence],
'our-show': [self.handle_event_status],
@@ -264,29 +249,6 @@ class Interface:
open_window('PasswordDialog', account=event.conn.name, event=event)
@staticmethod
- def handle_event_zc_name_conflict(event):
- def _on_ok(new_name):
- app.settings.set_account_setting(event.conn.name, 'name', new_name)
- event.conn.username = new_name
- event.conn.change_status(
- event.conn.status, event.conn.status_message)
-
- def _on_cancel(*args):
- event.conn.change_status('offline', '')
-
- InputDialog(
- _('Username Conflict'),
- _('Username Conflict'),
- _('Please enter a new username for your local account'),
- [DialogButton.make('Cancel',
- callback=_on_cancel),
- DialogButton.make('Accept',
- text=_('_OK'),
- callback=_on_ok)],
- input_str=event.alt_name,
- transient_for=app.window).show()
-
- @staticmethod
def handle_event_signed_in(event):
"""
SIGNED_IN event is emitted when we sign in, so handle it
@@ -692,11 +654,7 @@ class Interface:
window.add_account(account)
def enable_account(self, account: str) -> None:
- if account == app.ZEROCONF_ACC_NAME:
- app.connections[account] = connection_zeroconf.ConnectionZeroconf(
- account)
- else:
- app.connections[account] = Client(account)
+ app.connections[account] = Client(account)
app.plugin_manager.register_modules_for_account(
app.connections[account])
@@ -713,10 +671,7 @@ class Interface:
app.automatic_rooms[account] = {}
app.newly_added[account] = []
app.to_be_removed[account] = []
- if account == app.ZEROCONF_ACC_NAME:
- app.nicks[account] = app.ZEROCONF_ACC_NAME
- else:
- app.nicks[account] = app.settings.get_account_setting(account,
+ app.nicks[account] = app.settings.get_account_setting(account,
'name')
app.block_signed_in_notifications[account] = True
@@ -746,8 +701,6 @@ class Interface:
app.ged.raise_event(AccountDisabled(account=account))
- if account == app.ZEROCONF_ACC_NAME:
- app.connections[account].disable_account()
app.connections[account].cleanup()
del app.connections[account]
del self.instances[account]
@@ -942,36 +895,6 @@ class Interface:
view.updateNamespace({'gajim': app})
app.ipython_window = window
- def create_zeroconf_default_config(self) -> None:
- if app.settings.get_account_setting(app.ZEROCONF_ACC_NAME, 'name'):
- return
- log.info('Creating zeroconf account')
- app.settings.add_account(app.ZEROCONF_ACC_NAME)
- app.settings.set_account_setting(app.ZEROCONF_ACC_NAME,
- 'autoconnect',
- True)
- app.settings.set_account_setting(app.ZEROCONF_ACC_NAME,
- 'no_log_for',
- '')
- app.settings.set_account_setting(app.ZEROCONF_ACC_NAME,
- 'password',
- 'zeroconf')
- app.settings.set_account_setting(app.ZEROCONF_ACC_NAME,
- 'sync_with_global_status',
- True)
- app.settings.set_account_setting(app.ZEROCONF_ACC_NAME,
- 'custom_port',
- 5298)
- app.settings.set_account_setting(app.ZEROCONF_ACC_NAME,
- 'is_zeroconf',
- True)
- app.settings.set_account_setting(app.ZEROCONF_ACC_NAME,
- 'use_ft_proxies',
- False)
- app.settings.set_account_setting(app.ZEROCONF_ACC_NAME,
- 'active',
- False)
-
def run(self, _application: Gtk.Application) -> None:
# Creating plugin manager
from gajim import plugins
diff --git a/gajim/plugins/pluginmanager.py b/gajim/plugins/pluginmanager.py
index 99afcd595..cc0223756 100644
--- a/gajim/plugins/pluginmanager.py
+++ b/gajim/plugins/pluginmanager.py
@@ -500,8 +500,6 @@ class PluginManager(metaclass=Singleton):
return
for con in app.connections.values():
for module in plugin.modules:
- if not module.zeroconf and con.name == 'Local':
- continue
instance, name = module.get_instance(con)
modules.register_single_module(con, instance, name)
@@ -613,8 +611,6 @@ class PluginManager(metaclass=Singleton):
for module in plugin.modules:
instance, name = module.get_instance(con)
- if not module.zeroconf and con.name == 'Local':
- continue
modules.register_single_module(con, instance, name)
for handler in instance.handlers:
diff --git a/gajim/remote_control.py b/gajim/remote_control.py
index c016a499e..2c36b13ad 100644
--- a/gajim/remote_control.py
+++ b/gajim/remote_control.py
@@ -797,16 +797,11 @@ class GajimRemote(Server):
accounts = app.connections.keys()
for acct in accounts:
if app.account_is_available(acct):
- if not app.connections[acct].is_zeroconf:
- account = acct
- break
+ account = acct
+ break
if not account:
return
- if app.connections[account].is_zeroconf:
- # zeroconf not support groupchats
- return
-
app.interface.show_add_join_groupchat(account,
room_jid,
password=password)
diff --git a/mypy.ini b/mypy.ini
index 5742d627a..44bedab0c 100644
--- a/mypy.ini
+++ b/mypy.ini
@@ -19,9 +19,6 @@ ignore_missing_imports = True
[mypy-OpenSSL.*]
ignore_missing_imports = True
-[mypy-pybonjour.*]
-ignore_missing_imports = True
-
[mypy-encodings.*]
ignore_missing_imports = True
diff --git a/win/_base.sh b/win/_base.sh
index 9f1a4514f..d4aef121e 100644
--- a/win/_base.sh
+++ b/win/_base.sh
@@ -107,7 +107,6 @@ function install_deps {
PIP_REQUIREMENTS="\
git+https://dev.gajim.org/gajim/python-nbxmpp.git
-git+https://dev.gajim.org/lovetox/pybonjour-python3.git
keyring
python-gnupg
python-axolotl
diff --git a/win/dev_env.sh b/win/dev_env.sh
index c09a89a16..66ac4ccc3 100644
--- a/win/dev_env.sh
+++ b/win/dev_env.sh
@@ -26,7 +26,6 @@ function main {
PIP_REQUIREMENTS="\
git+https://dev.gajim.org/gajim/python-nbxmpp.git
-git+https://dev.gajim.org/lovetox/pybonjour-python3.git
python-axolotl
python-gnupg
keyring