Esempio n. 1
0
class Collector(BaseHandler):

    """
    The Collector handler allows for collecting a set of stanzas
    that match a given pattern. Unlike the Waiter handler, a
    Collector does not block execution, and will continue to
    accumulate matching stanzas until told to stop.

    :param string name: The name of the handler.
    :param matcher: A :class:`~sleekxmpp.xmlstream.matcher.base.MatcherBase`
                    derived object for matching stanza objects.
    :param stream: The :class:`~sleekxmpp.xmlstream.xmlstream.XMLStream`
                   instance this handler should monitor.
    """

    def __init__(self, name, matcher, stream=None):
        BaseHandler.__init__(self, name, matcher, stream=stream)
        self._payload = Queue()

    def prerun(self, payload):
        """Store the matched stanza when received during processing.

        :param payload: The matched
            :class:`~sleekxmpp.xmlstream.stanzabase.ElementBase` object.
        """
        self._payload.put(payload)

    def run(self, payload):
        """Do not process this handler during the main event loop."""
        pass

    def stop(self):
        """
        Stop collection of matching stanzas, and return the ones that
        have been stored so far.
        """
        self._destroy = True
        results = []
        try:
            while True:
                results.append(self._payload.get(False))
        except QueueEmpty:
            pass

        self.stream().remove_handler(self.name)
        return results
Esempio n. 2
0
class bot(sleekxmpp.ClientXMPP):
    '''
    Bot used to send and receive messages
    '''
    def __init__(self, master, jid_password):
        self.master = master
        self.buffer_size = 0  #number of bytes of data waiting to be sent
        self.buffer_queue = Queue()
        self.buffer_size_lock = threading.Lock()
        self.num_clients = 0  #number of clients sockets using this bot
        self.num_clients_lock = threading.Lock()
        self.__failed_send_stanza = None  #some sleekxmpp thing for resending stuff
        sleekxmpp.ClientXMPP.__init__(self, *jid_password)

        # gmail xmpp server is actually at talk.google.com
        if jid_password[0].find("@gmail.com") != -1:
            self.connect_address = ("talk.google.com", 5222)
        else:
            self.connect_address = None

        # event handlers are sleekxmpp's way of dealing with important xml tags it receives
        self.add_event_handler("session_start",
                               lambda event: self.session_start())
        self.add_event_handler("disconnected",
                               lambda event: self.disconnected())

        self.register_plugin('xep_0199')  # Ping

    def buffer_message(self, data):
        '''
        set the karma and time_last_sent
        karma_lock should have been acquired before calling this function
        '''
        self.buffer_queue.put(data)
        self.buffer_size += len(data)
        self.buffer_size_lock.release()

    def get_buffer_size(self):
        self.buffer_size_lock.acquire()
        return self.buffer_size

    def get_num_clients(self):
        self.num_clients_lock.acquire()
        return self.num_clients

    def register_hexchat_handlers(self):
        '''these handle the custom iq stanzas'''

        self.register_handler(
            callback.Callback('Connect Handler',
                              stanzapath.StanzaPath('iq@type=set/connect'),
                              self.master.connect_handler))
        self.register_handler(
            callback.Callback(
                'Connect Message Handler',
                stanzapath.StanzaPath('message@type=chat/connect'),
                self.master.connect_handler))
        self.register_handler(
            callback.Callback(
                'Connect Ack Handler',
                stanzapath.StanzaPath('iq@type=result/connect_ack'),
                self.master.connect_ack_handler))
        self.register_handler(
            callback.Callback('Data Handler',
                              stanzapath.StanzaPath('iq@type=set/packet'),
                              self.master.data_handler))
        self.register_handler(
            callback.Callback('Disconnect Handler',
                              stanzapath.StanzaPath('iq@type=set/disconnect'),
                              self.master.disconnect_handler))
        self.register_handler(
            callback.Callback(
                'Disconnect Error Message Handler',
                stanzapath.StanzaPath('message@type=chat/disconnect_error'),
                self.master.disconnect_error_handler))
        self.register_handler(
            callback.Callback(
                'Disconnect Error Iq Handler',
                stanzapath.StanzaPath('iq@type=set/disconnect_error'),
                self.master.disconnect_error_handler))

        self.register_handler(
            callback.Callback('IQ Error Handler',
                              stanzapath.StanzaPath('iq@type=error/error'),
                              self.master.error_handler))
        self.register_handler(
            callback.Callback(
                'Message Error Handler',
                stanzapath.StanzaPath('message@type=error/error'),
                self.master.error_handler))

    ### session management mathods:

    def boot(self, process=True):
        if self.connect(self.connect_address):
            if process:
                self.process()
        else:
            raise (Exception(self.boundjid.bare + " could not connect"))

    def session_start(self):
        """Called when the bot connects and establishes a session with the XMPP server."""

        # XMPP spec says that we should broadcast our presence when we connect.
        self.send_presence()

    def disconnected(self):
        '''
        Called when the bot disconnects from the XMPP server.
        Try to reconnect.
        '''

        logging.warning("XMPP chat server disconnected")
        logging.debug("Trying to reconnect")
        self.boot(False)
        self.send_presence()

    def _send_thread(self):
        '''
         modifed version of sleekxmpp's _send_thread
         that will not send faster than THROUGHPUT
        '''

        try:
            while not self.stop.is_set():
                while not self.stop.is_set() and \
                      not self.session_started_event.is_set():
                    self.session_started_event.wait(timeout=0.1)
                if self.__failed_send_stanza is not None:
                    data = self.__failed_send_stanza
                    self.__failed_send_stanza = None
                else:
                    try:
                        data = self.buffer_queue.get(True, 1)
                        was_buffered = True
                    except QueueEmpty:
                        try:
                            data = self.send_queue.get(True, 0.0)
                            was_buffered = False
                        except QueueEmpty:
                            continue

                logging.debug("SEND: %s", data)
                enc_data = data.encode('utf-8')
                total = len(enc_data)
                sent = 0
                count = 0
                tries = 0
                try:
                    with self.send_lock:
                        while sent < total and not self.stop.is_set() and \
                              self.session_started_event.is_set():
                            try:
                                num_bytes = self.socket.send(enc_data[sent:])
                                sent += num_bytes
                                count += 1
                                '''
                                throttling code
                                that prevents data from being sent
                                faster than THROUGHPUT
                                '''
                                time.sleep(num_bytes / THROUGHPUT)
                                if was_buffered:
                                    with self.buffer_size_lock:
                                        self.buffer_size -= num_bytes

                            except ssl.SSLError as serr:
                                if tries >= self.ssl_retry_max:
                                    logging.debug(
                                        'SSL error: max retries reached')
                                    self.exception(serr)
                                    logging.warning("Failed to send %s", data)
                                    if not self.stop.is_set():
                                        self.disconnect(self.auto_reconnect,
                                                        send_close=False)
                                    logging.warning(
                                        'SSL write error: retrying')
                                if not self.stop.is_set():
                                    time.sleep(self.ssl_retry_delay)
                                tries += 1
                    if count > 1:
                        logging.debug('SENT: %d chunks', count)

                    if was_buffered:
                        self.buffer_queue.task_done()
                    else:
                        self.send_queue.task_done()

                except (Socket.error, ssl.SSLError) as serr:
                    self.event('socket_error', serr, direct=True)
                    logging.warning("Failed to send %s", data)
                    if not self.stop.is_set():
                        self.__failed_send_stanza = data
                        self._end_thread('send')
                        self.disconnect(self.auto_reconnect, send_close=False)
                        return
        except Exception as ex:
            logging.exception('Unexpected error in send thread: %s', ex)
            self.exception(ex)
            if not self.stop.is_set():
                self._end_thread('send')
                self.disconnect(self.auto_reconnect)
                return

        self._end_thread('send')
Esempio n. 3
0
class Waiter(BaseHandler):
    """
    The Waiter handler allows an event handler to block until a
    particular stanza has been received. The handler will either be
    given the matched stanza, or ``False`` if the waiter has timed out.

    :param string name: The name of the handler.
    :param matcher: A :class:`~sleekxmpp.xmlstream.matcher.base.MatcherBase`
                    derived object for matching stanza objects.
    :param stream: The :class:`~sleekxmpp.xmlstream.xmlstream.XMLStream`
                   instance this handler should monitor.
    """
    def __init__(self, name, matcher, stream=None):
        BaseHandler.__init__(self, name, matcher, stream=stream)
        self._payload = Queue()

    def prerun(self, payload):
        """Store the matched stanza when received during processing.

        :param payload: The matched
            :class:`~sleekxmpp.xmlstream.stanzabase.ElementBase` object.
        """
        self._payload.put(payload)

    def run(self, payload):
        """Do not process this handler during the main event loop."""
        pass

    def wait(self, timeout=None):
        """Block an event handler while waiting for a stanza to arrive.

        Be aware that this will impact performance if called from a
        non-threaded event handler.

        Will return either the received stanza, or ``False`` if the
        waiter timed out.

        :param int timeout: The number of seconds to wait for the stanza
            to arrive. Defaults to the the stream's
            :class:`~sleekxmpp.xmlstream.xmlstream.XMLStream.response_timeout`
            value.
        """
        if timeout is None:
            timeout = self.stream().response_timeout

        elapsed_time = 0
        stanza = False
        while elapsed_time < timeout and not self.stream().stop.is_set():
            try:
                stanza = self._payload.get(True, 1)
                break
            except QueueEmpty:
                elapsed_time += 1
                if elapsed_time >= timeout:
                    log.warning("Timed out waiting for %s", self.name)
        self.stream().remove_handler(self.name)
        return stanza

    def check_delete(self):
        """Always remove waiters after use."""
        return True
Esempio n. 4
0
class IBBytestream(object):
    def __init__(self,
                 xmpp,
                 sid,
                 block_size,
                 to,
                 ifrom,
                 window_size=1,
                 use_messages=False):
        self.xmpp = xmpp
        self.sid = sid
        self.block_size = block_size
        self.window_size = window_size
        self.use_messages = use_messages

        self.receiver = to
        self.sender = ifrom

        self.send_seq = -1
        self.recv_seq = -1

        self._send_seq_lock = threading.Lock()
        self._recv_seq_lock = threading.Lock()

        self.stream_started = threading.Event()
        self.stream_in_closed = threading.Event()
        self.stream_out_closed = threading.Event()

        self.recv_queue = Queue()

        self.send_window = threading.BoundedSemaphore(value=self.window_size)
        self.window_ids = set()
        self.window_empty = threading.Event()
        self.window_empty.set()

    def send(self, data):
        if not self.stream_started.is_set() or \
               self.stream_out_closed.is_set():
            raise socket.error
        data = data[0:self.block_size]
        self.send_window.acquire()
        with self._send_seq_lock:
            self.send_seq = (self.send_seq + 1) % 65535
            seq = self.send_seq
        if self.use_messages:
            msg = self.xmpp.Message()
            msg['to'] = self.receiver
            msg['from'] = self.sender
            msg['id'] = self.xmpp.new_id()
            msg['ibb_data']['sid'] = self.sid
            msg['ibb_data']['seq'] = seq
            msg['ibb_data']['data'] = data
            msg.send()
            self.send_window.release()
        else:
            iq = self.xmpp.Iq()
            iq['type'] = 'set'
            iq['to'] = self.receiver
            iq['from'] = self.sender
            iq['ibb_data']['sid'] = self.sid
            iq['ibb_data']['seq'] = seq
            iq['ibb_data']['data'] = data
            self.window_empty.clear()
            self.window_ids.add(iq['id'])
            iq.send(block=False, callback=self._recv_ack)
        return len(data)

    def sendall(self, data):
        sent_len = 0
        while sent_len < len(data):
            sent_len += self.send(data[sent_len:])

    def _recv_ack(self, iq):
        self.window_ids.remove(iq['id'])
        if not self.window_ids:
            self.window_empty.set()
        self.send_window.release()
        if iq['type'] == 'error':
            self.close()

    def _recv_data(self, stanza):
        with self._recv_seq_lock:
            new_seq = stanza['ibb_data']['seq']
            if new_seq != (self.recv_seq + 1) % 65535:
                self.close()
                raise XMPPError('unexpected-request')
            self.recv_seq = new_seq

        data = stanza['ibb_data']['data']
        if len(data) > self.block_size:
            self.close()
            raise XMPPError('not-acceptable')

        self.recv_queue.put(data)
        self.xmpp.event('ibb_stream_data', {'stream': self, 'data': data})

        if isinstance(stanza, Iq):
            stanza.reply()
            stanza.send()

    def recv(self, *args, **kwargs):
        return self.read(block=True)

    def read(self, block=True, timeout=None, **kwargs):
        if not self.stream_started.is_set() or \
               self.stream_in_closed.is_set():
            raise socket.error
        if timeout is not None:
            block = True
        try:
            return self.recv_queue.get(block, timeout)
        except:
            return None

    def close(self):
        iq = self.xmpp.Iq()
        iq['type'] = 'set'
        iq['to'] = self.receiver
        iq['from'] = self.sender
        iq['ibb_close']['sid'] = self.sid
        self.stream_out_closed.set()
        iq.send(block=False, callback=lambda x: self.stream_in_closed.set())
        self.xmpp.event('ibb_stream_end', self)

    def _closed(self, iq):
        self.stream_in_closed.set()
        self.stream_out_closed.set()
        while not self.window_empty.is_set():
            log.info('waiting for send window to empty')
            self.window_empty.wait(timeout=1)
        iq.reply()
        iq.send()
        self.xmpp.event('ibb_stream_end', self)

    def makefile(self, *args, **kwargs):
        return self

    def connect(*args, **kwargs):
        return None

    def shutdown(self, *args, **kwargs):
        return None
Esempio n. 5
0
class IBBytestream(object):

    def __init__(self, xmpp, sid, block_size, to, ifrom, window_size=1):
        self.xmpp = xmpp
        self.sid = sid
        self.block_size = block_size
        self.window_size = window_size

        self.receiver = to
        self.sender = ifrom

        self.send_seq = -1
        self.recv_seq = -1

        self._send_seq_lock = threading.Lock()
        self._recv_seq_lock = threading.Lock()

        self.stream_started = threading.Event()
        self.stream_in_closed = threading.Event()
        self.stream_out_closed = threading.Event()

        self.recv_queue = Queue()

        self.send_window = threading.BoundedSemaphore(value=self.window_size)
        self.window_ids = set()
        self.window_empty = threading.Event()
        self.window_empty.set()

    def send(self, data):
        if not self.stream_started.is_set() or \
               self.stream_out_closed.is_set():
            raise socket.error
        data = data[0:self.block_size]
        self.send_window.acquire()
        with self._send_seq_lock:
            self.send_seq = (self.send_seq + 1) % 65535
            seq = self.send_seq
        iq = self.xmpp.Iq()
        iq['type'] = 'set'
        iq['to'] = self.receiver
        iq['from'] = self.sender
        iq['ibb_data']['sid'] = self.sid
        iq['ibb_data']['seq'] = seq
        iq['ibb_data']['data'] = data
        self.window_empty.clear()
        self.window_ids.add(iq['id'])
        iq.send(block=False, callback=self._recv_ack)
        return len(data)

    def sendall(self, data):
        sent_len = 0
        while sent_len < len(data):
            sent_len += self.send(data[sent_len:])

    def _recv_ack(self, iq):
        self.window_ids.remove(iq['id'])
        if not self.window_ids:
            self.window_empty.set()
        self.send_window.release()
        if iq['type'] == 'error':
            self.close()

    def _recv_data(self, iq):
        with self._recv_seq_lock:
            new_seq = iq['ibb_data']['seq']
            if new_seq != (self.recv_seq + 1) % 65535:
                self.close()
                raise XMPPError('unexpected-request')
            self.recv_seq = new_seq

        data = iq['ibb_data']['data']
        if len(data) > self.block_size:
            self.close()
            raise XMPPError('not-acceptable')

        self.recv_queue.put(data)
        self.xmpp.event('ibb_stream_data', {'stream': self, 'data': data})
        iq.reply()
        iq.send()

    def recv(self, *args, **kwargs):
        return self.read(block=True)

    def read(self, block=True, timeout=None, **kwargs):
        if not self.stream_started.is_set() or \
               self.stream_in_closed.is_set():
            raise socket.error
        if timeout is not None:
            block = True
        try:
            return self.recv_queue.get(block, timeout)
        except:
            return None

    def close(self):
        iq = self.xmpp.Iq()
        iq['type'] = 'set'
        iq['to'] = self.receiver
        iq['from'] = self.sender
        iq['ibb_close']['sid'] = self.sid
        self.stream_out_closed.set()
        iq.send(block=False,
                callback=lambda x: self.stream_in_closed.set())
        self.xmpp.event('ibb_stream_end', self)

    def _closed(self, iq):
        self.stream_in_closed.set()
        self.stream_out_closed.set()
        while not self.window_empty.is_set():
            log.info('waiting for send window to empty')
            self.window_empty.wait(timeout=1)
        iq.reply()
        iq.send()
        self.xmpp.event('ibb_stream_end', self)

    def makefile(self, *args, **kwargs):
        return self

    def connect(*args, **kwargs):
        return None

    def shutdown(self, *args, **kwargs):
        return None
Esempio n. 6
0
class bot(sleekxmpp.ClientXMPP):
    '''
    Bot used to send and receive messages
    '''
    def __init__(self, master, jid_password):
        self.master = master
        self.buffer_size = 0 #number of bytes of data waiting to be sent
        self.buffer_queue = Queue()
        self.buffer_size_lock = threading.Lock()
        self.num_clients = 0 #number of clients sockets using this bot
        self.num_clients_lock = threading.Lock()
        self.__failed_send_stanza = None #some sleekxmpp thing for resending stuff
        sleekxmpp.ClientXMPP.__init__(self, *jid_password)

        # gmail xmpp server is actually at talk.google.com
        if jid_password[0].find("@gmail.com") != -1:
            self.connect_address = ("talk.google.com", 5222)
        else:
            self.connect_address = None
            
        # event handlers are sleekxmpp's way of dealing with important xml tags it receives
        self.add_event_handler("session_start", lambda event: self.session_start())
        self.add_event_handler("disconnected", lambda event: self.disconnected())

        self.register_plugin('xep_0199') # Ping

    def buffer_message(self, data):
        '''
        set the karma and time_last_sent
        karma_lock should have been acquired before calling this function
        '''
        self.buffer_queue.put(data)
        self.buffer_size += len(data)
        self.buffer_size_lock.release()


    def get_buffer_size(self):
        self.buffer_size_lock.acquire()
        return self.buffer_size

    def get_num_clients(self):
        self.num_clients_lock.acquire()
        return self.num_clients

    def register_hexchat_handlers(self):
        
        '''these handle the custom iq stanzas'''
        
        self.register_handler(
            callback.Callback('Connect Handler',
            stanzapath.StanzaPath('iq@type=set/connect'),
            self.master.connect_handler)
            )
        self.register_handler(
            callback.Callback('Connect Message Handler',
            stanzapath.StanzaPath('message@type=chat/connect'),
            self.master.connect_handler)
            )
        self.register_handler(
            callback.Callback('Connect Ack Handler',
            stanzapath.StanzaPath('iq@type=result/connect_ack'),
            self.master.connect_ack_handler)
            )
        self.register_handler(
            callback.Callback('Data Handler',
            stanzapath.StanzaPath('iq@type=set/packet'),
            self.master.data_handler)
            )
        self.register_handler(
            callback.Callback('Disconnect Handler',
            stanzapath.StanzaPath('iq@type=set/disconnect'),
            self.master.disconnect_handler)
            )
        self.register_handler(
            callback.Callback('Disconnect Error Message Handler',
            stanzapath.StanzaPath('message@type=chat/disconnect_error'),
            self.master.disconnect_error_handler)
            )
        self.register_handler(
            callback.Callback('Disconnect Error Iq Handler',
            stanzapath.StanzaPath('iq@type=set/disconnect_error'),
            self.master.disconnect_error_handler)
            )

        self.register_handler(
            callback.Callback('IQ Error Handler',
                stanzapath.StanzaPath('iq@type=error/error'),
                self.master.error_handler)
            )
        self.register_handler(
            callback.Callback('Message Error Handler',
            stanzapath.StanzaPath('message@type=error/error'),
            self.master.error_handler)
            )

    ### session management mathods:

    def boot(self, process=True):
        if self.connect(self.connect_address):
            if process:
                self.process()
        else:
            raise(Exception(self.boundjid.bare + " could not connect"))

    def session_start(self):
        
        """Called when the bot connects and establishes a session with the XMPP server."""
        
        # XMPP spec says that we should broadcast our presence when we connect.
        self.send_presence()
        
    def disconnected(self):
        
        '''
        Called when the bot disconnects from the XMPP server.
        Try to reconnect.
        '''
        
        logging.warning("XMPP chat server disconnected")
        logging.debug("Trying to reconnect")
        self.boot(False)
        self.send_presence()

    def _send_thread(self):
        
        '''
         modifed version of sleekxmpp's _send_thread
         that will not send faster than THROUGHPUT
        '''
        
        try:
            while not self.stop.is_set():
                while not self.stop.is_set() and \
                      not self.session_started_event.is_set():
                    self.session_started_event.wait(timeout=0.1)
                if self.__failed_send_stanza is not None:
                    data = self.__failed_send_stanza
                    self.__failed_send_stanza = None
                else:
                    try:
                        data = self.buffer_queue.get(True, 1)
                        was_buffered = True
                    except QueueEmpty:
                        try:
                            data = self.send_queue.get(True, 0.0)
                            was_buffered = False
                        except QueueEmpty:
                            continue
                            
                logging.debug("SEND: %s", data)
                enc_data = data.encode('utf-8')
                total = len(enc_data)
                sent = 0
                count = 0
                tries = 0
                try:
                    with self.send_lock:
                        while sent < total and not self.stop.is_set() and \
                              self.session_started_event.is_set():
                            try:
                                num_bytes = self.socket.send(enc_data[sent:])
                                sent += num_bytes
                                count += 1
                                '''
                                throttling code
                                that prevents data from being sent
                                faster than THROUGHPUT
                                '''
                                time.sleep(num_bytes / THROUGHPUT)
                                if was_buffered:
                                    with self.buffer_size_lock:
                                        self.buffer_size -= num_bytes
                                        
                            except ssl.SSLError as serr:
                                if tries >= self.ssl_retry_max:
                                    logging.debug('SSL error: max retries reached')
                                    self.exception(serr)
                                    logging.warning("Failed to send %s", data)
                                    if not self.stop.is_set():
                                        self.disconnect(self.auto_reconnect,
                                                        send_close=False)
                                    logging.warning('SSL write error: retrying')
                                if not self.stop.is_set():
                                    time.sleep(self.ssl_retry_delay)
                                tries += 1
                    if count > 1:
                        logging.debug('SENT: %d chunks', count)
                        
                    if was_buffered:
                        self.buffer_queue.task_done()
                    else:
                        self.send_queue.task_done()
                        
                except (Socket.error, ssl.SSLError) as serr:
                    self.event('socket_error', serr, direct=True)
                    logging.warning("Failed to send %s", data)
                    if not self.stop.is_set():
                        self.__failed_send_stanza = data
                        self._end_thread('send')
                        self.disconnect(self.auto_reconnect, send_close=False)
                        return
        except Exception as ex:
            logging.exception('Unexpected error in send thread: %s', ex)
            self.exception(ex)
            if not self.stop.is_set():
                self._end_thread('send')
                self.disconnect(self.auto_reconnect)
                return

        self._end_thread('send')
Esempio n. 7
0
class Scheduler(object):
    """
    A threaded scheduler that allows for updates mid-execution unlike the
    scheduler in the standard library.

    Based on: http://docs.python.org/library/sched.html#module-sched

    :param parentstop: An :class:`~threading.Event` to signal stopping
                       the scheduler.
    """
    def __init__(self, parentstop=None):
        #: A queue for storing tasks
        self.addq = Queue()

        #: A list of tasks in order of execution time.
        self.schedule = []

        #: If running in threaded mode, this will be the thread processing
        #: the schedule.
        self.thread = None

        #: A flag indicating that the scheduler is running.
        self.run = False

        #: An :class:`~threading.Event` instance for signalling to stop
        #: the scheduler.
        self.stop = parentstop

        #: Lock for accessing the task queue.
        self.schedule_lock = threading.RLock()

    def process(self, threaded=True, daemon=False):
        """Begin accepting and processing scheduled tasks.

        :param bool threaded: Indicates if the scheduler should execute
                              in its own thread. Defaults to ``True``.
        """
        if threaded:
            self.thread = threading.Thread(name='scheduler_process',
                                           target=self._process)
            self.thread.daemon = daemon
            self.thread.start()
        else:
            self._process()

    def _process(self):
        """Process scheduled tasks."""
        self.run = True
        try:
            while self.run and not self.stop.is_set():
                wait = 0.1
                updated = False
                if self.schedule:
                    wait = self.schedule[0].next - time.time()
                try:
                    if wait <= 0.0:
                        newtask = self.addq.get(False)
                    else:
                        if wait >= 3.0:
                            wait = 3.0
                        newtask = None
                        elapsed = 0
                        while not self.stop.is_set() and \
                              newtask is None and \
                              elapsed < wait:
                            newtask = self.addq.get(True, 0.1)
                            elapsed += 0.1
                except QueueEmpty:
                    self.schedule_lock.acquire()
                    # select only those tasks which are to be executed now
                    relevant = itertools.takewhile(
                        lambda task: time.time() >= task.next, self.schedule)
                    # run the tasks and keep the return value in a tuple
                    status = map(lambda task: (task, task.run()), relevant)
                    # remove non-repeating tasks
                    for task, doRepeat in status:
                        if not doRepeat:
                            try:
                                self.schedule.remove(task)
                            except ValueError:
                                pass
                        else:
                            # only need to resort tasks if a repeated task has
                            # been kept in the list.
                            updated = True
                else:
                    updated = True
                    self.schedule_lock.acquire()
                    if newtask is not None:
                        self.schedule.append(newtask)
                finally:
                    if updated:
                        self.schedule.sort(key=lambda task: task.next)
                    self.schedule_lock.release()
        except KeyboardInterrupt:
            self.run = False
        except SystemExit:
            self.run = False
        log.debug("Quitting Scheduler thread")

    def add(self,
            name,
            seconds,
            callback,
            args=None,
            kwargs=None,
            repeat=False,
            qpointer=None):
        """Schedule a new task.

        :param string name: The name of the task.
        :param int seconds: The number of seconds to wait before executing.
        :param callback: The function to execute.
        :param tuple args: The arguments to pass to the callback.
        :param dict kwargs: The keyword arguments to pass to the callback.
        :param bool repeat: Indicates if the task should repeat.
                            Defaults to ``False``.
        :param pointer: A pointer to an event queue for queuing callback
                        execution instead of executing immediately.
        """
        try:
            self.schedule_lock.acquire()
            for task in self.schedule:
                if task.name == name:
                    raise ValueError("Key %s already exists" % name)

            self.addq.put(
                Task(name, seconds, callback, args, kwargs, repeat, qpointer))
        except:
            raise
        finally:
            self.schedule_lock.release()

    def remove(self, name):
        """Remove a scheduled task ahead of schedule, and without
        executing it.

        :param string name: The name of the task to remove.
        """
        try:
            self.schedule_lock.acquire()
            the_task = None
            for task in self.schedule:
                if task.name == name:
                    the_task = task
            if the_task is not None:
                self.schedule.remove(the_task)
        except:
            raise
        finally:
            self.schedule_lock.release()

    def quit(self):
        """Shutdown the scheduler."""
        self.run = False
Esempio n. 8
0
class Scheduler(object):

    """
    A threaded scheduler that allows for updates mid-execution unlike the
    scheduler in the standard library.

    Based on: http://docs.python.org/library/sched.html#module-sched

    :param parentstop: An :class:`~threading.Event` to signal stopping
                       the scheduler.
    """

    def __init__(self, parentstop=None):
        #: A queue for storing tasks
        self.addq = Queue()

        #: A list of tasks in order of execution time.
        self.schedule = []

        #: If running in threaded mode, this will be the thread processing
        #: the schedule.
        self.thread = None

        #: A flag indicating that the scheduler is running.
        self.run = False

        #: An :class:`~threading.Event` instance for signalling to stop
        #: the scheduler.
        self.stop = parentstop

        #: Lock for accessing the task queue.
        self.schedule_lock = threading.RLock()

    def process(self, threaded=True, daemon=False):
        """Begin accepting and processing scheduled tasks.

        :param bool threaded: Indicates if the scheduler should execute
                              in its own thread. Defaults to ``True``.
        """
        if threaded:
            self.thread = threading.Thread(name='scheduler_process',
                                           target=self._process)
            self.thread.daemon = daemon
            self.thread.start()
        else:
            self._process()

    def _process(self):
        """Process scheduled tasks."""
        self.run = True
        try:
            while self.run and not self.stop.is_set():
                wait = 0.1
                updated = False
                if self.schedule:
                    wait = self.schedule[0].next - time.time()
                try:
                    if wait <= 0.0:
                        newtask = self.addq.get(False)
                    else:
                        if wait >= 3.0:
                            wait = 3.0
                        newtask = None
                        elapsed = 0
                        while not self.stop.is_set() and \
                              newtask is None and \
                              elapsed < wait:
                            newtask = self.addq.get(True, 0.1)
                            elapsed += 0.1
                except QueueEmpty:
                    cleanup = []
                    self.schedule_lock.acquire()
                    for task in self.schedule:
                        if time.time() >= task.next:
                            updated = True
                            if not task.run():
                                cleanup.append(task)
                        else:
                            break
                    for task in cleanup:
                        self.schedule.pop(self.schedule.index(task))
                else:
                    updated = True
                    self.schedule_lock.acquire()
                    if newtask is not None:
                        self.schedule.append(newtask)
                finally:
                    if updated:
                        self.schedule = sorted(self.schedule,
                                               key=lambda task: task.next)
                    self.schedule_lock.release()
        except KeyboardInterrupt:
            self.run = False
        except SystemExit:
            self.run = False
        log.debug("Quitting Scheduler thread")

    def add(self, name, seconds, callback, args=None,
            kwargs=None, repeat=False, qpointer=None):
        """Schedule a new task.

        :param string name: The name of the task.
        :param int seconds: The number of seconds to wait before executing.
        :param callback: The function to execute.
        :param tuple args: The arguments to pass to the callback.
        :param dict kwargs: The keyword arguments to pass to the callback.
        :param bool repeat: Indicates if the task should repeat.
                            Defaults to ``False``.
        :param pointer: A pointer to an event queue for queuing callback
                        execution instead of executing immediately.
        """
        try:
            self.schedule_lock.acquire()
            for task in self.schedule:
                if task.name == name:
                    raise ValueError("Key %s already exists" % name)

            self.addq.put(Task(name, seconds, callback, args,
                               kwargs, repeat, qpointer))
        except:
            raise
        finally:
            self.schedule_lock.release()

    def remove(self, name):
        """Remove a scheduled task ahead of schedule, and without
        executing it.

        :param string name: The name of the task to remove.
        """
        try:
            self.schedule_lock.acquire()
            the_task = None
            for task in self.schedule:
                if task.name == name:
                    the_task = task
            if the_task is not None:
                self.schedule.remove(the_task)
        except:
            raise
        finally:
            self.schedule_lock.release()

    def quit(self):
        """Shutdown the scheduler."""
        self.run = False
Esempio n. 9
0
class IBBytestream(object):

    def __init__(self, xmpp, sid, block_size, jid, peer, window_size=1, use_messages=False):
        self.xmpp = xmpp
        self.sid = sid
        self.block_size = block_size
        self.window_size = window_size
        self.use_messages = use_messages

        if jid is None:
            jid = xmpp.boundjid
        self.self_jid = jid
        self.peer_jid = peer

        self.send_seq = -1
        self.recv_seq = -1

        self._send_seq_lock = threading.Lock()
        self._recv_seq_lock = threading.Lock()

        self.stream_started = threading.Event()
        self.stream_in_closed = threading.Event()
        self.stream_out_closed = threading.Event()

        self.recv_queue = Queue()

        self.send_window = threading.BoundedSemaphore(value=self.window_size)
        self.window_ids = set()
        self.window_empty = threading.Event()
        self.window_empty.set()

    def send(self, data):
        if not self.stream_started.is_set() or \
               self.stream_out_closed.is_set():
            raise socket.error
        data = data[0:self.block_size]
        self.send_window.acquire()
        with self._send_seq_lock:
            self.send_seq = (self.send_seq + 1) % 65535
            seq = self.send_seq
        if self.use_messages:
            msg = self.xmpp.Message()
            msg['to'] = self.peer_jid
            msg['from'] = self.self_jid
            msg['id'] = self.xmpp.new_id()
            msg['ibb_data']['sid'] = self.sid
            msg['ibb_data']['seq'] = seq
            msg['ibb_data']['data'] = data
            msg.send()
            self.send_window.release()
        else:
            iq = self.xmpp.Iq()
            iq['type'] = 'set'
            iq['to'] = self.peer_jid
            iq['from'] = self.self_jid
            iq['ibb_data']['sid'] = self.sid
            iq['ibb_data']['seq'] = seq
            iq['ibb_data']['data'] = data
            self.window_empty.clear()
            self.window_ids.add(iq['id'])
            iq.send(block=False, callback=self._recv_ack)
        return len(data)

    def sendall(self, data):
        sent_len = 0
        while sent_len < len(data):
            sent_len += self.send(data[sent_len:])

    def _recv_ack(self, iq):
        self.window_ids.remove(iq['id'])
        if not self.window_ids:
            self.window_empty.set()
        self.send_window.release()
        if iq['type'] == 'error':
            self.close()

    def _recv_data(self, stanza):
        with self._recv_seq_lock:
            new_seq = stanza['ibb_data']['seq']
            if new_seq != (self.recv_seq + 1) % 65535:
                self.close()
                raise XMPPError('unexpected-request')
            self.recv_seq = new_seq

        data = stanza['ibb_data']['data']
        if len(data) > self.block_size:
            self.close()
            raise XMPPError('not-acceptable')

        self.recv_queue.put(data)
        self.xmpp.event('ibb_stream_data', {'stream': self, 'data': data})

        if isinstance(stanza, Iq):
            stanza.reply()
            stanza.send()

    def recv(self, *args, **kwargs):
        return self.read(block=True)

    def read(self, block=True, timeout=None, **kwargs):
        if not self.stream_started.is_set() or \
               self.stream_in_closed.is_set():
            raise socket.error
        if timeout is not None:
            block = True
        try:
            return self.recv_queue.get(block, timeout)
        except:
            return None

    def close(self):
        iq = self.xmpp.Iq()
        iq['type'] = 'set'
        iq['to'] = self.peer_jid
        iq['from'] = self.self_jid
        iq['ibb_close']['sid'] = self.sid
        self.stream_out_closed.set()
        iq.send(block=False,
                callback=lambda x: self.stream_in_closed.set())
        self.xmpp.event('ibb_stream_end', self)

    def _closed(self, iq):
        self.stream_in_closed.set()
        self.stream_out_closed.set()
        iq.reply()
        iq.send()
        self.xmpp.event('ibb_stream_end', self)

    def makefile(self, *args, **kwargs):
        return self

    def connect(*args, **kwargs):
        return None

    def shutdown(self, *args, **kwargs):
        return None
Esempio n. 10
0
class TestLiveSocket(object):

    """
    A live test socket that reads and writes to queues in
    addition to an actual networking socket.

    Methods:
        next_sent -- Return the next sent stanza.
        next_recv -- Return the next received stanza.
        recv_data -- Dummy method to have same interface as TestSocket.
        recv      -- Read the next stanza from the socket.
        send      -- Write a stanza to the socket.
        makefile  -- Dummy call, returns self.
        read      -- Read the next stanza from the socket.
    """

    def __init__(self, *args, **kwargs):
        """
        Create a new, live test socket.

        Arguments:
            Same as arguments for socket.socket
        """
        self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.recv_buffer = []
        self.recv_queue = Queue()
        self.send_queue = Queue()
        self.send_queue_lock = threading.Lock()
        self.recv_queue_lock = threading.Lock()
        self.is_live = True

    def __getattr__(self, name):
        """
        Return attribute values of internal, live socket.

        Arguments:
            name -- Name of the attribute requested.
        """

        return getattr(self.socket, name)

    # ------------------------------------------------------------------
    # Testing Interface

    def disconnect_errror(self):
        """
        Used to simulate a socket disconnection error.

        Not used by live sockets.
        """
        try:
            self.socket.shutdown()
            self.socket.close()
        except:
            pass

    def next_sent(self, timeout=None):
        """
        Get the next stanza that has been sent.

        Arguments:
            timeout -- Optional timeout for waiting for a new value.
        """
        args = {'block': False}
        if timeout is not None:
            args = {'block': True, 'timeout': timeout}
        try:
            return self.send_queue.get(**args)
        except:
            return None

    def next_recv(self, timeout=None):
        """
        Get the next stanza that has been received.

        Arguments:
            timeout -- Optional timeout for waiting for a new value.
        """
        args = {'block': False}
        if timeout is not None:
            args = {'block': True, 'timeout': timeout}
        try:
            if self.recv_buffer:
                return self.recv_buffer.pop(0)
            else:
                return self.recv_queue.get(**args)
        except:
            return None

    def recv_data(self, data):
        """
        Add data to a receive buffer for cases when more than a single stanza
        was received.
        """
        self.recv_buffer.append(data)

    # ------------------------------------------------------------------
    # Socket Interface

    def recv(self, *args, **kwargs):
        """
        Read data from the socket.

        Store a copy in the receive queue.

        Arguments:
            Placeholders. Same as for socket.recv.
        """
        data = self.socket.recv(*args, **kwargs)
        with self.recv_queue_lock:
            self.recv_queue.put(data)
        return data

    def send(self, data):
        """
        Send data on the socket.

        Store a copy in the send queue.

        Arguments:
            data -- String value to write.
        """
        with self.send_queue_lock:
            self.send_queue.put(data)
        return self.socket.send(data)

    # ------------------------------------------------------------------
    # File Socket

    def makefile(self, *args, **kwargs):
        """
        File socket version to use with ElementTree.

        Arguments:
            Placeholders, same as socket.makefile()
        """
        return self

    def read(self, *args, **kwargs):
        """
        Implement the file socket read interface.

        Arguments:
            Placeholders, same as socket.recv()
        """
        return self.recv(*args, **kwargs)

    def clear(self):
        """
        Empty the send queue, typically done once the session has started to
        remove the feature negotiation and log in stanzas.
        """
        with self.send_queue_lock:
            for i in range(0, self.send_queue.qsize()):
                self.send_queue.get(block=False)
        with self.recv_queue_lock:
            for i in range(0, self.recv_queue.qsize()):
                self.recv_queue.get(block=False)
Esempio n. 11
0
class Scheduler(object):

    """
    A threaded scheduler that allows for updates mid-execution unlike the
    scheduler in the standard library.

    Based on: http://docs.python.org/library/sched.html#module-sched

    :param parentstop: An :class:`~threading.Event` to signal stopping
                       the scheduler.
    """

    def __init__(self, parentstop=None):
        #: A queue for storing tasks
        self.addq = Queue()

        #: A list of tasks in order of execution time.
        self.schedule = []

        #: If running in threaded mode, this will be the thread processing
        #: the schedule.
        self.thread = None

        #: A flag indicating that the scheduler is running.
        self.run = False

        #: An :class:`~threading.Event` instance for signalling to stop
        #: the scheduler.
        self.stop = parentstop

        #: Lock for accessing the task queue.
        self.schedule_lock = threading.RLock()

        #: The time in seconds to wait for events from the event queue,
        #: and also the time between checks for the process stop signal.
        self.wait_timeout = WAIT_TIMEOUT

    def process(self, threaded=True, daemon=False):
        """Begin accepting and processing scheduled tasks.

        :param bool threaded: Indicates if the scheduler should execute
                              in its own thread. Defaults to ``True``.
        """
        if threaded:
            self.thread = threading.Thread(name='scheduler_process',
                                           target=self._process)
            self.thread.daemon = daemon
            self.thread.start()
        else:
            self._process()

    def _process(self):
        """Process scheduled tasks."""
        self.run = True
        try:
            while self.run and not self.stop.is_set():
                updated = False
                if self.schedule:
                    wait = self.schedule[0].next - time.time()
                else:
                    wait = self.wait_timeout
                try:
                    if wait <= 0.0:
                        newtask = self.addq.get(False)
                    else:
                        newtask = None
                        while self.run and \
                              not self.stop.is_set() and \
                              newtask is None and \
                              wait > 0:
                            try:
                                newtask = self.addq.get(True, min(wait, self.wait_timeout))
                            except QueueEmpty:          # Nothing to add, nothing to do. Check run flags and continue waiting.
                                wait -= self.wait_timeout
                except QueueEmpty:                      # Time to run some tasks, and no new tasks to add.
                    self.schedule_lock.acquire()
                    # select only those tasks which are to be executed now
                    relevant = itertools.takewhile(
                        lambda task: time.time() >= task.next, self.schedule)
                    # run the tasks and keep the return value in a tuple
                    status = map(lambda task: (task, task.run()), relevant)
                    # remove non-repeating tasks
                    for task, doRepeat in status:
                        if not doRepeat:
                            try:
                                self.schedule.remove(task)
                            except ValueError:
                                pass
                        else:
                            # only need to resort tasks if a repeated task has
                            # been kept in the list.
                            updated = True
                else:                                   # Add new task
                    self.schedule_lock.acquire()
                    if newtask is not None:
                        self.schedule.append(newtask)
                        updated = True
                finally:
                    if updated:
                        self.schedule.sort(key=lambda task: task.next)
                    self.schedule_lock.release()
        except KeyboardInterrupt:
            self.run = False
        except SystemExit:
            self.run = False
        log.debug("Quitting Scheduler thread")

    def add(self, name, seconds, callback, args=None,
            kwargs=None, repeat=False, qpointer=None):
        """Schedule a new task.

        :param string name: The name of the task.
        :param int seconds: The number of seconds to wait before executing.
        :param callback: The function to execute.
        :param tuple args: The arguments to pass to the callback.
        :param dict kwargs: The keyword arguments to pass to the callback.
        :param bool repeat: Indicates if the task should repeat.
                            Defaults to ``False``.
        :param pointer: A pointer to an event queue for queuing callback
                        execution instead of executing immediately.
        """
        try:
            self.schedule_lock.acquire()
            for task in self.schedule:
                if task.name == name:
                    raise ValueError("Key %s already exists" % name)

            self.addq.put(Task(name, seconds, callback, args,
                               kwargs, repeat, qpointer))
        except:
            raise
        finally:
            self.schedule_lock.release()

    def remove(self, name):
        """Remove a scheduled task ahead of schedule, and without
        executing it.

        :param string name: The name of the task to remove.
        """
        try:
            self.schedule_lock.acquire()
            the_task = None
            for task in self.schedule:
                if task.name == name:
                    the_task = task
            if the_task is not None:
                self.schedule.remove(the_task)
        except:
            raise
        finally:
            self.schedule_lock.release()

    def quit(self):
        """Shutdown the scheduler."""
        self.run = False
Esempio n. 12
0
class TestSocket(object):
    """
    A dummy socket that reads and writes to queues instead
    of an actual networking socket.

    Methods:
        next_sent -- Return the next sent stanza.
        recv_data -- Make a stanza available to read next.
        recv      -- Read the next stanza from the socket.
        send      -- Write a stanza to the socket.
        makefile  -- Dummy call, returns self.
        read      -- Read the next stanza from the socket.
    """
    def __init__(self, *args, **kwargs):
        """
        Create a new test socket.

        Arguments:
            Same as arguments for socket.socket
        """
        self.socket = socket.socket(*args, **kwargs)
        self.recv_queue = Queue()
        self.send_queue = Queue()
        self.is_live = False
        self.disconnected = False

    def __getattr__(self, name):
        """
        Return attribute values of internal, dummy socket.

        Some attributes and methods are disabled to prevent the
        socket from connecting to the network.

        Arguments:
            name -- Name of the attribute requested.
        """
        def dummy(*args):
            """Method to do nothing and prevent actual socket connections."""
            return None

        overrides = {'connect': dummy, 'close': dummy, 'shutdown': dummy}

        return overrides.get(name, getattr(self.socket, name))

    # ------------------------------------------------------------------
    # Testing Interface

    def next_sent(self, timeout=None):
        """
        Get the next stanza that has been 'sent'.

        Arguments:
            timeout -- Optional timeout for waiting for a new value.
        """
        args = {'block': False}
        if timeout is not None:
            args = {'block': True, 'timeout': timeout}
        try:
            return self.send_queue.get(**args)
        except:
            return None

    def recv_data(self, data):
        """
        Add data to the receiving queue.

        Arguments:
            data -- String data to 'write' to the socket to be received
                    by the XMPP client.
        """
        self.recv_queue.put(data)

    def disconnect_error(self):
        """
        Simulate a disconnect error by raising a socket.error exception
        for any current or further socket operations.
        """
        self.disconnected = True

    # ------------------------------------------------------------------
    # Socket Interface

    def recv(self, *args, **kwargs):
        """
        Read a value from the received queue.

        Arguments:
            Placeholders. Same as for socket.Socket.recv.
        """
        if self.disconnected:
            raise socket.error
        return self.read(block=True)

    def send(self, data):
        """
        Send data by placing it in the send queue.

        Arguments:
            data -- String value to write.
        """
        if self.disconnected:
            raise socket.error
        self.send_queue.put(data)
        return len(data)

    # ------------------------------------------------------------------
    # File Socket

    def makefile(self, *args, **kwargs):
        """
        File socket version to use with ElementTree.

        Arguments:
            Placeholders, same as socket.Socket.makefile()
        """
        return self

    def read(self, block=True, timeout=None, **kwargs):
        """
        Implement the file socket interface.

        Arguments:
            block   -- Indicate if the read should block until a
                       value is ready.
            timeout -- Time in seconds a block should last before
                       returning None.
        """
        if self.disconnected:
            raise socket.error
        if timeout is not None:
            block = True
        try:
            return self.recv_queue.get(block, timeout)
        except:
            return None
Esempio n. 13
0
class Superfeedr(ClientXMPP):
    def __init__(self, jid, password, auto_connect=True):

        ClientXMPP.__init__(self, jid, password)

        self.success = False
        self.notification_callback = None
        self.wait_for_start = Queue()

        self.register_plugin('xep_0004')
        self.register_plugin('xep_0030')
        self.register_plugin('xep_0060')
        self.register_plugin('xep_0199')
        self.add_event_handler("session_start", self.start)

        handler = Callback('superfeedr',
                           MatchXPath("{jabber:client}message/"
                                      "{http://jabber.org/protocol/pubsub#event}event"),
                           self.superfeedr_msg)

        self.register_handler(handler)

        if auto_connect:
            self.connect_wait()

    def connect_wait(self, timeout=10):
        self.success = self.connect(('xmpp.superfeedr.com', 5222))
        if self.success:
            self.process(threaded=True)
            start = self.wait_for_start.get(timeout)
            if start is None:
                self.success = False

    def start(self, event):
        self.get_roster()
        self.send_presence()
        self.wait_for_start.put(True)

    def superfeedr_msg(self, stanza):
        xml = stanza.xml

        event = {'items': [],
                 'status': self.parse_status(xml, '{http://jabber.org/protocol/pubsub#event}event')}

        items = xml.findall('{http://jabber.org/protocol/pubsub#event}event/'
                            '{http://jabber.org/protocol/pubsub#event}items/'
                            '{http://jabber.org/protocol/pubsub}item/'
                            '{http://www.w3.org/2005/Atom}content')

        for item in items:
            if item.get('type') == 'application/json':
                event['items'].append(json.loads(item.text))

        self.event('superfeedr', event)
        if len(event.get('items', [])) > 0:
            self.event('superfeedr_entry', event)

        return event

    def subscribe(self, feeds):
        if len(feeds) > 20:
            raise ValueError('Maximum of 20 feeds allowed per subscription message.')

        pubsub = ElementTree.Element('{http://jabber.org/protocol/pubsub}pubsub')
        pubsub.attrib['xmlns:superfeedr'] = 'http://superfeedr.com/xmpp-pubsub-ext'

        for f in feeds:
            feed = ElementTree.Element('subscribe')
            feed.attrib['node'] = f
            feed.attrib['jid'] = self.boundjid.bare
            feed.attrib['superfeedr:format'] = 'json'
            pubsub.append(feed)

        iq = self.make_iq_set(pubsub)
        iq.attrib['to'] = 'firehoser.superfeedr.com'
        iq.attrib['from'] = self.boundjid.bare
        iq.attrib['type'] = 'set'

        response = self.send_wait(iq)

        subscriptions = response.findall('{http://jabber.org/protocol/pubsub}pubsub/'
                                         '{http://jabber.org/protocol/pubsub}subscription')
        result = False
        if subscriptions:
            result = []
            for subscription in subscriptions:
                status = self.parse_status(subscription)
                result.append({"subscription": {"status": status,
                                                "feed": {"url": subscription.get('node'),
                                                         "title": status['title']}}})
        return result

    def unsubscribe(self, feed):
        return self.plugin['xep_0060'].unsubscribe('firehoser.superfeedr.com', feed)

    def list(self, page=0):
        pubsub = ElementTree.Element('{http://jabber.org/protocol/pubsub}pubsub')
        pubsub.attrib['xmlns:superfeedr'] = 'http://superfeedr.com/xmpp-pubsub-ext'

        subscriptions = ElementTree.Element('subscriptions')
        subscriptions.attrib['jid'] = self.boundjid.full
        subscriptions.attrib['superfeedr:page'] = str(page)

        pubsub.append(subscriptions)

        iq = self.make_iq_set(pubsub)
        iq.attrib['to'] = 'firehoser.superfeedr.com'
        iq.attrib['from'] = self.boundjid.full
        iq.attrib['type'] = 'get'

        result = self.send_wait(iq)

        if not result or result.get('type') == 'error':
            return False

        nodes = result.findall('{http://jabber.org/protocol/pubsub}pubsub/'
                               '{http://jabber.org/protocol/pubsub}subscriptions/'
                               '{http://jabber.org/protocol/pubsub}subscription')
        if nodes is None:
            return []

        nodelist = []
        for node in nodes:
            nodelist.append(node.get('node', ''))

        return nodelist

    def on_notification(self, callback):
        self.add_event_handler('superfeedr', callback)

    def on_entry(self, callback):
        self.add_event_handler('superfeedr_entry', callback)

    def send_wait(self, iq, timeout=None):
        """
         :param iq: Stanza to send
         :param int timeout: The number of seconds to wait for the stanza
            to arrive. Defaults to the the stream's
            :class:`~sleekxmpp.xmlstream.xmlstream.XMLStream.response_timeout`
            value.
        """

        iq_id = iq.get('id')
        self.send(iq)

        waiter = Waiter("SendWait_%s" % self.new_id(), MatcherId(iq_id))
        self.register_handler(waiter)

        return waiter.wait(timeout)

    @staticmethod
    def parse_status(xml, base_query=None):

        base_query = base_query + '/' if base_query else ''
        status_query = base_query + '{http://superfeedr.com/xmpp-pubsub-ext}status'
        status_child_query = status_query + '/{http://superfeedr.com/xmpp-pubsub-ext}'

        status = xml.find(status_query)
        http = xml.find(status_child_query + 'http')
        next_fetch = xml.find(status_child_query + 'next_fetch')
        last_fetch = xml.find(status_child_query + 'last_fetch')
        last_parse = xml.find(status_child_query + 'last_parse')
        period = xml.find(status_child_query + 'period')
        last_maintenance_at = xml.find(status_child_query + 'last_maintenance_at')
        title = xml.find(status_child_query + 'title')

        if None not in (status, http, next_fetch, last_fetch, last_parse, period,
                        last_maintenance_at, last_fetch, title):

            result = dict(lastParse=date_to_epoch(last_parse.text),
                          lastFetch=date_to_epoch(last_fetch.text),
                          nextFetch=date_to_epoch(next_fetch.text),
                          lastMaintenanceAt=date_to_epoch(last_maintenance_at.text),
                          period=period.text,
                          title=title.text)

            feed = status.get('feed')
            if feed is not None:
                result['feed'] = feed

            if http.text is not None:
                result['http'] = http.text

            code = http.get('code')
            if code:
                result['code'] = http.get('code')

        return result
Esempio n. 14
0
class Waiter(BaseHandler):

    """
    The Waiter handler allows an event handler to block until a
    particular stanza has been received. The handler will either be
    given the matched stanza, or ``False`` if the waiter has timed out.

    :param string name: The name of the handler.
    :param matcher: A :class:`~sleekxmpp.xmlstream.matcher.base.MatcherBase`
                    derived object for matching stanza objects.
    :param stream: The :class:`~sleekxmpp.xmlstream.xmlstream.XMLStream`
                   instance this handler should monitor.
    """

    def __init__(self, name, matcher, stream=None):
        BaseHandler.__init__(self, name, matcher, stream=stream)
        self._payload = Queue()

    def prerun(self, payload):
        """Store the matched stanza when received during processing.

        :param payload: The matched
            :class:`~sleekxmpp.xmlstream.stanzabase.ElementBase` object.
        """
        self._payload.put(payload)

    def run(self, payload):
        """Do not process this handler during the main event loop."""
        pass

    def wait(self, timeout=None):
        """Block an event handler while waiting for a stanza to arrive.

        Be aware that this will impact performance if called from a
        non-threaded event handler.

        Will return either the received stanza, or ``False`` if the
        waiter timed out.

        :param int timeout: The number of seconds to wait for the stanza
            to arrive. Defaults to the the stream's
            :class:`~sleekxmpp.xmlstream.xmlstream.XMLStream.response_timeout`
            value.
        """
        if timeout is None:
            timeout = self.stream().response_timeout

        elapsed_time = 0
        stanza = False
        while elapsed_time < timeout and not self.stream().stop.is_set():
            try:
                stanza = self._payload.get(True, 1)
                break
            except QueueEmpty:
                elapsed_time += 1
                if elapsed_time >= timeout:
                    log.warning("Timed out waiting for %s", self.name)
        self.stream().remove_handler(self.name)
        return stanza

    def check_delete(self):
        """Always remove waiters after use."""
        return True
Esempio n. 15
0
class TestSocket(object):

    """
    A dummy socket that reads and writes to queues instead
    of an actual networking socket.

    Methods:
        next_sent -- Return the next sent stanza.
        recv_data -- Make a stanza available to read next.
        recv      -- Read the next stanza from the socket.
        send      -- Write a stanza to the socket.
        makefile  -- Dummy call, returns self.
        read      -- Read the next stanza from the socket.
    """

    def __init__(self, *args, **kwargs):
        """
        Create a new test socket.

        Arguments:
            Same as arguments for socket.socket
        """
        self.socket = socket.socket(*args, **kwargs)
        self.recv_queue = Queue()
        self.send_queue = Queue()
        self.is_live = False
        self.disconnected = False

    def __getattr__(self, name):
        """
        Return attribute values of internal, dummy socket.

        Some attributes and methods are disabled to prevent the
        socket from connecting to the network.

        Arguments:
            name -- Name of the attribute requested.
        """

        def dummy(*args):
            """Method to do nothing and prevent actual socket connections."""
            return None

        overrides = {'connect': dummy,
                     'close': dummy,
                     'shutdown': dummy}

        return overrides.get(name, getattr(self.socket, name))

    # ------------------------------------------------------------------
    # Testing Interface

    def next_sent(self, timeout=None):
        """
        Get the next stanza that has been 'sent'.

        Arguments:
            timeout -- Optional timeout for waiting for a new value.
        """
        args = {'block': False}
        if timeout is not None:
            args = {'block': True, 'timeout': timeout}
        try:
            return self.send_queue.get(**args)
        except:
            return None

    def recv_data(self, data):
        """
        Add data to the receiving queue.

        Arguments:
            data -- String data to 'write' to the socket to be received
                    by the XMPP client.
        """
        self.recv_queue.put(data)

    def disconnect_error(self):
        """
        Simulate a disconnect error by raising a socket.error exception
        for any current or further socket operations.
        """
        self.disconnected = True

    # ------------------------------------------------------------------
    # Socket Interface

    def recv(self, *args, **kwargs):
        """
        Read a value from the received queue.

        Arguments:
            Placeholders. Same as for socket.Socket.recv.
        """
        if self.disconnected:
            raise socket.error
        return self.read(block=True)

    def send(self, data):
        """
        Send data by placing it in the send queue.

        Arguments:
            data -- String value to write.
        """
        if self.disconnected:
            raise socket.error
        self.send_queue.put(data)
        return len(data)

    # ------------------------------------------------------------------
    # File Socket

    def makefile(self, *args, **kwargs):
        """
        File socket version to use with ElementTree.

        Arguments:
            Placeholders, same as socket.Socket.makefile()
        """
        return self

    def read(self, block=True, timeout=None, **kwargs):
        """
        Implement the file socket interface.

        Arguments:
            block   -- Indicate if the read should block until a
                       value is ready.
            timeout -- Time in seconds a block should last before
                       returning None.
        """
        if self.disconnected:
            raise socket.error
        if timeout is not None:
            block = True
        try:
            return self.recv_queue.get(block, timeout)
        except:
            return None