Back to index

plone3  3.1.7
WebDAVSupport.py
Go to the documentation of this file.
00001 import tempfile
00002 import posixpath
00003 
00004 from zExceptions import MethodNotAllowed
00005 from Products.CMFCore.utils import getToolByName
00006 from Products.Archetypes.utils import shasattr, mapply
00007 # Recent enough Zopes will have this. Do we care about older ones?
00008 from ZPublisher.Iterators import IStreamIterator
00009 
00010 from zope import event
00011 from zope.lifecycleevent import ObjectModifiedEvent
00012 
00013 class PdataStreamIterator(object):
00014 
00015     __implements__ = (IStreamIterator,)
00016 
00017     def __init__(self, data, size, streamsize=1<<16):
00018         # Consume the whole data into a TemporaryFile when
00019         # constructing, otherwise we might end up loading the whole
00020         # file in memory or worse, loading objects after the
00021         # connection is closed.
00022         f = tempfile.TemporaryFile(mode='w+b')
00023 
00024         while data is not None:
00025             f.write(data.data)
00026             data = data.next
00027 
00028         assert size == f.tell(), 'Informed length does not match real length'
00029         f.seek(0)
00030 
00031         self.file = f
00032         self.size = size
00033         self.streamsize = streamsize
00034 
00035     def __iter__(self):
00036         return self
00037 
00038     def next(self):
00039         data = self.file.read(self.streamsize)
00040         if not data:
00041             self.file.close()
00042             raise StopIteration
00043         return data
00044 
00045     def __len__(self):
00046         return self.size
00047 
00048 _marker = []
00049 
00050 def collection_check(self):
00051     if not shasattr(self, '__dav_marshall__'):
00052         # Backwards-compatible, if property not set ignore.
00053         return
00054     if not self.__dav_marshall__:
00055         # If property is set to a false value, do not allow
00056         # marshalling.
00057         raise MethodNotAllowed, 'Method not supported.'
00058 
00059 def PUT(self, REQUEST=None, RESPONSE=None):
00060     """ HTTP PUT handler with marshalling support
00061     """
00062     if not REQUEST:
00063         REQUEST = self.REQUEST
00064     if not RESPONSE:
00065         RESPONSE = REQUEST.RESPONSE
00066     if not self.Schema().hasLayer('marshall'):
00067         RESPONSE.setStatus(501) # Not implemented
00068         return RESPONSE
00069 
00070     self.dav__init(REQUEST, RESPONSE)
00071     collection_check(self)
00072 
00073     self.dav__simpleifhandler(REQUEST, RESPONSE, refresh=1)
00074 
00075     file = REQUEST.get('BODYFILE', _marker)
00076     if file is _marker:
00077         data = REQUEST.get('BODY', _marker)
00078         if data is _marker:
00079             raise AttributeError, 'REQUEST neither has a BODY nor a BODYFILE'
00080     else:
00081         data = ''
00082         file.seek(0)
00083 
00084     mimetype = REQUEST.get_header('content-type', None)
00085     # mimetype, if coming from request can be like:
00086     # text/plain; charset='utf-8'
00087     #
00088     # XXX we should really parse the extra params and pass them on as
00089     # keyword arguments.
00090     if mimetype is not None:
00091         mimetype = str(mimetype).split(';')[0].strip()
00092 
00093     filename = posixpath.basename(REQUEST.get('PATH_INFO', self.getId()))
00094     # XXX remove after we are using global services
00095     # use the request to find an object in the traversal hierachy that is
00096     # able to acquire a mimetypes_registry instance
00097     # This is a hack to avoid the acquisition problem on FTP/WebDAV object
00098     # creation
00099     parents = (self,) + tuple(REQUEST.get('PARENTS', ()))
00100     context = None
00101     for parent in parents:
00102         if getToolByName(parent, 'mimetypes_registry', None) is not None:
00103             context = parent
00104             break
00105 
00106     # Marshall the data
00107     marshaller = self.Schema().getLayerImpl('marshall')
00108 
00109     args = [self, data]
00110     kwargs = {'file':file,
00111               'context':context,
00112               'mimetype':mimetype,
00113               'filename':filename,
00114               'REQUEST':REQUEST,
00115               'RESPONSE':RESPONSE}
00116     ddata = mapply(marshaller.demarshall, *args, **kwargs)
00117 
00118     if (shasattr(self, 'demarshall_hook') and self.demarshall_hook):
00119         self.demarshall_hook(ddata)
00120     self.manage_afterPUT(data, marshall_data = ddata, **kwargs)
00121     self.reindexObject()
00122     event.notify(ObjectModifiedEvent(self))
00123     
00124     RESPONSE.setStatus(204)
00125     return RESPONSE
00126 
00127 def manage_FTPget(self, REQUEST=None, RESPONSE=None):
00128     """Get the raw content for this object (also used for the WebDAV source)
00129     """
00130 
00131     if REQUEST is None:
00132         REQUEST = self.REQUEST
00133 
00134     if RESPONSE is None:
00135         RESPONSE = REQUEST.RESPONSE
00136 
00137     if not self.Schema().hasLayer('marshall'):
00138         RESPONSE.setStatus(501) # Not implemented
00139         return RESPONSE
00140 
00141     self.dav__init(REQUEST, RESPONSE)
00142     collection_check(self)
00143 
00144     marshaller = self.Schema().getLayerImpl('marshall')
00145     ddata = marshaller.marshall(self, REQUEST=REQUEST, RESPONSE=RESPONSE)
00146     if (shasattr(self, 'marshall_hook') and self.marshall_hook):
00147         ddata = self.marshall_hook(ddata)
00148 
00149     content_type, length, data = ddata
00150 
00151     RESPONSE.setHeader('Content-Type', content_type)
00152     # Only set Content-Length header length is not None. If we want to
00153     # support 'chunked' Transfer-Encoding we shouldn't set
00154     # this. However ExternalEditor *expects* it to be set if we return
00155     # a StreamIterator, so until that gets fixed we must set it.
00156     if length is not None:
00157         RESPONSE.setHeader('Content-Length', length)
00158 
00159     if type(data) is type(''):
00160         return data
00161 
00162     # We assume 'data' is a 'Pdata chain' as used by OFS.File and
00163     # return a StreamIterator.
00164     assert length is not None, 'Could not figure out length of Pdata chain'
00165     return PdataStreamIterator(data, length)
00166 
00167 def manage_afterPUT(self, data, marshall_data, file, context, mimetype,
00168                     filename, REQUEST, RESPONSE):
00169     """After webdav/ftp PUT method
00170     """
00171     pass