Beispiel #1
0
    def send(self, request):
        """Sends the specified MTProtoRequest, previously sending any message
           which needed confirmation. This also pauses the updates thread"""

        # Only cancel the receive *if* it was the
        # updates thread who was receiving. We do
        # not want to cancel other pending requests!
        if self.updates_thread_receiving:
            Log.i('Cancelling updates receive from send()...')
            self.transport.cancel_receive()

        # Now only us can be using this method
        with self.lock:
            Log.d('send() acquired the lock')
            # Set the flag to true so the updates thread stops trying to receive
            self.waiting_receive = True

            # If any message needs confirmation send an AckRequest first
            if self.need_confirmation:
                msgs_ack = MsgsAck(self.need_confirmation)
                with BinaryWriter() as writer:
                    msgs_ack.on_send(writer)
                    self.send_packet(writer.get_bytes(), msgs_ack)

                del self.need_confirmation[:]

            # Finally send our packed request
            with BinaryWriter() as writer:
                request.on_send(writer)
                self.send_packet(writer.get_bytes(), request)

            # And update the saved session
            self.session.save()

        Log.d('send() released the lock')
Beispiel #2
0
    def handle_update(self, msg_id, sequence, reader):
        tlobject = reader.tgread_object()
        Log.d('Handling update for object %s', repr(tlobject))
        for handler in self.on_update_handlers:
            handler(tlobject)

        return False
Beispiel #3
0
    def handle_bad_msg_notification(self, msg_id, sequence, reader):
        Log.d('Handling bad message notification')
        reader.read_int(signed=False)  # code
        reader.read_long(signed=False)  # request_id
        reader.read_int()  # request_sequence

        error_code = reader.read_int()
        raise BadMessageError(error_code)
Beispiel #4
0
    def handle_gzip_packed(self, msg_id, sequence, reader, request):
        Log.d('Handling gzip packed data')
        reader.read_int(signed=False)  # code
        packed_data = reader.tgread_bytes()
        unpacked_data = gzip.decompress(packed_data)

        with BinaryReader(unpacked_data) as compressed_reader:
            return self.process_msg(msg_id, sequence, compressed_reader,
                                    request)
Beispiel #5
0
    def handle_pong(self, msg_id, sequence, reader, request):
        Log.d('Handling pong')
        reader.read_int(signed=False)  # code
        recv_msg_id = reader.read_long(signed=False)

        if recv_msg_id == request.msg_id:
            Log.w('Pong confirmed a request')
            request.confirm_received = True

        return False
Beispiel #6
0
    def handle_container(self, msg_id, sequence, reader, request):
        Log.d('Handling container')
        reader.read_int(signed=False)  # code
        size = reader.read_int()
        for _ in range(size):
            inner_msg_id = reader.read_long(signed=False)
            reader.read_int()  # inner_sequence
            inner_length = reader.read_int()
            begin_position = reader.tell_position()

            if not self.process_msg(inner_msg_id, sequence, reader, request):
                reader.set_position(begin_position + inner_length)

        return False
Beispiel #7
0
    def handle_rpc_result(self, msg_id, sequence, reader, request):
        Log.d('Handling RPC result, request is%s None',
              ' not' if request else '')
        reader.read_int(signed=False)  # code
        request_id = reader.read_long(signed=False)
        inner_code = reader.read_int(signed=False)

        if request and request_id == request.msg_id:
            request.confirm_received = True

        if inner_code == 0x2144ca19:  # RPC Error
            error = RPCError(code=reader.read_int(),
                             message=reader.tgread_string())

            Log.w('Read RPC error: %s', str(error))
            if error.must_resend:
                if not request:
                    raise ValueError(
                        'The previously sent request must be resent. '
                        'However, no request was previously sent (called from updates thread).'
                    )
                request.confirm_received = False

            if error.message.startswith('FLOOD_WAIT_'):
                self.updates_thread_sleep = error.additional_data

                print('Should wait {}s. Sleeping until then.'.format(
                    error.additional_data))
                sleep(error.additional_data)

            elif '_MIGRATE_' in error.message:
                raise InvalidDCError(error.additional_data)

            else:
                raise error
        else:
            if not request:
                raise ValueError(
                    'Cannot receive a request from inside an RPC result from the updates thread.'
                )

            Log.d('Reading request response')
            if inner_code == 0x3072cfa1:  # GZip packed
                unpacked_data = gzip.decompress(reader.tgread_bytes())
                with BinaryReader(unpacked_data) as compressed_reader:
                    request.on_response(compressed_reader)
            else:
                reader.seek(-4)
                request.on_response(reader)
Beispiel #8
0
    def handle_bad_server_salt(self, msg_id, sequence, reader, request):
        Log.d('Handling bad server salt')
        reader.read_int(signed=False)  # code
        reader.read_long(signed=False)  # bad_msg_id
        reader.read_int()  # bad_msg_seq_no
        reader.read_int()  # error_code
        new_salt = reader.read_long(signed=False)

        self.session.salt = new_salt

        if request is None:
            raise ValueError(
                'Tried to handle a bad server salt with no request specified')

        # Resend
        self.send(request)

        return True
Beispiel #9
0
    def receive(self, request, timeout=timedelta(seconds=5)):
        """Receives the specified MTProtoRequest ("fills in it"
           the received data). This also restores the updates thread.
           An optional timeout can be specified to cancel the operation
           if no data has been read after its time delta"""

        with self.lock:
            Log.d('receive() acquired the lock')
            # Don't stop trying to receive until we get the request we wanted
            while not request.confirm_received:
                Log.i('Trying to .receive() the request result...')
                seq, body = self.transport.receive(timeout)
                message, remote_msg_id, remote_sequence = self.decode_msg(body)

                with BinaryReader(message) as reader:
                    self.process_msg(remote_msg_id, remote_sequence, reader,
                                     request)

            Log.i('Request result received')

            # We can now set the flag to False thus resuming the updates thread
            self.waiting_receive = False
        Log.d('receive() released the lock')
Beispiel #10
0
    def updates_thread_method(self):
        """This method will run until specified and listen for incoming updates"""

        # Set a reasonable timeout when checking for updates
        timeout = timedelta(minutes=1)

        while self.updates_thread_running:
            # Always sleep a bit before each iteration to relax the CPU,
            # since it's possible to early 'continue' the loop to reach
            # the next iteration, but we still should to sleep.
            if self.updates_thread_sleep:
                sleep(self.updates_thread_sleep)
                self.updates_thread_sleep = None
            else:
                # Longer sleep if we're not expecting updates (only pings)
                sleep(0.1 if self.on_update_handlers else 1)

            # Only try to receive updates if we're not waiting to receive a request
            if not self.waiting_receive:
                with self.lock:
                    Log.d('Updates thread acquired the lock')
                    try:
                        now = time()
                        # If ping_interval seconds passed since last ping, send a new one
                        if now >= self.ping_time_last + self.ping_interval:
                            self.ping_time_last = now
                            self.send_ping()
                            Log.d('Ping sent from the updates thread')

                        # Exit the loop if we're not expecting to receive any updates
                        if not self.on_update_handlers:
                            Log.d('No updates handlers found, continuing')
                            continue

                        self.updates_thread_receiving = True
                        Log.d(
                            'Trying to receive updates from the updates thread'
                        )
                        seq, body = self.transport.receive(timeout)
                        message, remote_msg_id, remote_sequence = self.decode_msg(
                            body)

                        Log.i('Received update from the updates thread')
                        with BinaryReader(message) as reader:
                            self.process_msg(remote_msg_id, remote_sequence,
                                             reader)

                    except TimeoutError:
                        Log.d('Receiving updates timed out')
                        # TODO Workaround for issue #50
                        r = GetStateRequest()
                        try:
                            Log.d(
                                'Sending GetStateRequest (workaround for issue #50)'
                            )
                            self.send(r)
                            self.receive(r)
                        except TimeoutError:
                            Log.w(
                                'Timed out inside a timeout, trying to reconnect...'
                            )
                            self.reconnect()
                            self.send(r)
                            self.receive(r)

                    except ReadCancelledError:
                        Log.i('Receiving updates cancelled')
                    except OSError:
                        Log.w('OSError on updates thread, %s logging out',
                              'was' if self.logging_out else 'was not')

                        if self.logging_out:
                            # This error is okay when logging out, means we got disconnected
                            # TODO Not sure why this happens because we call disconnect()…
                            self.set_updates_thread(running=False)
                        else:
                            raise

                Log.d('Updates thread released the lock')
                self.updates_thread_receiving = False