def mkcol(self, uri): """ create a new collection see par. 9.3 of rfc4918 """ self.parent.log_message('MKCOL: %s' % uri) cr, uid, pool, dbname, uri2 = self.get_cr(uri) if not uri2[-1]: if cr: cr.close() raise DAV_Error(409, "Cannot create nameless collection.") if not dbname: if cr: cr.close() raise DAV_Error, 409 node = self.uri2object(cr, uid, pool, uri2[:-1]) if not node: cr.close() raise DAV_Error(409, "Parent path %s does not exist" % uri2[:-1]) nc = node.child(cr, uri2[-1]) if nc: cr.close() raise DAV_Error(405, "Path already exists.") self._try_function(node.create_child_collection, (cr, uri2[-1]), "create col %s" % uri2[-1], cr=cr) cr.commit() cr.close() return True
def lock(self, uri, lock_data): """ Lock (may create) resource. Data is a dict, may contain: depth, token, refresh, lockscope, locktype, owner """ cr, uid, pool, dbname, uri2 = self.get_cr(uri) created = False if not dbname: raise DAV_Error, 409 try: node = self.uri2object(cr, uid, pool, uri2[:]) except Exception: node = False objname = misc.ustr(uri2[-1]) if not node: dir_node = self.uri2object(cr, uid, pool, uri2[:-1]) if not dir_node: raise DAV_NotFound('Parent folder not found.') # We create a new node (file) but with empty data=None, # as in RFC4918 p. 9.10.4 node = self._try_function(dir_node.create_child, (cr, objname, None), "create %s" % objname, cr=cr) if not node: cr.commit() raise DAV_Error(400, "Failed to create resource.") created = True try: node_fn = node.dav_lock except AttributeError: # perhaps the node doesn't support locks raise DAV_Error(400, 'No locks for this resource.') # Obtain the lock on the node lres, pid, token = self._try_function(node_fn, (cr, lock_data), "lock %s" % objname, cr=cr) if not lres: cr.commit() raise DAV_Error(423, "Resource already locked.") assert isinstance(lres, list), 'lres: %s' % repr(lres) try: data = mk_lock_response(self, uri, lres) cr.commit() except Exception: raise return created, data, token
def do_LOCK(self): """ Attempt to place a lock on the given resource. """ dc = self.IFACE_CLASS lock_data = {} self.log_message('LOCKing resource %s' % self.headers) body = None if self.headers.has_key('Content-Length'): l = self.headers['Content-Length'] body = self.rfile.read(atoi(l)) depth = self.headers.get('Depth', 'infinity') uri = urlparse.urljoin(self.get_baseuri(dc), self.path) uri = urllib.unquote(uri) self.log_message('do_LOCK: uri = %s' % uri) ifheader = self.headers.get('If') if ifheader: ldif = IfParser(ifheader) if isinstance(ldif, list): if len(ldif) !=1 or (not isinstance(ldif[0], TagList)) \ or len(ldif[0].list) != 1: raise DAV_Error(400, "Cannot accept multiple tokens.") ldif = ldif[0].list[0] if ldif[0] == '<' and ldif[-1] == '>': ldif = ldif[1:-1] lock_data['token'] = ldif if not body: lock_data['refresh'] = True else: lock_data['refresh'] = False lock_data.update(self._lock_unlock_parse(body)) if lock_data['refresh'] and not lock_data.get('token', False): raise DAV_Error(400, 'Lock refresh must specify token.') lock_data['depth'] = depth try: created, data, lock_token = dc.lock(uri, lock_data) except DAV_Error, (ec, dd): return self.send_status(ec, dd)
def _get_caldav_schedule_outbox_URL(self, uri): dbname, dburi = self._get_dburi(uri) if not dbname: raise DAV_NotFound pool = Pool(Transaction().database.name) try: Collection = pool.get('webdav.collection') except KeyError: raise DAV_NotFound if not getattr(Collection, 'get_schedule_outbox_URL', None): raise DAV_NotFound try: res = Collection.get_schedule_outbox_URL(dburi, cache=LOCAL.cache) except DAV_Error as exception: self._log_exception(exception) raise except Exception as exception: self._log_exception(exception) raise DAV_Error(500) uparts = list(urllib.parse.urlsplit(uri)) uparts[2] = urllib.parse.quote(dbname + res) doc = domimpl.createDocument(None, 'href', None) href = doc.documentElement href.tagName = 'D:href' huri = doc.createTextNode(urllib.parse.urlunsplit(uparts)) href.appendChild(huri) return href
def _get_caldav_calendar_user_address_set(self, uri): dbname, dburi = self._get_dburi(uri) if not dbname: raise DAV_NotFound pool = Pool(Transaction().database.name) try: Collection = pool.get('webdav.collection') except KeyError: raise DAV_NotFound if not getattr(Collection, 'get_calendar_user_address_set', None): raise DAV_NotFound try: res = Collection.get_calendar_user_address_set(dburi, cache=LOCAL.cache) except DAV_Error as exception: self._log_exception(exception) raise except Exception as exception: self._log_exception(exception) raise DAV_Error(500) doc = domimpl.createDocument(None, 'href', None) href = doc.documentElement href.tagName = 'D:href' huri = doc.createTextNode('MAILTO:' + res) href.appendChild(huri) return href
def get_data(self, uri, rrange=None): self.parent.log_message('GET: %s' % uri) cr, uid, pool, dbname, uri2 = self.get_cr(uri) try: if not dbname: raise DAV_Error, 409 node = self.uri2object(cr, uid, pool, uri2) if not node: raise DAV_NotFound2(uri2) # TODO: if node is a collection, for some specific set of # clients ( web browsers; available in node context), # we may return a pseydo-html page with the directory listing. try: res = node.open_data(cr, 'r') if rrange: assert isinstance(rrange, (tuple, list)) start, end = map(long, rrange) if not start: start = 0 assert start >= 0 if end and end < start: self.parent.log_error("Invalid range for data: %s-%s" % (start, end)) raise DAV_Error(416, "Invalid range for data.") if end: if end >= res.size(): raise DAV_Error( 416, "Requested data exceeds available size.") length = (end + 1) - start else: length = res.size() - start res = BoundStream2(res, offset=start, length=length) except TypeError, e: # for the collections that return this error, the DAV standard # says we'd better just return 200 OK with empty data return '' except IndexError, e: self.parent.log_error("GET IndexError: %s", str(e)) raise DAV_NotFound2(uri2)
def put(self, uri, data, content_type=None): """ put the object into the filesystem """ self.parent.log_message( 'Putting %s (%d), %s' % (misc.ustr(uri), data and len(data) or 0, content_type)) cr, uid, pool, dbname, uri2 = self.get_cr(uri) if not dbname: if cr: cr.close() raise DAV_Forbidden try: node = self.uri2object(cr, uid, pool, uri2[:]) except Exception: node = False objname = misc.ustr(uri2[-1]) ret = None if not node: dir_node = self.uri2object(cr, uid, pool, uri2[:-1]) if not dir_node: cr.close() raise DAV_NotFound('Parent folder not found') newchild = self._try_function(dir_node.create_child, (cr, objname, data), "create %s" % objname, cr=cr) if not newchild: cr.commit() cr.close() raise DAV_Error(400, "Failed to create resource") uparts = urlparse.urlparse(uri) fileloc = '/'.join(newchild.full_path()) if isinstance(fileloc, unicode): fileloc = fileloc.encode('utf-8') # the uri we get is a mangled one, where the davpath has been removed davpath = self.parent.get_davpath() surl = '%s://%s' % (uparts[0], uparts[1]) uloc = urllib.quote(fileloc) hurl = False if uri != ('/' + uloc) and uri != (surl + '/' + uloc): hurl = '%s%s/%s/%s' % (surl, davpath, dbname, uloc) etag = False try: etag = str(newchild.get_etag(cr)) except Exception, e: self.parent.log_error("Cannot get etag for node: %s" % e) ret = (str(hurl), etag)
def _get_carddav_address_data(self, uri): dbname, dburi = self._get_dburi(uri) if not dbname: raise DAV_NotFound pool = Pool(Transaction().database.name) try: Collection = pool.get('webdav.collection') except KeyError: raise DAV_NotFound try: return Collection.get_address_data(dburi, cache=LOCAL.cache) except DAV_Error: raise except Exception: raise DAV_Error(500)
def exists(self, uri): dbname, dburi = self._get_dburi(uri) if not dbname or not dburi: return 1 pool = Pool(Transaction().database.name) Collection = pool.get('webdav.collection') try: res = Collection.exists(dburi, cache=LOCAL.cache) except (DAV_Error, DAV_NotFound, DAV_Secret, DAV_Forbidden) as exception: self._log_exception(exception) raise except Exception as exception: self._log_exception(exception) raise DAV_Error(500) return res
def _get_dav_getcontenttype(self, uri): dbname, dburi = self._get_dburi(uri) if not dbname or self.is_collection(uri): return "text/html" pool = Pool(Transaction().database.name) Collection = pool.get('webdav.collection') try: res = Collection.get_contenttype(dburi, cache=LOCAL.cache) except (DAV_Error, DAV_NotFound, DAV_Secret, DAV_Forbidden) as exception: self._log_exception(exception) raise except Exception as exception: self._log_exception(exception) raise DAV_Error(500) return res
def _get_caldav_post(self, uri, body, contenttype=''): dbname, dburi = self._get_dburi(uri) if not dbname: raise DAV_Forbidden pool = Pool(Transaction().database.name) Calendar = pool.get('calendar.calendar') if not getattr(Calendar, 'post', None): raise DAV_NotFound try: res = Calendar.post(dburi, body) except DAV_Error as exception: self._log_exception(exception) raise except Exception as exception: self._log_exception(exception) raise DAV_Error(500) return res
def get_childs(self, uri, filter=None): res = [] dbname, dburi = self._get_dburi(uri) if not dbname: with Transaction().start(None, 0, close=True) as transaction: list_ = transaction.database.list() for dbname in list_: res.append(urllib.parse.urljoin(uri, dbname)) return res pool = Pool(Transaction().database.name) try: Collection = pool.get('webdav.collection') scheme, netloc, path, params, query, fragment = \ urllib.parse.urlparse(uri) if not isinstance(uri, str): scheme = scheme.decode() netloc = netloc.decode() path = path.decode() params = params.decode() query = query.decode() fragment = fragment.decode() if path[-1:] != '/': path = path + '/' for child in Collection.get_childs(dburi, filter=filter, cache=LOCAL.cache): path_child = (path + child) res.append( urllib.parse.urlunparse( (scheme, netloc, path_child, params, query, fragment))) except KeyError: return res except (DAV_Error, DAV_NotFound, DAV_Secret, DAV_Forbidden) as exception: self._log_exception(exception) raise except Exception as exception: self._log_exception(exception) raise DAV_Error(500) return res
def _get_dav_displayname(self, uri): dbname, dburi = self._get_dburi(uri) if not dbname or not dburi: return uri.decode().split('/')[-1] pool = Pool(Transaction().database.name) try: Collection = pool.get('webdav.collection') res = Collection.get_displayname(dburi, cache=LOCAL.cache) except KeyError: raise DAV_NotFound except (DAV_Error, DAV_NotFound, DAV_Secret, DAV_Forbidden) as exception: self._log_exception(exception) raise except Exception as exception: self._log_exception(exception) raise DAV_Error(500) return res
def _try_function(self, funct, args, opname='run function', cr=None, default_exc=DAV_Forbidden): """ Try to run a function, and properly convert exceptions to DAV ones. @objname the name of the operation being performed @param cr if given, the cursor to close at exceptions """ try: return funct(*args) except DAV_Error: raise except NotImplementedError, e: import traceback self.parent.log_error("Cannot %s: %s", opname, str(e)) self.parent.log_message("Exc: %s",traceback.format_exc()) # see par 9.3.1 of rfc raise DAV_Error(403, str(e) or 'Not supported at this path.')
def unlock(self, uri, token): """ Unlock a resource from that token @return True if unlocked, False if no lock existed, Exceptions """ cr, uid, pool, dbname, uri2 = self.get_cr(uri) if not dbname: raise DAV_Error, 409 node = self.uri2object(cr, uid, pool, uri2) try: node_fn = node.dav_unlock except AttributeError: # perhaps the node doesn't support locks raise DAV_Error(400, 'No locks for this resource.') res = self._try_function(node_fn, (cr, token), "unlock %s" % uri, cr=cr) cr.commit() return res
def _get_caldav_calendar_data(self, uri): dbname, dburi = self._get_dburi(uri) if not dbname: raise DAV_NotFound pool = Pool(Transaction().database.name) try: Collection = pool.get('webdav.collection') except KeyError: raise DAV_NotFound if not getattr(Collection, 'get_calendar_data', None): raise DAV_NotFound try: res = Collection.get_calendar_data(dburi, cache=LOCAL.cache) except DAV_Error as exception: self._log_exception(exception) raise except Exception as exception: self._log_exception(exception) raise DAV_Error(500) return res
def put(self, uri, data, content_type=''): dbname, dburi = self._get_dburi(uri) if not dbname or not dburi: raise DAV_Forbidden pool = Pool(Transaction().database.name) Collection = pool.get('webdav.collection') try: res = Collection.put(dburi, data, content_type, cache=LOCAL.cache) Transaction().commit() except (DAV_Error, DAV_NotFound, DAV_Secret, DAV_Forbidden) as exception: self._log_exception(exception) Transaction().rollback() except Exception as exception: self._log_exception(exception) Transaction().rollback() raise DAV_Error(500) if res: uparts = list(urllib.parse.urlsplit(uri)) uparts[2] = res.encode() res = urllib.parse.urlunsplit(uparts) return res
raise DAV_NotFound pool = Pool(Transaction().cursor.database_name) try: Collection = pool.get('webdav.collection') except KeyError: raise DAV_NotFound try: res = Collection.get_calendar_description(dburi, cache=CACHE) except AttributeError: raise DAV_NotFound except DAV_Error, exception: self._log_exception(exception) raise except Exception, exception: self._log_exception(exception) raise DAV_Error(500) return res TrytonDAVInterface._get_caldav_calendar_description = \ _get_caldav_calendar_description def _get_caldav_calendar_data(self, uri): dbname, dburi = self._get_dburi(uri) if not dbname: raise DAV_NotFound pool = Pool(Transaction().cursor.database_name) try: Collection = pool.get('webdav.collection') except KeyError: raise DAV_NotFound
def get_data(self, uri, range=None): uri = uri.decode() dbname, dburi = self._get_dburi(uri) if not dbname or (self.exists(uri) and self.is_collection(uri)): res = ('<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 ' 'Transitional//EN">') res += '<html>' res += '<head>' res += ('<meta http-equiv="Content-Type" content="text/html; ' 'charset=utf-8">') res += '<title>Tryton - WebDAV - %s</title>' % dbname or 'root' res += '</head>' res += '<body>' res += '<h2>Collection: %s</h2>' % (get_urifilename(uri) or '/') res += '<ul>' if dbname: scheme, netloc, path, params, query, fragment = \ urllib.parse.urlparse(uri) if path[-1:] != '/': path += '/' res += ('<li><a href="%s">..</a></li>' % urllib.parse.urlunparse((scheme, netloc, path + '..', params, query, fragment))) childs = self.get_childs(uri) childs.sort() for child in childs: res += ('<li><a href="%s">%s</a></li>' % (quote_uri(child), get_urifilename(child))) res += '</ul>' res += '<hr noshade>' res += ('<em>Powered by <a href="http://www.tryton.org/">' 'Tryton</a> version %s</em>' % __version__) res += '</body>' res += '</html>' return res pool = Pool(Transaction().database.name) Collection = pool.get('webdav.collection') try: res = Collection.get_data(dburi, cache=LOCAL.cache) except (DAV_Error, DAV_NotFound, DAV_Secret, DAV_Forbidden) as exception: self._log_exception(exception) raise except Exception as exception: self._log_exception(exception) raise DAV_Error(500) if range is None: return res size = len(res) if range[1] == '': range[1] = size else: range[1] = int(range[1]) if range[1] > size: range[1] = size if range[0] == '': range[0] = size - range[1] else: range[0] = int(range[0]) if range[0] > size: raise DAV_Requested_Range_Not_Satisfiable return res[range[0]:range[1]]
class TrytonDAVInterface(iface.dav_interface): def __init__(self, interface, port, secure=False): if secure: protocol = 'https' else: protocol = 'http' self.baseuri = '%s://%s:%s/' % (protocol, interface or socket.gethostname(), port) self.verbose = False def _log_exception(self, exception): if isinstance(exception, (NotLogged, ConcurrencyException, UserError, UserWarning, DAV_Error, DAV_NotFound, DAV_Secret, DAV_Forbidden)): logger.debug('Exception %s', exception, exc_info=True) else: logger.error('Exception %s', exception, exc_info=True) @staticmethod def get_dburi(uri): uri = urlparse.urlsplit(uri)[2] if uri and uri[0] == '/': uri = uri[1:] dbname, uri = (uri.split('/', 1) + [None])[0:2] if dbname: dbname = urllib.unquote_plus(dbname) if uri: uri = urllib.unquote_plus(uri) return dbname, uri def _get_dburi(self, uri): return TrytonDAVInterface.get_dburi(uri) def get_childs(self, uri, filter=None): res = [] dbname, dburi = self._get_dburi(uri) if not dbname: database = backend.get('Database')().connect() cursor = database.cursor() try: lists = database.list(cursor) except Exception: lists = [] finally: cursor.close() for dbname in lists: res.append(urlparse.urljoin(uri, dbname)) return res pool = Pool(Transaction().cursor.database_name) try: Collection = pool.get('webdav.collection') scheme, netloc, path, params, query, fragment = \ urlparse.urlparse(uri) if path[-1:] != '/': path += '/' for child in Collection.get_childs(dburi, filter=filter, cache=CACHE): res.append( urlparse.urlunparse( (scheme, netloc, path + child.encode('utf-8'), params, query, fragment))) except KeyError: return res except (DAV_Error, DAV_NotFound, DAV_Secret, DAV_Forbidden), exception: self._log_exception(exception) raise except Exception, exception: self._log_exception(exception) raise DAV_Error(500)