diff --git a/src/common/resolver.py b/src/common/resolver.py index bc32de56e..c215b5e21 100644 --- a/src/common/resolver.py +++ b/src/common/resolver.py @@ -20,14 +20,10 @@ import sys import os import re +import logging +log = logging.getLogger('gajim.c.resolver') -from xmpp.idlequeue import * - -# needed for nslookup -if os.name == 'nt': - from subprocess import * # python24 only. we ask this for Windows -elif os.name == 'posix': - import fcntl +from xmpp.idlequeue import IdleCommand # it is good to check validity of arguments, when calling system commands ns_type_pattern = re.compile('^[a-z]+$') @@ -35,14 +31,13 @@ ns_type_pattern = re.compile('^[a-z]+$') # match srv host_name host_pattern = re.compile('^[a-z0-9\-._]*[a-z0-9]\.[a-z]{2,}$') -USE_LIBASYNCNS = False - try: #raise ImportError("Manually disabled libasync") import libasyncns USE_LIBASYNCNS = True log.info("libasyncns-python loaded") except ImportError: + USE_LIBASYNCNS = False log.debug("Import of libasyncns-python failed, getaddrinfo will block", exc_info=True) # FIXME: Remove these prints before release, replace with a warning dialog. @@ -98,11 +93,11 @@ class CommonResolver(): def start_resolve(self, host, type): pass - +# FIXME: API usage is not consistent! This one requires that process is called class LibAsyncNSResolver(CommonResolver): ''' - Asynchronous resolver using libasyncns-python. process() method has to be called - in order to proceed the pending requests. + Asynchronous resolver using libasyncns-python. process() method has to be + called in order to proceed the pending requests. Based on patch submitted by Damien Thebault. ''' def __init__(self): @@ -126,7 +121,6 @@ class LibAsyncNSResolver(CommonResolver): CommonResolver._on_ready(self, host, type, result_list) - def process(self): try: self.asyncns.wait(False) @@ -157,6 +151,7 @@ class LibAsyncNSResolver(CommonResolver): resq.userdata['callback'](resq.userdata['dname'], rl) return True + class NSLookupResolver(CommonResolver): ''' Asynchronous DNS resolver calling nslookup. Processing of pending requests @@ -264,112 +259,14 @@ class NSLookupResolver(CommonResolver): result_list = self.parse_srv_result(host, result) CommonResolver._on_ready(self, host, type, result_list) - def start_resolve(self, host, type): ''' spawn new nslookup process and start waiting for results ''' ns = NsLookup(self._on_ready, host, type) ns.set_idlequeue(self.idlequeue) ns.commandtimeout = 10 ns.start() - -# TODO: move IdleCommand class in other file, maybe helpers ? -class IdleCommand(IdleObject): - def __init__(self, on_result): - # how long (sec.) to wait for result ( 0 - forever ) - # it is a class var, instead of a constant and we can override it. - self.commandtimeout = 0 - # when we have some kind of result (valid, ot not) we call this handler - self.result_handler = on_result - # if it is True, we can safetely execute the command - self.canexecute = True - self.idlequeue = None - self.result ='' - - def set_idlequeue(self, idlequeue): - self.idlequeue = idlequeue - - def _return_result(self): - if self.result_handler: - self.result_handler(self.result) - self.result_handler = None - - def _compose_command_args(self): - return ['echo', 'da'] - - def _compose_command_line(self): - ''' return one line representation of command and its arguments ''' - return reduce(lambda left, right: left + ' ' + right, self._compose_command_args()) - - def wait_child(self): - if self.pipe.poll() is None: - # result timeout - if self.endtime < self.idlequeue.current_time(): - self._return_result() - self.pipe.stdout.close() - self.pipe.stdin.close() - else: - # child is still active, continue to wait - self.idlequeue.set_alarm(self.wait_child, 0.1) - else: - # child has quit - self.result = self.pipe.stdout.read() - self._return_result() - self.pipe.stdout.close() - self.pipe.stdin.close() - def start(self): - if not self.canexecute: - self.result = '' - self._return_result() - return - if os.name == 'nt': - self._start_nt() - elif os.name == 'posix': - self._start_posix() - - def _start_nt(self): - # if gajim is started from noninteraactive shells stdin is closed and - # cannot be forwarded, so we have to keep it open - self.pipe = Popen(self._compose_command_args(), stdout=PIPE, - bufsize = 1024, shell = True, stderr = STDOUT, stdin = PIPE) - if self.commandtimeout >= 0: - self.endtime = self.idlequeue.current_time() + self.commandtimeout - self.idlequeue.set_alarm(self.wait_child, 0.1) - - def _start_posix(self): - self.pipe = os.popen(self._compose_command_line()) - self.fd = self.pipe.fileno() - fcntl.fcntl(self.pipe, fcntl.F_SETFL, os.O_NONBLOCK) - self.idlequeue.plug_idle(self, False, True) - if self.commandtimeout >= 0: - self.idlequeue.set_read_timeout(self.fd, self.commandtimeout) - - def end(self): - self.idlequeue.unplug_idle(self.fd) - try: - self.pipe.close() - except: - pass - - def pollend(self): - self.idlequeue.remove_timeout(self.fd) - self.end() - self._return_result() - - def pollin(self): - try: - res = self.pipe.read() - except Exception, e: - res = '' - if res == '': - return self.pollend() - else: - self.result += res - - def read_timeout(self): - self.end() - self._return_result() - + class NsLookup(IdleCommand): def __init__(self, on_result, host='_xmpp-client', type='srv'): IdleCommand.__init__(self, on_result) @@ -378,11 +275,11 @@ class NsLookup(IdleCommand): self.type = type.lower() if not host_pattern.match(self.host): # invalid host name - print >> sys.stderr, 'Invalid host: %s' % self.host + log.error('Invalid host: %s' % self.host) self.canexecute = False return if not ns_type_pattern.match(self.type): - print >> sys.stderr, 'Invalid querytype: %s' % self.type + log.error('Invalid querytype: %s' % self.type) self.canexecute = False return @@ -396,15 +293,12 @@ class NsLookup(IdleCommand): # below lines is on how to use API and assist in testing if __name__ == '__main__': - if os.name == 'posix': - idlequeue = IdleQueue() - elif os.name == 'nt': - idlequeue = SelectIdleQueue() - # testing Resolver class import gobject import gtk + from xmpp import idlequeue - resolver = Resolver(idlequeue) + idlequeue = idlequeue.get_idlequeue() + resolver = get_resolver(idlequeue) def clicked(widget): global resolver diff --git a/src/common/xmpp/idlequeue.py b/src/common/xmpp/idlequeue.py index b38118495..378e27e57 100644 --- a/src/common/xmpp/idlequeue.py +++ b/src/common/xmpp/idlequeue.py @@ -20,12 +20,19 @@ import os import select import logging log = logging.getLogger('gajim.c.x.idlequeue') + +# needed for get_idleqeue try: import gobject HAVE_GOBJECT = True except ImportError: HAVE_GOBJECT = False +# needed for idlecommand +if os.name == 'nt': + from subprocess import * # python24 only. we ask this for Windows +elif os.name == 'posix': + import fcntl FLAG_WRITE = 20 # write only FLAG_READ = 19 # read only @@ -36,6 +43,7 @@ PENDING_READ = 3 # waiting read event PENDING_WRITE = 4 # waiting write event IS_CLOSED = 16 # channel closed + def get_idlequeue(): ''' Get an appropriate idlequeue ''' if os.name == 'nt': @@ -74,6 +82,110 @@ class IdleObject: pass +class IdleCommand(IdleObject): + ''' + Can be subclassed to execute commands asynchronously by the idlequeue. + Result will be optained via file descriptor of created pipe + ''' + def __init__(self, on_result): + IdleObject.__init__(self) + # how long (sec.) to wait for result ( 0 - forever ) + # it is a class var, instead of a constant and we can override it. + self.commandtimeout = 0 + # when we have some kind of result (valid, ot not) we call this handler + self.result_handler = on_result + # if it is True, we can safetely execute the command + self.canexecute = True + self.idlequeue = None + self.result ='' + + def set_idlequeue(self, idlequeue): + self.idlequeue = idlequeue + + def _return_result(self): + if self.result_handler: + self.result_handler(self.result) + self.result_handler = None + + def _compose_command_args(self): + return ['echo', 'da'] + + def _compose_command_line(self): + ''' return one line representation of command and its arguments ''' + return reduce(lambda left, right: left + ' ' + right, + self._compose_command_args()) + + def wait_child(self): + if self.pipe.poll() is None: + # result timeout + if self.endtime < self.idlequeue.current_time(): + self._return_result() + self.pipe.stdout.close() + self.pipe.stdin.close() + else: + # child is still active, continue to wait + self.idlequeue.set_alarm(self.wait_child, 0.1) + else: + # child has quit + self.result = self.pipe.stdout.read() + self._return_result() + self.pipe.stdout.close() + self.pipe.stdin.close() + + def start(self): + if not self.canexecute: + self.result = '' + self._return_result() + return + if os.name == 'nt': + self._start_nt() + elif os.name == 'posix': + self._start_posix() + + def _start_nt(self): + # if gajim is started from noninteraactive shells stdin is closed and + # cannot be forwarded, so we have to keep it open + self.pipe = Popen(self._compose_command_args(), stdout=PIPE, + bufsize = 1024, shell = True, stderr = STDOUT, stdin = PIPE) + if self.commandtimeout >= 0: + self.endtime = self.idlequeue.current_time() + self.commandtimeout + self.idlequeue.set_alarm(self.wait_child, 0.1) + + def _start_posix(self): + self.pipe = os.popen(self._compose_command_line()) + self.fd = self.pipe.fileno() + fcntl.fcntl(self.pipe, fcntl.F_SETFL, os.O_NONBLOCK) + self.idlequeue.plug_idle(self, False, True) + if self.commandtimeout >= 0: + self.idlequeue.set_read_timeout(self.fd, self.commandtimeout) + + def end(self): + self.idlequeue.unplug_idle(self.fd) + try: + self.pipe.close() + except: + pass + + def pollend(self): + self.idlequeue.remove_timeout(self.fd) + self.end() + self._return_result() + + def pollin(self): + try: + res = self.pipe.read() + except Exception, e: + res = '' + if res == '': + return self.pollend() + else: + self.result += res + + def read_timeout(self): + self.end() + self._return_result() + + class IdleQueue: ''' IdleQueue provide three distinct time based features. Uses select.poll()