Esempio n. 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')
Esempio n. 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')
Esempio n. 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')
Esempio n. 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')
Esempio n. 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
Esempio n. 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')
Esempio n. 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')
Esempio n. 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')
Esempio n. 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')
    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')
Esempio n. 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
Esempio n. 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')
Esempio n. 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)
    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')
Esempio n. 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')
Esempio n. 19
0
 def commit(self):
     """Make the client commit the current transaction."""
     self._input_queue.put(Command('USERCOMMIT'), 'usercommand')
Esempio n. 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')
Esempio n. 24
0
 def connect(self):
     """Make the client connect to the server."""
     self._input_queue.put(Command('CONNECT'), 'usercommand')