Пример #1
0
    def _on_entering(self):
        """Acquire network resources and try to connect.

        The connection is authenticated by the mean of the server
        certificate, so it is trusted.
        """
        self.logger.info(u"Connecting to server...")
        self._context._internal_facade.set_global_status(
            GStatuses.NC_CONNECTING)
        if self._context.acquire_network_resources():
            self.logger.info(u"Server has successfully authenticated itself")
            self._context.num_connection_attempts = 0
            self._context._internal_facade.reset_pause_timer()
            self._set_next_state(StateRegister.get('ConnectedState'))
            self._context._input_queue.put(Command('HANDSHAKE'),
                                           'sessioncommand')
        else:
            self._context._internal_facade.set_global_status(
                GStatuses.NC_NOSERVER)
            if self._context.num_connection_attempts > self._context.max_connection_attempts:
                self.logger.error(
                    u'Server has been unreachable for too long, giving up.')
                return self._context._internal_facade.pause_and_restart()
            self._set_next_state(StateRegister.get('DisconnectedState'))
            self._context._input_queue.put(Command('CONNECT'),
                                           'sessioncommand')
Пример #2
0
 def _handle_command_OPERATIONSFINISHED(self, command):
     """Receiving this command means that all the internal operations
     have been served. Wait until all of them are complete.
     """
     self._set_next_state(StateRegister.get('SyncWaitingForCompletionState'))
     cmd = Command('GOTOONCOMPLETION')
     cmd.next_state = 'SyncDoneState'
     self._context._input_queue.put(cmd, 'sessioncommand')
Пример #3
0
    def _set_next_state(self, next_state):
        """Change ServerSession's state.

        @param next_state:
                    Instance of a subclass of ServerSessionState.
        """
        command = Command('CHANGESTATE')
        command.next_state = next_state
        self._context._input_queue.put(command, 'sessioncommand')
Пример #4
0
    def _set_next_state(self, next_state):
        """Change ServerSession's state.

        @param next_state:
                    Instance of a subclass of ServerSessionState.
        """
        command = Command('CHANGESTATE')
        command.next_state = next_state
        self._context._input_queue.put(command, 'sessioncommand')
Пример #5
0
    def signal_download_integrity_error(self, operation, reason, expected_etag,
                                        expected_basis, actual_etag,
                                        computed_basis):
        """Tell ServerSession that the integrity check of a downloaded
        file has failed.

        This method is meant to be called by Workers.

        @param operation:
                    Instance of PathnameOperation. Remember that it
                    contains also its Proof object.
        @param reason:
                    String shortly describing the error.
        @param expected_etag:
                    The etag the file was expected to have. It's the one
                    communicated by the server in its file list.
        @param expected_basis:
                    The trusted basis. It's the one the user had
                    accepted when the sync started.
        @param actual_etag:
                    The etag the file has turned to have after being
                    downloaded.
        @param computed_basis:
                    The basis returned by the IntegrityManager when
                    computing the given proof object. Possibly None.
        """
        cmd = Command('INTEGRITYERRORONDOWNLOAD')
        cmd.operation = operation
        cmd.proof = operation.download_info['proof']
        cmd.reason = reason
        cmd.expected_etag = expected_etag
        cmd.expected_basis = expected_basis
        cmd.actual_etag = actual_etag
        cmd.computed_basis = computed_basis
        self._input_queue.put(cmd, 'systemcommand')
 def _exchange_keepalive_message(self):
     #self.logger.debug(
     #    u"Sending KEEP_ALIVE id=%s to server" % self.keepalive_id)
     self.output_message_queue.put(
         KEEP_ALIVE('KEEP_ALIVE', {'id': self.keepalive_id}))
     self.keepalive_id += 1
     if self.keepalive_id > 99999:
         self.keepalive_id = 1
     try:
         while True:
             message = self.input_message_queue.get(
                 block=True, timeout=self.timeout_time)
             if message is POISON_PILL:
                 break
             if message.getParameter('id') == self.keepalive_id - 1:
                 break
     except Queue.Empty:
         self.logger.warning(
             u"Server hasn't replied to KEEP_ALIVE in %s seconds,"
             " assuming crash." % self.timeout_time)
         self._session_queue.put(
             Command('KEEPALIVETIMEDOUT'), 'sessioncommand')
         return
     # Have we been woken up due to suspension?
     if not self._check_suspension():
         # No, it was a normal KEEP_ALIVE reply from the server
         ##self.logger.debug(
         ##    u"Received KEEP_ALIVE reply id=%s from server"
         ##    % (message.getParameter('id')))
         pass
Пример #7
0
    def _handle_message_COMMIT_DONE(self, message):
        """Everything went well, the server has completed the commit.

        Check the resulting basis sent by the server against our
        candidate basis, if it's valid then finalize the commit by
        updating all internal data structures.
        Finally go into the sync phase, the session can begin at last.
        """
        self.logger.info(u'Commit done')
        server_basis = message.getParameter('new_basis')
        candidate_basis = self._load_candidate_basis()
        previous_basis = self._load_trusted_basis()
        self.logger.debug(u"Server basis: %s" % server_basis)
        self.logger.debug(u"Candidate basis: %s" % candidate_basis)
        self.logger.debug(u"Last trusted basis: %s" % previous_basis)

        # Check the server basis against the candidate basis
        self._context.integrity_manager.setCurrentBasis(candidate_basis)
        self._check_integrity(server_basis)

        # Everything OK, persist the new trusted basis and related metadata
        operations = self._context.transaction_cache.get_all_records()
        operations = [(op_id, op) for (op_id, op, _) in operations]
        self._persist_integrity_metadata(previous_basis, server_basis,
                                         operations)
        self._context.integrity_manager.setCurrentBasis(server_basis)

        self.logger.info(u"Pending commit successfully recovered.")
        self.logger.info(u"Updated basis: %s" % server_basis)
        self._update_user_interfaces(message)
        self._set_next_state(StateRegister.get('ReadyForServiceState'))
        self._context._input_queue.put(Command('STARTSYNCPHASE'),
                                       'sessioncommand')
Пример #8
0
    def signal_free_worker(self):
        """
        Tell ServerSession that a worker is free to receive new tasks.

        This method is meant to be called by WorkerPool.
        """
        self._input_queue.put(Command('WORKERFREE'), 'systemcommand')
Пример #9
0
 def _handle_message_REPLICATION_START(self, message):
     """The server is ready to leave the sync phase, too. Let's
     start the Replication & Transfer phase.
     """
     self._set_next_state(
         StateRegister.get('EnteringReplicationAndTransferState'))
     self._context._input_queue.put(
         Command('UPDATEBEFOREREPLICATION'), 'sessioncommand')
Пример #10
0
    def signal_deletelocal_integrity_error(self, pathname, proof, reason,
                                           expected_basis, computed_basis):
        """Tell ServerSession that the integrity check of a
        pathname to delete locally has failed.

        This method is meant to be called by Workers.

        @param pathname:
                    String representing the pathname.
        @param reason:
                    String shortly describing the error.
        @param expected_basis:
                    The trusted basis. It's the one the user had
                    accepted when the sync started.
        @param computed_basis:
                    The basis returned by the IntegrityManager when
                    computing the given proof object. Possibly None.
        """
        cmd = Command('INTEGRITYERRORONDELETELOCAL')
        cmd.pathname = pathname
        cmd.proof = proof
        cmd.reason = reason
        cmd.expected_basis = expected_basis
        cmd.computed_basis = computed_basis
        self._input_queue.put(cmd, 'systemcommand')
 def run(self):
     self.started = True
     try:
         while not self._termination_requested():
             ready, _, _ = select([self.sock], [], [], 1)
             if ready:
                 msg = self._receive_message()
                 if msg.name == 'KEEP_ALIVE':
                     self.input_keepalive_queue.put(msg)
                 else:
                     self.input_message_queue.put(msg, 'servermessage')
     except ConnectionException:
         self.logger.info(u"Server has closed the connection, aborting")
         self.input_message_queue.put(Command('BROKENCONNECTION'),
                                      'sessioncommand')
     except Exception as e:
         self.logger.warning(u"Detected a connection problem, aborting: %s",
                             e)
         self.input_message_queue.put(Command('BROKENCONNECTION'),
                                      'sessioncommand')
Пример #12
0
    def signal_deletelocal_integrity_error(
            self, pathname, proof, reason, expected_basis, computed_basis):
        """Tell ServerSession that the integrity check of a
        pathname to delete locally has failed.

        This method is meant to be called by Workers.

        @param pathname:
                    String representing the pathname.
        @param reason:
                    String shortly describing the error.
        @param expected_basis:
                    The trusted basis. It's the one the user had
                    accepted when the sync started.
        @param computed_basis:
                    The basis returned by the IntegrityManager when
                    computing the given proof object. Possibly None.
        """
        cmd = Command('INTEGRITYERRORONDELETELOCAL')
        cmd.pathname = pathname
        cmd.proof = proof
        cmd.reason = reason
        cmd.expected_basis = expected_basis
        cmd.computed_basis = computed_basis
        self._input_queue.put(cmd, 'systemcommand')
Пример #13
0
    def run(self):
        """Implementation of the threading.Thread.run() method."""
        self._started = True
        try:
            self.worker_pool.start_workers()
            self.keepalive_timer.start()
            self.current_state = StateRegister.get('DisconnectedState')
            curr_basis = self.current_state._load_trusted_basis()
            self.integrity_manager.setCurrentBasis(curr_basis)
            self.logger.info(u'Current basis is: %s' % curr_basis)
            self.current_state._on_entering()
            self._internal_facade.set_global_status(GStatuses.NC_STOPPED)
            if self.auto_start:
                self._input_queue.put(Command('CONNECT'), 'sessioncommand')
            self.cryptoAdapter.start()
            self._scheduler.schedule_action(self.check_encrypted_folder,
                                            name='check_encrypted_folder',
                                            seconds=5,
                                            repeating=True)

            # The event loop
            self._main_loop()

        except UnexpectedMessageException as e:
            self.logger.critical(
                u"Received an unexpected message from the Server while in "
                u"state '%s': %s. Forcing termination." %
                (self.current_state.__class__, str(e)))
            raise

        except ProtocolException as e:
            self.logger.critical(
                u"Detected an unrecoverable error, forcing termination: %s" %
                str(e))
            # Pre-emptive release, just stop before messing up the server
            self.release_network_resources()
            raise

        except Exception as e:
            self.logger.critical(
                u"Forcing termination due to uncaught exception '%s': %s" %
                (e.__class__, e))
            self.logger.debug(u"Last error stacktrace:\n%s" %
                              traceback.format_exc())
            # Pre-emptive release, just stop before messing up the server
            self.release_network_resources()
            raise
Пример #14
0
    def terminate(self):
        """
        Termination routine for this component.

        Stops the running thread and releases any acquired resource.
        """
        self.logger.debug(u"Terminating Server Session...")
        if self._started:
            self.must_die.set()
            self.worker_pool.terminate()
            self._input_queue.put(Command('TERMINATE'), 'usercommand')
            self.transaction.can_be_committed.set()
            self.join() if self is not threading.current_thread() else None
            self.keepalive_timer.terminate()
            self.release_network_resources()
            self.cryptoAdapter.terminate()
        self.logger.debug(u"Server Session terminanted.")
 def run(self):
     self.started = True
     try:
         msg = None
         while not self._termination_requested():
             if msg == None:
                 msg = self.output_message_queue.get()
             if msg == POISON_PILL:
                 continue
             _, ready, _ = select([], [self.sock], [], 1)
             if ready:
                 self._send_message(msg)
                 msg = None
     except Exception as exception:
         self.logger.warning(u"Detected a connection problem, aborting: %s",
                             exception)
         self._session_queue.put(Command('BROKENCONNECTION'),
                                 'sessioncommand')
Пример #16
0
 def _handle_message_CHALLENGE_VERIFY_RESPONSE(self, message):
     """Received a reply to our challenge. Did the server authenticate
     us? If not, maybe another client is already connected to this
     account.
     """
     auth_result = message.getParameter('result')
     if auth_result:
         self.logger.info(u"Client has successfully authenticated itself.")
         self._context.output_message_queue.put(
             READY_FOR_SERVICE('READY_FOR_SERVICE'))
         self._context.session_id = message.get_session_id()
         self.logger.debug(u"Received Session id %s from server" %
                           self._context.session_id)
         self._context.keepalive_timer.resume_execution()
         self._set_next_state(StateRegister.get('ReadyForServiceState'))
     else:
         self.logger.error(u"Server rejected client authentication.")
         self._context._internal_facade.set_global_status(
             GStatuses.NC_NOTAUTHORIZED)
         if message.is_other_client_connected():
             client = message.get_other_connected_client()
             if client["client_id"] == 0:
                 other_client_message = u"Your web client is already connected"
             else:
                 other_client_message = \
                     u"Client number %s from computer %s already connected" \
                     % (client["client_id"], client["hostname"])
             self.logger.warning(other_client_message)
             self._context.disconnect_other_client = True
             force_disconnection = self._context._ui_controller.ask_for_user_input(
                 "other_client_connected", client["client_id"],
                 client["hostname"])
             if not force_disconnection == 'ok':
                 self._context._internal_facade.pause()
                 return
             else:
                 self._context.current_state._set_next_state(
                     StateRegister.get('DisconnectedState'))
                 self._context._input_queue.put(Command('CONNECT'),
                                                'sessioncommand')
                 return
         else:
             relink_user(self)
Пример #17
0
    def signal_download_integrity_error(
            self, operation, reason,
            expected_etag, expected_basis,
            actual_etag, computed_basis):
        """Tell ServerSession that the integrity check of a downloaded
        file has failed.

        This method is meant to be called by Workers.

        @param operation:
                    Instance of PathnameOperation. Remember that it
                    contains also its Proof object.
        @param reason:
                    String shortly describing the error.
        @param expected_etag:
                    The etag the file was expected to have. It's the one
                    communicated by the server in its file list.
        @param expected_basis:
                    The trusted basis. It's the one the user had
                    accepted when the sync started.
        @param actual_etag:
                    The etag the file has turned to have after being
                    downloaded.
        @param computed_basis:
                    The basis returned by the IntegrityManager when
                    computing the given proof object. Possibly None.
        """
        cmd = Command('INTEGRITYERRORONDOWNLOAD')
        cmd.operation = operation
        cmd.proof = operation.download_info['proof']
        cmd.reason = reason
        cmd.expected_etag = expected_etag
        cmd.expected_basis = expected_basis
        cmd.actual_etag = actual_etag
        cmd.computed_basis = computed_basis
        self._input_queue.put(cmd, 'systemcommand')
Пример #18
0
 def disconnect(self):
     """Make the client disconnect from the client, if connected."""
     # TODO: this method has been temporarly replaced by PAUSE
     self._input_queue.put(Command('DISCONNECT'), 'usercommand')
Пример #19
0
 def commit(self):
     """Make the client commit the current transaction."""
     self._input_queue.put(Command('USERCOMMIT'), 'usercommand')
Пример #20
0
 def _handle_command_BROKENCONNECTION(self, command):
     """The connection to the server is broken.
     """
     self.logger.info(u"Detected disconnection from the server")
     self._set_next_state(StateRegister.get('DisconnectedState'))
     self._context._input_queue.put(Command('CONNECT'), 'sessioncommand')
 def _handle_command_USERCOMMIT(self, message):
     """The user has asked to commit, the operation will be recovered on
     next R&T loop.
     """
     self._set_next_state(StateRegister.get('ReplicationAndTransferState'))
     self._context._input_queue.put(Command('USERCOMMIT'), 'sessioncommand')
 def _wake_me_up(self):
     """Callback for the scheduler.
     """
     self._context._input_queue.put(Command('REDECLAREOPERATION'), 'sessioncommand')
 def _check_time_to_commit(self):
     """If it's time to commit, so be it.
     """
     if self._is_time_to_commit():
         self._context._input_queue.put(Command('COMMIT'), 'sessioncommand')
Пример #24
0
 def connect(self):
     """Make the client connect to the server."""
     self._input_queue.put(Command('CONNECT'), 'usercommand')