예제 #1
0
  def applySyncCommand(self, subscription_path, response_message_id,
                       activate_kw, **kw):
    """
    This methods is intented to be called by asynchronous engine in activity to
    apply sync commands for a subset of data
    As engines are not zodb object, the tool acts as a placeholder for method
    that need to be called in activities
    """
    subscription = self.restrictedTraverse(subscription_path)
    assert subscription is not None, "Impossible to find subscription %s" \
        % (subscription_path)
    # Build Message
    if response_message_id:
      syncml_response = SyncMLResponse()
      syncml_response.addHeader(
        session_id=subscription.getSessionId(),
        message_id=response_message_id,
        target=subscription.getUrlString(),
        source=subscription.getSubscriptionUrlString())
      syncml_response.addBody()
    else:
      syncml_response = None

    subscription.applySyncCommand(syncml_response=syncml_response, **kw)

    # Send the message in activity to prevent recomputing data in case of
    # transport failure
    if syncml_response:
      syncml_logger("---- %s sending %s notifications of sync"
                    % (subscription.getTitle(),
                       syncml_response.sync_confirmation_counter))
      subscription.activate(activity="SQLQueue",
                            # group_method_id=None,
                            # group_method_cost=.05,
                            tag=activate_kw).sendMessage(xml=str(syncml_response))
예제 #2
0
 def _generateBaseResponse(self, subscription):
   syncml_response = SyncMLResponse()
   syncml_response.addHeader(
     session_id=subscription.getSessionId(),
     message_id=subscription.getNextMessageId(),
     target=subscription.getUrlString(),
     source=subscription.getSubscriptionUrlString())
   syncml_response.addBody()
   return syncml_response
예제 #3
0
  def sendSyncCommand(self, id_list, message_id, subscription_path,
                      activate_kw, is_final_message=False):
    """
    This methods is intented to be called by asynchronous engine in activity to
    send sync commands for a subset of data
    As engines are not zodb object, the tool acts as a placeholder for method
    that need to be called in activities
    """
    subscription = self.restrictedTraverse(subscription_path)
    assert subscription is not None, "Impossible to find subscription %s" \
        % (subscription_path)
    # Build Message
    syncml_response = SyncMLResponse()
    syncml_response.addHeader(
      session_id=subscription.getSessionId(),
      message_id=message_id,
      target=subscription.getUrlString(),
      source=subscription.getSubscriptionUrlString())
    syncml_response.addBody()


    subscription._getSyncMLData(
      syncml_response=syncml_response,
      id_list=id_list,
      )

    if is_final_message:
      # Notify that all modifications were sent
      syncml_response.addFinal()

    # Send the message in activity to prevent recomputing data in case of
    # transport failure
    # activate_kw["group_method_id"] = None
    # activate_kw["group_method_cost"] = .05
    subscription.activate(**activate_kw).sendMessage(xml=str(syncml_response))
예제 #4
0
    def checkAndSendDeleteMessage(self, message_list):
        """
    This is a group method that will be invoked for a message list
    It check signature synchronization state to know which one has
    to be deleted and send the syncml message
    """
        syncml_logger.warning("Checking deleted signature on %s" % (self.getRelativeUrl(),))
        to_delete_id_list = []
        for m in message_list:
            if m[0].getValidationState() == "not_synchronized":
                to_delete_id_list.append(m[0].getId())
        syncml_logger.warning("\tdeleted object is %s" % (to_delete_id_list,))
        if len(to_delete_id_list):
            syncml_response = SyncMLResponse()
            syncml_response.addHeader(
                session_id=self.getSessionId(),
                message_id=self.getNextMessageId(),
                target=self.getUrlString(),
                source=self.getSubscriptionUrlString(),
            )
            syncml_response.addBody()
            for gid in to_delete_id_list:
                syncml_response.addDeleteCommand(gid=gid)

            syncml_logger.info("%s sendDeleteCommand for %s" % (self.getRelativeUrl(), to_delete_id_list))
            self.activate(
                activity="SQLQueue", tag="%s_delete" % (self.getRelativeUrl(),), priority=ACTIVITY_PRIORITY
            ).sendMessage(xml=str(syncml_response))
예제 #5
0
 def sendSyncCommand(self, min_gid, max_gid, message_id, activate_kw):
   """
   This methods is intented to be called by asynchronous engine in activity to
   send sync commands for a subset of data
   """
   # Build Message
   syncml_response = SyncMLResponse()
   syncml_response = self.generateBaseResponse(message_id)
   self._getSyncMLData(
     syncml_response=syncml_response,
     min_gid=min_gid,
     max_gid=max_gid,
     )
   # Send the message in activity to prevent recomputation of data in case of
   # transport failure
   # activate_kw["group_method_id"] = None
   # activate_kw["group_method_cost"] = .05
   self.activate(**activate_kw).sendMessage(xml=str(syncml_response))
예제 #6
0
 def generateBaseResponse(self, message_id=None):
   """
   Return a message containing default headers
   """
   if not message_id:
     message_id=self.getNextMessageId(),
   syncml_response = SyncMLResponse()
   syncml_response.addHeader(
     session_id=self.getSessionId(),
     message_id=message_id,
     target=self.getUrlString(),
     source=self.getSubscriptionUrlString())
   syncml_response.addBody()
   return syncml_response
예제 #7
0
    def _sendFinalMessage(self):
        """
    Send an empty message containing the final tag to notify the end of
    the "sending_modification" stage of the synchronization
    """
        syncml_response = SyncMLResponse()
        syncml_response.addHeader(
            session_id=self.getSessionId(),
            message_id=self.getNextMessageId(),
            target=self.getUrlString(),
            source=self.getSubscriptionUrlString(),
        )
        syncml_response.addBody()
        syncml_response.addFinal()

        final_activate_kw = {
            "after_method_id": ("processServerSynchronization", "processClientSynchronization"),
            "priority": ACTIVITY_PRIORITY + 1,
            "tag": "%s_delete" % (self.getRelativeUrl(),),
        }
        syncml_logger.warning("Sending final message for modificationson on %s" % (self.getRelativeUrl(),))
        self.activate(**final_activate_kw).sendMessage(xml=str(syncml_response))
예제 #8
0
  def processServerInitialization(self, publication, syncml_request, subscriber,
                                  alert_dict):
    """
    This is the method called on server side when initializing a
    new synchronization.

    This method is called by client on server. Server will generate
    Package 2 messages based on what it got from clients

    Server will returns :
    - Header with credential if authentication is needed
    - Status answering the authentication & alert commands of the client
    - Alert command for each database to be synchronized
   (Following messages/commands are not implemented)
    - Status about the device information if sent by client
    - Result element containing device information if client requested it
    - Put command if the server want to send its service capabilities
    - Get command if the server want to get the client service capabilities
    """
    if subscriber is None:
      # first synchronization, create the subscribtion object under the publication
      # it will be used to store status of synchronization
      syncml_logger.info("\t\tCreating a subscriber")
      # Define source/destination on subscriber as it will be used by protocol
      # Source is server/publication, Destination is client/subscription
      subscriber = publication.createUnrestrictedSubscriber(
        # Publication information
        source_reference=alert_dict['target'],
        subscription_url_string=syncml_request.header['target'],
        # Subscription information
        destination_reference=alert_dict['source'],
        url_string=syncml_request.header['source'],
        # Other information copied from publication
        xml_binding_generator_method_id=
          publication.getXmlBindingGeneratorMethodId(),
        conduit_module_id=publication.getConduitModuleId(),
        list_method_id=publication.getListMethodId(),
        gid_generator_method_id=publication.getGidGeneratorMethodId(),
        source=publication.getSource(),
        synchronization_id_generator_method_id =
          publication.getSynchronizationIdGeneratorMethodId(), # XXX Deprecated
        is_activity_enabled = publication.getIsActivityEnabled(),
        # Protocol information
        syncml_alert_code="syncml_alert_code/%s" %(alert_dict["code"],),
        session_id=syncml_request.header['session_id'],
        last_message_id=syncml_request.header['message_id'],
        )
    else:
      if subscriber.getSessionId() == syncml_request.header['session_id']:
        # We do not start a new session migth be a duplicated message
        if not subscriber.checkCorrectRemoteMessageId(
            syncml_request.header["message_id"]):
          syncml_logger.warning("Resending last init message")
          return subscriber.getLastSentMessage("")
      else:
        # XXX must check that previous session ended before
        subscriber.edit(session_id=syncml_request.header['session_id'])

    if alert_dict["code"] in ('two_way', 'slow_sync',
                              'one_way_from_server',
                              'refresh_from_client_only',
                              'one_way_from_client'):
      # Make sure we update configuration based on publication data
      # so that manual edition is propagated
      # XXX Must check all properties that must be setted
      subscriber.setXmlBindingGeneratorMethodId(
        publication.getXmlBindingGeneratorMethodId())
      subscriber.setConduitModuleId(publication.getConduitModuleId())
    else:
      raise NotImplementedError('Alert code not handled yet: %r'
                                % syncml_request.alert['data'])


    syncml_logger.info('--- Starting synchronization on server side : %s in mode %s ---'
                       % (publication.getPath(), alert_dict["data"]))
    # at the begining of the synchronization the subscriber is not authenticated
    if subscriber.getAuthenticationState() == 'logged_in':
      subscriber.logout()

    if not subscriber.getSynchronizationState() == "initializing":
      # This can be called many time in sync init when credentials failed
      subscriber.initialize()  # Workflow action

    # XXX it must be known that here we do a server layer authentication,
    # The database layer authentication is not implemented, although we defined
    # credentials on pub/sub documents
    authentication_code = None
    if not len(syncml_request.credentials):
      syncml_logger.info("\tReceived message without credential, will ask for them")
      authentication_code = "missing_credentials"
    else:
      # First try to authenticate the client
      if syncml_request.credentials['type'] == "syncml:auth-md5":
        # MD5 authentication is not supported
        raise NotImplementedError("MD5 authentication not supported")

      if syncml_request.credentials['type'] == publication.getAuthenticationType():
        decoded = decode(syncml_request.credentials['format'],
                         syncml_request.credentials['data'])
        if decoded and ':' in decoded:
          login, password = decoded.split(':')
          # TODO: make it work for users existing anywhere
          user_folder = publication.getPortalObject().acl_users
          for plugin_name, plugin in user_folder._getOb('plugins')\
              .listPlugins(IAuthenticationPlugin):
            if plugin.authenticateCredentials(
                {'login': login, 'password': password}) is not None:
              subscriber.login()
              syncml_logger.info("\tServer accepted authentication for user %s"
                                 % (login,))
              authentication_code = 'authentication_accepted'
              subscriber._loginUser(login)
              subscriber._edit(authenticated_user=login)
              break
            else:
              # When authentication is invalid, a second try is possible for the client
              # if header_kw["message_id"] == 1:
              #   authentication_code = 'missing_credentials'
              # else:
              authentication_code = 'invalid_credentials'
              syncml_logger.error("\tServer rejected authentication for %s" % (login))
        else:
          # if header_kw["message_id"] == 1:
          #   authentication_code = 'missing_credentials'
          # else:
          authentication_code = 'invalid_credentials'
          syncml_logger.warning(
            "\tCredentials does not look like auth-basis, decoded value is '%s,'"
            % (decoded))
      else:
        # To complete, must send a challenge message
        syncml_logger.warning(
          "\tAuthentication type does not math, from client '%s', from server '%s'" %(
            syncml_request.credentials['type'],
            publication.getAuthenticationType()))
        authentication_code = 'missing_credentials'


    # Build the xml message for the Sync initialization package
    syncml_response = SyncMLResponse()
    syncml_response.addHeader(session_id=subscriber.getSessionId(),
                             message_id=subscriber.getNextMessageId(),
                             target=syncml_request.header['source'],
                             source=publication.getUrlString())
    # syncml body
    syncml_response.addBody()

    if authentication_code == 'authentication_accepted':
      sync_type_validation_code = "success"
      sync_type = alert_dict["data"]

      # Now check the alert command sent by client
      if alert_dict['code'] == 'slow_sync' and \
          subscriber.getNextAnchor() != NULL_ANCHOR:
        # If slow sync, then resend everything
        syncml_logger.info("\tClient requested a slow sync, signatures are reset on server side")
        subscriber.resetAllSignatures()
        subscriber.resetAnchorList()
      elif subscriber.getNextAnchor() != alert_dict['last_anchor']:
        # Anchor does not match, must start a slow sync
        syncml_logger.warning("\tAnchor does not match on server, \
           received is %s, stored %s. Will start a slow sync"
           %(alert_dict['last_anchor'],
             subscriber.getNextAnchor()))
        sync_type_validation_code = "command_failed" # Error 500
        sync_type = 'slow_sync'
      else:
        # Last synchronization went fine
        subscriber.setNextAnchor(alert_dict['next_anchor'])

      # Two status message must be sent here
      # - One answering about the authentication
      # - One answering about the database synchronization status
      # Then one aler message per database is send, client must
      # follow sync type in this alert message is status was KO

      # Status about authentication
      syncml_response.addStatusMessage(
        message_reference=syncml_request.header['message_id'],
        command_reference=0,  # answer to a header
        command='SyncHdr',
        status_code=authentication_code,
        target=syncml_request.header['target'],
        source=syncml_request.header['source']
        )

      # Status about database sync
      syncml_response.addStatusMessage(
        message_reference=syncml_request.header['message_id'],
        command_reference=alert_dict['command_id'],
        command='Alert',
        status_code=sync_type_validation_code,
        target=alert_dict['target'],
        source=alert_dict['source'],
        anchor=alert_dict['next_anchor'])

      # one alert message for each database to sync
      syncml_response.addAlertCommand(
        alert_code=sync_type,
        target=alert_dict['target'],
        source=alert_dict['source'],
        last_anchor=subscriber.getLastAnchor(),
        next_anchor=subscriber.getNextAnchor())

      # Server get sync commands from client first
      subscriber.processSyncRequest()
    else:
      # Add a status message with a challenge command
      syncml_response.addChallengeMessage(
        message_reference=syncml_request.header['message_id'],
        target=syncml_request.header['source'],
        source=syncml_request.header['target'],
        authentication_format=publication.getAuthenticationFormat(),
        authentication_type=publication.getAuthenticationType(),
        authentication_code=authentication_code)

    # Generate and send the message
    syncml_response.addFinal()
    if subscriber.getIsActivityEnabled():
      subscriber.activate(
        activity="SQLQueue",
        after_tag = "%s_reset" % subscriber.getPath(),
        # Wait for all reset to be done
        # before starting sync
        priority=ACTIVITY_PRIORITY,
        tag=publication.getRelativeUrl()).sendMessage(xml=str(syncml_response))
    else:
      subscriber.sendMessage(xml=str(syncml_response))

    return syncml_response
예제 #9
0
  def initializeClientSynchronization(self, subscription):
    """ Client Initialisation package to server (pkg 1)

    Client must inform the server which database it want to synchronize
    and which type og synchronization is desired.

    Options that can be included in this package :
    - authentification
    - service capabilities (PUT)

    Note that this package can be combined with package 3, this is the case of
    'Sync without separate initialization'. Client may implement it. This is not
    done here but can be a way of improvement to decrease number of messages
    exchanged.
    """
    syncml_logger.info('--- Starting synchronization on client side : %s ---'
                       % (subscription.getPath(),))
    if not subscription.getSynchronizationState() == "initializing":
      # This can be called many time in sync init when credentials failed
      subscription.initialize()  # Worflow action
    subscription.createNewAnchor()

    # The user launching the synchronization is save so that when we get an
    # request from the the server, it get process with this user
    # XXX this can be managed using credentials like on server part ?
    user_id = getSecurityManager().getUser().getId()
    subscription._loginUser(user_id)
    subscription._edit(authenticated_user=user_id)
    if subscription.getAuthenticationState() != 'logged_in':
      # Workflow action
      subscription.login()

    subscription.indexSourceData(client=True)

    # Create the package 1
    syncml_response = SyncMLResponse()

    # Create the header part
    session_id = subscription.generateNewSessionId()
    subscription.setSessionId(session_id)
    header_kw = {'session_id': session_id,
                 'message_id': subscription.getNextMessageId(),
                 'target': subscription.getUrlString(),
                 'source': subscription.getSubscriptionUrlString(),
                 # Include credentials
                 'user_id': subscription.getUserId(),
                 'password': subscription.getPassword(),
                 'authentication_format':
                   subscription.getAuthenticationFormat(),
                 'authentication_type':
                   subscription.getAuthenticationType()
                 }
    syncml_response.addHeader(**header_kw)

    # Create the body part which consists of :
    # - one alert command per database to sync, each containing its anchors
    # - one put command (optional)
    # - one get command if client when device capabilities of server (optional)
    syncml_response.addBody()
    # Here we only run one synchronization at a time, so only one alert command
    # is created
    syncml_response.addAlertCommand(
      alert_code=subscription.getSyncmlAlertCode(),
      target=subscription.getDestinationReference(),
      source=subscription.getSourceReference(),
      last_anchor=subscription.getLastAnchor(),
      next_anchor=subscription.getNextAnchor())

    # Generate the put command
    syncml_response.addPutMessage(subscription)

    return syncml_response
예제 #10
0
    def processServerInitialization(self, publication, syncml_request, subscriber, alert_dict):
        """
    This is the method called on server side when initializing a
    new synchronization.

    This method is called by client on server. Server will generate
    Package 2 messages based on what it got from clients

    Server will returns :
    - Header with credential if authentication is needed
    - Status answering the authentication & alert commands of the client
    - Alert command for each database to be synchronized
   (Following messages/commands are not implemented)
    - Status about the device information if sent by client
    - Result element containing device information if client requested it
    - Put command if the server want to send its service capabilities
    - Get command if the server want to get the client service capabilities
    """
        if subscriber is None:
            # first synchronization, create the subscribtion object under the publication
            # it will be used to store status of synchronization
            syncml_logger.info("\t\tCreating a subscriber")
            # Define source/destination on subscriber as it will be used by protocol
            # Source is server/publication, Destination is client/subscription
            subscriber = publication.createUnrestrictedSubscriber(
                # Publication information
                source_reference=alert_dict["target"],
                subscription_url_string=syncml_request.header["target"],
                # Subscription information
                destination_reference=alert_dict["source"],
                url_string=syncml_request.header["source"],
                # Other information copied from publication
                xml_binding_generator_method_id=publication.getXmlBindingGeneratorMethodId(),
                conduit_module_id=publication.getConduitModuleId(),
                list_method_id=publication.getListMethodId(),
                gid_generator_method_id=publication.getGidGeneratorMethodId(),
                source=publication.getSource(),
                synchronization_id_generator_method_id=publication.getSynchronizationIdGeneratorMethodId(),  # XXX Deprecated
                is_activity_enabled=publication.getIsActivityEnabled(),
                # Protocol information
                syncml_alert_code="syncml_alert_code/%s" % (alert_dict["code"],),
                session_id=syncml_request.header["session_id"],
                last_message_id=syncml_request.header["message_id"],
            )
        else:
            if subscriber.getSessionId() == syncml_request.header["session_id"]:
                # We do not start a new session migth be a duplicated message
                if not subscriber.checkCorrectRemoteMessageId(syncml_request.header["message_id"]):
                    syncml_logger.warning("Resending last init message")
                    return subscriber.getLastSentMessage("")
            else:
                # XXX must check that previous session ended before
                subscriber.edit(session_id=syncml_request.header["session_id"])

        if alert_dict["code"] in (
            "two_way",
            "slow_sync",
            "one_way_from_server",
            "refresh_from_client_only",
            "one_way_from_client",
        ):
            # Make sure we update configuration based on publication data
            # so that manual edition is propagated
            # XXX Must check all properties that must be setted
            subscriber.setXmlBindingGeneratorMethodId(publication.getXmlBindingGeneratorMethodId())
            subscriber.setConduitModuleId(publication.getConduitModuleId())
        else:
            raise NotImplementedError("Alert code not handled yet: %r" % syncml_request.alert["data"])

        syncml_logger.info(
            "--- Starting synchronization on server side : %s in mode %s ---"
            % (publication.getPath(), alert_dict["data"])
        )
        # at the begining of the synchronization the subscriber is not authenticated
        if subscriber.getAuthenticationState() == "logged_in":
            subscriber.logout()

        if not subscriber.getSynchronizationState() == "initializing":
            # This can be called many time in sync init when credentials failed
            subscriber.initialize()  # Workflow action

        # XXX it must be known that here we do a server layer authentication,
        # The database layer authentication is not implemented, although we defined
        # credentials on pub/sub documents
        authentication_code = None
        if not len(syncml_request.credentials):
            syncml_logger.info("\tReceived message without credential, will ask for them")
            authentication_code = "missing_credentials"
        else:
            # First try to authenticate the client
            if syncml_request.credentials["type"] == "syncml:auth-md5":
                # MD5 authentication is not supported
                raise NotImplementedError("MD5 authentication not supported")

            if syncml_request.credentials["type"] == publication.getAuthenticationType():
                decoded = decode(syncml_request.credentials["format"], syncml_request.credentials["data"])
                if decoded and ":" in decoded:
                    login, password = decoded.split(":")
                    # TODO: make it work for users existing anywhere
                    user_folder = publication.getPortalObject().acl_users
                    for plugin_name, plugin in user_folder._getOb("plugins").listPlugins(IAuthenticationPlugin):
                        if plugin.authenticateCredentials({"login": login, "password": password}) is not None:
                            subscriber.login()
                            syncml_logger.info("\tServer accepted authentication for user %s" % (login,))
                            authentication_code = "authentication_accepted"
                            subscriber._loginUser(login)
                            subscriber._edit(authenticated_user=login)
                            break
                        else:
                            # When authentication is invalid, a second try is possible for the client
                            # if header_kw["message_id"] == 1:
                            #   authentication_code = 'missing_credentials'
                            # else:
                            authentication_code = "invalid_credentials"
                            syncml_logger.error("\tServer rejected authentication for %s" % (login))
                else:
                    # if header_kw["message_id"] == 1:
                    #   authentication_code = 'missing_credentials'
                    # else:
                    authentication_code = "invalid_credentials"
                    syncml_logger.warning(
                        "\tCredentials does not look like auth-basis, decoded value is '%s,'" % (decoded)
                    )
            else:
                # To complete, must send a challenge message
                syncml_logger.warning(
                    "\tAuthentication type does not math, from client '%s', from server '%s'"
                    % (syncml_request.credentials["type"], publication.getAuthenticationType())
                )
                authentication_code = "missing_credentials"

        # Build the xml message for the Sync initialization package
        syncml_response = SyncMLResponse()
        syncml_response.addHeader(
            session_id=subscriber.getSessionId(),
            message_id=subscriber.getNextMessageId(),
            target=syncml_request.header["source"],
            source=publication.getUrlString(),
        )
        # syncml body
        syncml_response.addBody()

        if authentication_code == "authentication_accepted":
            sync_type_validation_code = "success"
            sync_type = alert_dict["data"]

            # Now check the alert command sent by client
            if alert_dict["code"] == "slow_sync" and subscriber.getNextAnchor() != NULL_ANCHOR:
                # If slow sync, then resend everything
                syncml_logger.info("\tClient requested a slow sync, signatures are reset on server side")
                subscriber.resetAllSignatures()
                subscriber.resetAnchorList()
            elif subscriber.getNextAnchor() != alert_dict["last_anchor"]:
                # Anchor does not match, must start a slow sync
                syncml_logger.warning(
                    "\tAnchor does not match on server, \
           received is %s, stored %s. Will start a slow sync"
                    % (alert_dict["last_anchor"], subscriber.getNextAnchor())
                )
                sync_type_validation_code = "command_failed"  # Error 500
                sync_type = "slow_sync"
            else:
                # Last synchronization went fine
                subscriber.setNextAnchor(alert_dict["next_anchor"])

            # Two status message must be sent here
            # - One answering about the authentication
            # - One answering about the database synchronization status
            # Then one aler message per database is send, client must
            # follow sync type in this alert message is status was KO

            # Status about authentication
            syncml_response.addStatusMessage(
                message_reference=syncml_request.header["message_id"],
                command_reference=0,  # answer to a header
                command="SyncHdr",
                status_code=authentication_code,
                target=syncml_request.header["target"],
                source=syncml_request.header["source"],
            )

            # Status about database sync
            syncml_response.addStatusMessage(
                message_reference=syncml_request.header["message_id"],
                command_reference=alert_dict["command_id"],
                command="Alert",
                status_code=sync_type_validation_code,
                target=alert_dict["target"],
                source=alert_dict["source"],
                anchor=alert_dict["next_anchor"],
            )

            # one alert message for each database to sync
            syncml_response.addAlertCommand(
                alert_code=sync_type,
                target=alert_dict["target"],
                source=alert_dict["source"],
                last_anchor=subscriber.getLastAnchor(),
                next_anchor=subscriber.getNextAnchor(),
            )

            # Server get sync commands from client first
            subscriber.processSyncRequest()
        else:
            # Add a status message with a challenge command
            syncml_response.addChallengeMessage(
                message_reference=syncml_request.header["message_id"],
                target=syncml_request.header["source"],
                source=syncml_request.header["target"],
                authentication_format=publication.getAuthenticationFormat(),
                authentication_type=publication.getAuthenticationType(),
                authentication_code=authentication_code,
            )

        # Generate and send the message
        syncml_response.addFinal()
        if subscriber.getIsActivityEnabled():
            subscriber.activate(
                activity="SQLQueue",
                after_tag="%s_reset" % subscriber.getPath(),
                # Wait for all reset to be done
                # before starting sync
                priority=ACTIVITY_PRIORITY,
                tag=publication.getRelativeUrl(),
            ).sendMessage(xml=str(syncml_response))
        else:
            subscriber.sendMessage(xml=str(syncml_response))

        return syncml_response
예제 #11
0
    def initializeClientSynchronization(self, subscription):
        """ Client Initialisation package to server (pkg 1)

    Client must inform the server which database it want to synchronize
    and which type og synchronization is desired.

    Options that can be included in this package :
    - authentification
    - service capabilities (PUT)

    Note that this package can be combined with package 3, this is the case of
    'Sync without separate initialization'. Client may implement it. This is not
    done here but can be a way of improvement to decrease number of messages
    exchanged.
    """
        syncml_logger.info("--- Starting synchronization on client side : %s ---" % (subscription.getPath(),))
        if not subscription.getSynchronizationState() == "initializing":
            # This can be called many time in sync init when credentials failed
            subscription.initialize()  # Worflow action
        subscription.createNewAnchor()

        # The user launching the synchronization is save so that when we get an
        # request from the the server, it get process with this user
        # XXX this can be managed using credentials like on server part ?
        user_id = getSecurityManager().getUser().getId()
        subscription._loginUser(user_id)
        subscription._edit(authenticated_user=user_id)
        if subscription.getAuthenticationState() != "logged_in":
            # Workflow action
            subscription.login()

        subscription.indexSourceData(client=True)

        # Create the package 1
        syncml_response = SyncMLResponse()

        # Create the header part
        session_id = subscription.generateNewSessionId()
        subscription.setSessionId(session_id)
        header_kw = {
            "session_id": session_id,
            "message_id": subscription.getNextMessageId(),
            "target": subscription.getUrlString(),
            "source": subscription.getSubscriptionUrlString(),
            # Include credentials
            "user_id": subscription.getUserId(),
            "password": subscription.getPassword(),
            "authentication_format": subscription.getAuthenticationFormat(),
            "authentication_type": subscription.getAuthenticationType(),
        }
        syncml_response.addHeader(**header_kw)

        # Create the body part which consists of :
        # - one alert command per database to sync, each containing its anchors
        # - one put command (optional)
        # - one get command if client when device capabilities of server (optional)
        syncml_response.addBody()
        # Here we only run one synchronization at a time, so only one alert command
        # is created
        syncml_response.addAlertCommand(
            alert_code=subscription.getSyncmlAlertCode(),
            target=subscription.getDestinationReference(),
            source=subscription.getSourceReference(),
            last_anchor=subscription.getLastAnchor(),
            next_anchor=subscription.getNextAnchor(),
        )

        # Generate the put command
        syncml_response.addPutMessage(subscription)

        return syncml_response