PublicationSynchronization.py 6.61 KB
Newer Older
Jean-Paul Smets's avatar
Jean-Paul Smets committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
##############################################################################
#
# Copyright (c) 2003 Nexedi SARL and Contributors. All Rights Reserved.
#          Sebastien Robin <seb@nexedi.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################

import smtplib # to send emails
from Publication import Publication,Subscriber
from Subscription import Signature
from xml.dom.ext.reader.Sax2 import FromXmlStream, FromXml
Sebastien Robin's avatar
Sebastien Robin committed
33
from xml.dom.minidom import parse, parseString
Jean-Paul Smets's avatar
Jean-Paul Smets committed
34 35
from XMLSyncUtils import XMLSyncUtils
from Conduit.ERP5Conduit import ERP5Conduit
36
from Products.CMFCore.utils import getToolByName
Jean-Paul Smets's avatar
Jean-Paul Smets committed
37 38 39 40 41
import commands
from zLOG import LOG

class PublicationSynchronization(XMLSyncUtils):

42
  def PubSyncInit(self, publication=None, xml_client=None, subscriber=None, sync_type=None):
Jean-Paul Smets's avatar
Jean-Paul Smets committed
43 44 45 46 47 48 49 50 51 52 53 54 55 56
    """
      Read the client xml message
      Send the first XML message from the server
    """
    LOG('PubSyncInit',0,'Starting... publication: %s' % str(publication))

    alert = None
    # Get informations from the body
    if xml_client is not None: # We have received a message
      last_anchor = self.getAlertLastAnchor(xml_client)
      next_anchor = self.getAlertNextAnchor(xml_client)
      alert = self.checkAlert(xml_client)
      alert_code = self.getAlertCode(xml_client)

57 58 59 60 61
      # If slow sync, then resend everything
      if alert_code == self.SLOW_SYNC:
        LOG('Warning !!!, reseting client synchronization for subscriber:',0,subscriber)
        subscriber.resetAllSignatures()

Jean-Paul Smets's avatar
Jean-Paul Smets committed
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
      # Check if the last time synchronization is the same as the client one
      if subscriber.getNextAnchor() != last_anchor:
        if last_anchor == None:
          LOG('PubSyncInit',0,'anchor null')
        else:
          message = "bad anchors in PubSyncInit! " + subscriber.getNextAnchor() + \
                    " and " + last_anchor
          LOG('PubSyncInit',0,message)
      else:
        subscriber.setNextAnchor(next_anchor)

      # We have to set every object as NOT_SYNCHRONIZED
      subscriber.startSynchronization()
    else:
      # We have started the sync from the server (may be for a conflict resolution)
      pass

    xml = ""
    #if alert is not None:
    if 1:
      # Prepare the xml message for the Sync initialization package
      cmd_id = 1 # specifies a SyncML message-unique command identifier
      xml = ""
      xml += '<SyncML>\n'

      # syncml header
      xml += self.SyncMLHeader(subscriber.getSessionId(), "1",
          subscriber.getSubscriptionUrl(), publication.getPublicationUrl())

      # syncml body
      xml += ' <SyncBody>\n'
      # alert message
94
      xml += self.SyncMLAlert(cmd_id, sync_type, subscriber.getSubscriptionUrl(),
Jean-Paul Smets's avatar
Jean-Paul Smets committed
95 96 97 98 99 100
            publication.getPublicationUrl(), subscriber.getLastAnchor(), subscriber.getNextAnchor())
      cmd_id += 1
      xml += ' </SyncBody>\n'

      xml += '</SyncML>\n'

101
    self.sendResponse(from_url=publication.getPublicationUrl(),
102
         to_url=subscriber.getSubscriptionUrl(),sync_id=publication.getTitle(),xml=xml,
103 104
         domain=publication)
    return {'has_response':1,'xml':xml}
Jean-Paul Smets's avatar
Jean-Paul Smets committed
105 106 107 108 109 110 111 112


  def PubSync(self, id, msg=None, RESPONSE=None, subscriber=None):
    """
      This is the synchronization method for the server
    """
    LOG('PubSync',0,'Starting... id: %s' % str(id))
    # Read the request from the client
113 114 115 116
    xml_client = msg
    if xml_client is None:
      xml_client = self.readResponse(from_url='file://tmp/sync_server')
    LOG('PubSync',0,'Starting... msg: %s' % str(xml_client))
117
    result = None
118
    publication = self.getPublication(id)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
119 120

    if xml_client is not None:
121
      if type(xml_client) in (type('a'),type(u'a')):
Sebastien Robin's avatar
Sebastien Robin committed
122 123
        xml_client = parseString(xml_client)
      first_node = xml_client.childNodes[0]
Jean-Paul Smets's avatar
Jean-Paul Smets committed
124 125 126 127

      if first_node.nodeName != "SyncML":
        LOG('PubSync',0,'This is not a SyncML Message')
        return
128
      alert_code = self.getAlertCode(xml_client)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
129 130 131 132 133 134 135 136 137 138

      # Get informations from the header
      client_header = first_node.childNodes[1]
      if client_header.nodeName != "SyncHdr":
        LOG('PubSync',0,'This is not a SyncML Header')
        return
      for subnode in client_header.childNodes:
        if subnode.nodeType == subnode.ELEMENT_NODE and subnode.nodeName == "Source":
          subscription_url = str(subnode.childNodes[0].data)
      # Get the subscriber or create it if not already in the list
139
      subscriber = publication.getSubscriber(subscription_url)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
140
      if subscriber == None:
141
        subscriber = Subscriber(publication.generateNewId(),subscription_url)
142
        subscriber.setXMLMapping(publication.getXMLMapping())
143
        publication.addSubscriber(subscriber)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
144
        # first synchronization
145
        result = self.PubSyncInit(publication,xml_client,subscriber=subscriber,sync_type=self.SLOW_SYNC)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
146

147

148
      elif self.checkAlert(xml_client) and alert_code in (self.TWO_WAY,self.SLOW_SYNC):
149
        result = self.PubSyncInit(publication=publication,
150
                         xml_client=xml_client, subscriber=subscriber,sync_type=alert_code)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
151
      else:
152
        result = self.PubSyncModif(publication, xml_client)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
153 154 155
    elif subscriber is not None:
      # This looks like we are starting a synchronization after
      # a conflict resolution by the user
156
      result = self.PubSyncInit(publication=publication,
157
                      xml_client=None, subscriber=subscriber,sync_type=self.TWO_WAY)
Jean-Paul Smets's avatar
Jean-Paul Smets committed
158 159 160

    if RESPONSE is not None:
      RESPONSE.redirect('managePublications')
161 162
    elif result is not None:
      return result
Jean-Paul Smets's avatar
Jean-Paul Smets committed
163 164 165 166 167

  def PubSyncModif(self, publication, xml_client):
    """
    The modidification message for the publication
    """
168
    return self.SyncModif(publication,xml_client)