webish: add preliminary mutable file support: upload, download, listings, JSON, URI, RO-URI. No replace yet.

This commit is contained in:
Brian Warner 2007-11-09 03:54:27 -07:00
parent 63233ecf37
commit 1f22768dc7
5 changed files with 110 additions and 25 deletions

View File

@ -433,6 +433,9 @@ class FileNode:
def get_uri(self):
return self.uri
def is_readonly(self):
return True
def get_size(self):
return IFileURI(self.uri).get_size()

View File

@ -1148,7 +1148,8 @@ class MutableFileNode:
# wants to get our contents, we'll pull from shares and fill those
# in.
self._uri = IMutableFileURI(myuri)
self._writekey = self._uri.writekey
if not self._uri.is_readonly():
self._writekey = self._uri.writekey
self._readkey = self._uri.readkey
self._storage_index = self._uri.storage_index
self._fingerprint = self._uri.fingerprint
@ -1269,6 +1270,14 @@ class MutableFileNode:
def get_uri(self):
return self._uri.to_string()
def get_size(self):
return "?" # TODO: this is likely to cause problems, not being an int
def get_readonly(self):
if self.is_readonly():
return self
ro = MutableFileNode(self._client)
ro.init_from_uri(self._uri.get_readonly())
return ro
def is_mutable(self):
return self._uri.is_mutable()
@ -1293,9 +1302,15 @@ class MutableFileNode:
return self._client.getServiceNamed("checker").check(verifier)
def download(self, target):
#downloader = self._client.getServiceNamed("downloader")
#return downloader.download(self.uri, target)
raise NotImplementedError
# fake it. TODO: make this cleaner.
d = self.download_to_data()
def _done(data):
target.open(len(data))
target.write(data)
target.close()
return target.finish()
d.addCallback(_done)
return d
def download_to_data(self):
r = Retrieve(self)

View File

@ -29,6 +29,11 @@ class MyClient(service.MultiService):
def get_all_peerids(self):
return []
def upload(self, uploadable):
uploader = self.getServiceNamed("uploader")
return uploader.upload(uploadable)
class MyDownloader(service.Service):
implements(interfaces.IDownloader)
name = "downloader"

View File

@ -7,6 +7,7 @@ from twisted.internet import defer
from allmydata.interfaces import IVirtualDrive, IDirnodeURI, IURI
from allmydata.util import observer
from allmydata import dirnode
from allmydata.dirnode2 import INewDirectoryURI
class NoGlobalVirtualDriveError(Exception):
pass
@ -133,10 +134,11 @@ class VirtualDrive(service.MultiService):
def get_node(self, node_uri):
node_uri = IURI(node_uri)
if IDirnodeURI.providedBy(node_uri):
if (IDirnodeURI.providedBy(node_uri)
and not INewDirectoryURI.providedBy(node_uri)):
return dirnode.create_directory_node(self.parent, node_uri)
else:
return defer.succeed(dirnode.FileNode(node_uri, self.parent))
return defer.succeed(self.parent.create_node_from_uri(node_uri))
def get_node_at_path(self, path, root=None):

View File

@ -10,7 +10,8 @@ from nevow import inevow, rend, loaders, appserver, url, tags as T
from nevow.static import File as nevow_File # TODO: merge with static.File?
from allmydata.util import fileutil
import simplejson
from allmydata.interfaces import IDownloadTarget, IDirectoryNode, IFileNode
from allmydata.interfaces import IDownloadTarget, IDirectoryNode, IFileNode, \
IMutableFileNode
from allmydata import upload, download
from allmydata import provisioning
from zope.interface import implements, Interface
@ -180,7 +181,9 @@ class Directory(rend.Page):
# build the base of the uri_link link url
uri_link = "/uri/" + urllib.quote(target.get_uri().replace("/", "!"))
assert IFileNode.providedBy(target) or IDirectoryNode.providedBy(target), target
assert (IFileNode.providedBy(target)
or IDirectoryNode.providedBy(target)
or IMutableFileNode.providedBy(target)), target
if IFileNode.providedBy(target):
# file
@ -203,6 +206,27 @@ class Directory(rend.Page):
text_plain_link = uri_link + "?filename=foo.txt"
text_plain_tag = T.a(href=text_plain_link)["text/plain"]
elif IMutableFileNode.providedBy(target):
# file
# add the filename to the uri_link url
uri_link += '?%s' % (urllib.urlencode({'filename': name}),)
# to prevent javascript in displayed .html files from stealing a
# secret vdrive URI from the URL, send the browser to a URI-based
# page that doesn't know about the vdrive at all
#dlurl = urllib.quote(name)
dlurl = uri_link
ctx.fillSlots("filename",
T.a(href=dlurl)[html.escape(name)])
ctx.fillSlots("type", "SSK")
ctx.fillSlots("size", "?")
text_plain_link = uri_link + "?filename=foo.txt"
text_plain_tag = T.a(href=text_plain_link)["text/plain"]
elif IDirectoryNode.providedBy(target):
# directory
@ -274,6 +298,7 @@ class Directory(rend.Page):
T.input(type="text", name="name"), " ",
T.input(type="submit", value="Create"),
]]
upload = T.form(action=".", method="post",
enctype="multipart/form-data")[
T.fieldset[
@ -284,7 +309,10 @@ class Directory(rend.Page):
T.input(type="file", name="file", class_="freeform-input-file"),
" ",
T.input(type="submit", value="Upload"),
" Mutable?:",
T.input(type="checkbox", name="mutable"),
]]
mount = T.form(action=".", method="post",
enctype="multipart/form-data")[
T.fieldset[
@ -367,7 +395,8 @@ class WebDownloadTarget:
class FileDownloader(resource.Resource):
def __init__(self, filenode, name):
IFileNode(filenode)
assert (IFileNode.providedBy(filenode)
or IMutableFileNode.providedBy(filenode))
self._filenode = filenode
self._name = name
@ -457,6 +486,13 @@ class FileURI(FileJSONMetadata):
file_uri = filenode.get_uri()
return file_uri
class FileReadOnlyURI(FileJSONMetadata):
def renderNode(self, filenode):
if filenode.is_readonly():
return filenode.get_uri()
else:
return filenode.get_readonly().get_uri()
class DirnodeWalkerMixin:
"""Visit all nodes underneath (and including) the rootnode, one at a
time. For each one, call the visitor. The visitor will see the
@ -719,19 +755,41 @@ class POSTHandler(rend.Page):
def _done(res):
return "thing renamed"
d.addCallback(_done)
elif t == "upload":
contents = req.fields["file"]
name = name or contents.filename
if name is not None:
name = name.strip()
if not name:
raise RuntimeError("set-uri requires a name")
uploadable = upload.FileHandle(contents.file)
d = self._check_replacement(name)
d.addCallback(lambda res: self._node.add_file(name, uploadable))
def _done(newnode):
return newnode.get_uri()
d.addCallback(_done)
if "mutable" in req.fields:
contents = req.fields["file"]
name = name or contents.filename
if name is not None:
name = name.strip()
if not name:
raise RuntimeError("upload-mutable requires a name")
# SDMF: files are small, and we can only upload data.
contents.file.seek(0)
data = contents.file.read()
uploadable = upload.FileHandle(contents.file)
d = self._check_replacement(name)
d.addCallback(lambda res:
IClient(ctx).create_mutable_file(data))
def _uploaded(newnode):
d1 = self._node.set_node(name, newnode)
d1.addCallback(lambda res: newnode.get_uri())
return d1
d.addCallback(_uploaded)
else:
contents = req.fields["file"]
name = name or contents.filename
if name is not None:
name = name.strip()
if not name:
raise RuntimeError("upload requires a name")
uploadable = upload.FileHandle(contents.file)
d = self._check_replacement(name)
d.addCallback(lambda res: self._node.add_file(name, uploadable))
def _done(newnode):
return newnode.get_uri()
d.addCallback(_done)
elif t == "check":
d = self._node.get(name)
def _got_child(child_node):
@ -1006,7 +1064,8 @@ class VDrive(rend.Page):
# node itself.
d = self.get_child_at_path(path)
def file_or_dir(node):
if IFileNode.providedBy(node):
if (IFileNode.providedBy(node)
or IMutableFileNode.providedBy(node)):
filename = "unknown"
if path:
filename = path[-1]
@ -1026,7 +1085,7 @@ class VDrive(rend.Page):
elif t == "uri":
return FileURI(node), ()
elif t == "readonly-uri":
return FileURI(node), ()
return FileReadOnlyURI(node), ()
else:
raise RuntimeError("bad t=%s" % t)
elif IDirectoryNode.providedBy(node):
@ -1093,8 +1152,7 @@ class URIPUTHandler(rend.Page):
# "PUT /uri", to create an unlinked file. This is like PUT but
# without the associated set_uri.
uploadable = upload.FileHandle(req.content)
uploader = IClient(ctx).getServiceNamed("uploader")
d = uploader.upload(uploadable)
d = IClient(ctx).upload(uploadable)
# that fires with the URI of the new file
return d
@ -1103,6 +1161,8 @@ class URIPUTHandler(rend.Page):
# public vdriveserver to create the dirnode.
vdrive = IClient(ctx).getServiceNamed("vdrive")
d = vdrive.create_directory()
# TODO: switch to new-style dirnodes and replace this with:
#d = IClient(ctx).create_empty_dirnode()
d.addCallback(lambda dirnode: dirnode.get_uri())
return d