gajim-plural/gajim/common/resolver.py

158 lines
5.2 KiB
Python

## common/resolver.py
##
## Copyright (C) 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 sys
import logging
import functools
log = logging.getLogger('gajim.c.resolver')
if __name__ == '__main__':
sys.path.append('..')
from gajim.common import i18n
from gajim.common import configpaths
configpaths.gajimpaths.init(None)
from gi.repository import Gio, GLib
def get_resolver(idlequeue):
return GioResolver()
class CommonResolver():
def __init__(self):
# dict {"host+type" : list of records}
self.resolved_hosts = {}
# dict {"host+type" : list of callbacks}
self.handlers = {}
def resolve(self, host, on_ready, type_='srv'):
host = host.lower()
log.debug('resolve %s type=%s' % (host, type_))
assert(type_ in ['srv', 'txt'])
if not host:
# empty host, return empty list of srv records
on_ready([])
return
if host + type_ in self.resolved_hosts:
# host is already resolved, return cached values
log.debug('%s already resolved: %s' % (host,
self.resolved_hosts[host + type_]))
on_ready(host, self.resolved_hosts[host + type_])
return
if host + type_ in self.handlers:
# host is about to be resolved by another connection,
# attach our callback
log.debug('already resolving %s' % host)
self.handlers[host + type_].append(on_ready)
else:
# host has never been resolved, start now
log.debug('Starting to resolve %s using %s' % (host, self))
self.handlers[host + type_] = [on_ready]
self.start_resolve(host, type_)
def _on_ready(self, host, type_, result_list):
# practically it is impossible to be the opposite, but who knows :)
host = host.lower()
log.debug('Resolving result for %s: %s' % (host, result_list))
if host + type_ not in self.resolved_hosts:
self.resolved_hosts[host + type_] = result_list
if host + type_ in self.handlers:
for callback in self.handlers[host + type_]:
callback(host, result_list)
del(self.handlers[host + type_])
def start_resolve(self, host, type_):
pass
class GioResolver(CommonResolver):
"""
Asynchronous resolver using GIO. process() method has to be
called in order to proceed the pending requests.
"""
def __init__(self):
super().__init__()
self.gio_resolver = Gio.Resolver.get_default()
def start_resolve(self, host, type_):
if type_ == 'txt':
# TXT record resolution isn't used anywhere at the moment so
# implementing it here isn't urgent
raise NotImplementedError("Gio resolver does not currently implement TXT records")
else:
callback = functools.partial(self._on_ready_srv, host)
type_ = Gio.ResolverRecordType.SRV
resq = self.gio_resolver.lookup_records_async(host, type_, None, callback)
def _on_ready_srv(self, host, source_object, result):
try:
variant_results = source_object.lookup_records_finish(result)
except GLib.Error as e:
if e.domain == 'g-resolver-error-quark':
result_list = []
log.warning("Could not resolve host: %s", e.message)
else:
raise
else:
result_list = [
{
'weight': weight,
'prio': prio,
'port': port,
'host': host,
}
for prio, weight, port, host
in variant_results
]
super()._on_ready(host, 'srv', result_list)
# below lines is on how to use API and assist in testing
if __name__ == '__main__':
from gi.repository import Gtk
from nbxmpp import idlequeue
idlequeue = idlequeue.get_idlequeue()
resolver = get_resolver(idlequeue)
def clicked(widget):
global resolver
host = text_view.get_text()
def on_result(host, result_array):
print('Result:\n' + repr(result_array))
resolver.resolve(host, on_result)
win = Gtk.Window()
win.set_border_width(6)
win.connect('remove', Gtk.main_quit)
text_view = Gtk.Entry()
text_view.set_text('_xmpp-client._tcp.jabber.org')
hbox = Gtk.HBox()
hbox.set_spacing(3)
but = Gtk.Button(' Lookup SRV ')
hbox.pack_start(text_view, 5, True, 0)
hbox.pack_start(but, 0, True, 0)
but.connect('clicked', clicked)
win.add(hbox)
win.show_all()
GLib.timeout_add(200, idlequeue.process)
Gtk.main()