tahoe-lafs/src/allmydata/webish.py

996 lines
36 KiB
Python
Raw Normal View History

import os.path
from twisted.application import service, strports
from twisted.web import static, resource, server, html, http
2006-12-04 12:15:36 +00:00
from twisted.python import util, log
from twisted.internet import defer
2006-12-04 12:15:36 +00:00
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 idlib, fileutil
from allmydata.uri import unpack_uri, is_dirnode_uri
from allmydata.interfaces import IDownloadTarget, IDirectoryNode, IFileNode
from allmydata.dirnode import FileNode
from allmydata import upload, download
from zope.interface import implements, Interface
import urllib
from formless import webform
def getxmlfile(name):
return loaders.xmlfile(util.sibpath(__file__, "web/%s" % name))
class IClient(Interface):
pass
def get_downloader_service(ctx):
return IClient(ctx).getServiceNamed("downloader")
def get_uploader_service(ctx):
return IClient(ctx).getServiceNamed("uploader")
class Directory(rend.Page):
addSlash = True
docFactory = getxmlfile("directory.xhtml")
2007-07-08 04:31:02 +00:00
def __init__(self, rootname, dirnode, dirpath):
self._rootname = rootname
self._dirnode = dirnode
self._dirpath = dirpath
def childFactory(self, ctx, name):
if name.startswith("freeform"): # ick
return None
#if name == "@manifest": # ick, this time it's my fault
# return Manifest(self._dirnode, self._dirpath)
return rend.NotFound
def dirpath_as_string(self):
return "/" + "/".join(self._dirpath)
def render_title(self, ctx, data):
return ctx.tag["Directory '%s':" % self.dirpath_as_string()]
def render_header(self, ctx, data):
2007-07-08 04:31:02 +00:00
parent_directories = ("<%s>" % self._rootname,) + self._dirpath
num_dirs = len(parent_directories)
header = ["Directory '"]
for i,d in enumerate(parent_directories):
upness = num_dirs - i - 1
if upness:
link = "/".join( ("..",) * upness )
else:
link = "."
header.append(T.a(href=link)[d])
if upness != 0:
header.append("/")
header.append("'")
if not self._dirnode.is_mutable():
header.append(" (readonly)")
header.append(":")
return ctx.tag[header]
def render_welcome(self, ctx, data):
depth = len(self._dirpath) + 2
link = "/".join([".."] * depth)
return T.div[T.a(href=link)["Return to Welcome page"]]
def data_share_uri(self, ctx, data):
return self._dirnode.get_uri()
def data_share_readonly_uri(self, ctx, data):
return self._dirnode.get_immutable_uri()
def data_children(self, ctx, data):
d = self._dirnode.list()
d.addCallback(lambda dict: sorted(dict.items()))
return d
def render_row(self, ctx, data):
name, target = data
if self._dirnode.is_mutable():
# this creates a button which will cause our child__delete method
# to be invoked, which deletes the file and then redirects the
# browser back to this directory
2007-07-08 03:46:30 +00:00
del_url = url.here.add("t", "delete").add("name", name)
del_url = del_url.add("when_done", url.here)
delete = T.form(action=del_url, method="post")[
T.input(type='submit', value='del', name="del"),
]
else:
delete = "-"
ctx.fillSlots("delete", delete)
childdata = [T.a(href="%s?t=json" % name)["JSON"], ", ",
T.a(href="%s?t=xml" % name)["XML"], ", ",
T.a(href="%s?t=uri" % name)["URI"], ", ",
T.a(href="%s?t=readonly-uri" % name)["readonly-URI"], ", ",
T.a(href="/uri/%s" % target.get_uri())["URI-link"],
]
ctx.fillSlots("data", childdata)
if IFileNode.providedBy(target):
# file
dlurl = urllib.quote(name)
ctx.fillSlots("filename",
T.a(href=dlurl)[html.escape(name)])
ctx.fillSlots("type", "FILE")
#uri = target.uri
#dl_uri_url = url.root.child("download_uri").child(uri)
## add a filename= query argument to give it a Content-Type
#dl_uri_url = dl_uri_url.add("filename", name)
#ctx.fillSlots("uri", T.a(href=dl_uri_url)[html.escape(uri)])
2007-05-04 20:07:32 +00:00
#extract and display file size
try:
size = unpack_uri(target.get_uri())['size']
except AssertionError:
size = "?"
ctx.fillSlots("size", size)
2007-05-04 20:07:32 +00:00
elif IDirectoryNode.providedBy(target):
# directory
subdir_url = urllib.quote(name)
ctx.fillSlots("filename",
T.a(href=subdir_url)[html.escape(name)])
if target.is_mutable():
dirtype = "DIR"
else:
dirtype = "DIR-RO"
ctx.fillSlots("type", dirtype)
2007-05-04 20:07:32 +00:00
ctx.fillSlots("size", "-")
else:
raise RuntimeError("unknown thing %s" % (target,))
return ctx.tag
2006-12-04 12:15:36 +00:00
def render_forms(self, ctx, data):
if not self._dirnode.is_mutable():
return T.div["No upload forms: directory is immutable"]
mkdir = T.form(action=".", method="post",
enctype="multipart/form-data")[
T.fieldset[
T.input(type="hidden", name="t", value="mkdir"),
T.input(type="hidden", name="when_done", value=url.here),
T.legend(class_="freeform-form-label")["Create a new directory"],
"New directory name: ",
T.input(type="text", name="name"), " ",
T.input(type="submit", value="Create"),
]]
upload = T.form(action=".", method="post",
enctype="multipart/form-data")[
T.fieldset[
T.input(type="hidden", name="t", value="upload"),
T.input(type="hidden", name="when_done", value=url.here),
T.legend(class_="freeform-form-label")["Upload a file to this directory"],
"Choose a file to upload: ",
T.input(type="file", name="file", class_="freeform-input-file"),
" ",
T.input(type="submit", value="Upload"),
]]
mount = T.form(action=".", method="post",
enctype="multipart/form-data")[
T.fieldset[
T.input(type="hidden", name="t", value="uri"),
T.input(type="hidden", name="when_done", value=url.here),
T.legend(class_="freeform-form-label")["Attach a file or directory"
" (by URI) to this"
"directory"],
"New child name: ",
T.input(type="text", name="name"), " ",
"URI of new child: ",
T.input(type="text", name="uri"), " ",
T.input(type="submit", value="Attach"),
]]
return [T.div(class_="freeform-form")[mkdir],
T.div(class_="freeform-form")[upload],
T.div(class_="freeform-form")[mount],
]
2006-12-04 12:15:36 +00:00
def render_results(self, ctx, data):
req = inevow.IRequest(ctx)
if "results" in req.args:
return req.args["results"]
else:
return ""
def child__delete(self, ctx):
# perform the delete, then redirect back to the directory page
args = inevow.IRequest(ctx).args
name = args["name"][0]
d = self._dirnode.delete(name)
d.addCallback(lambda done: url.here.up())
return d
class WebDownloadTarget:
implements(IDownloadTarget)
def __init__(self, req, content_type, content_encoding):
self._req = req
self._content_type = content_type
self._content_encoding = content_encoding
self._opened = False
def open(self, size):
self._opened = True
self._req.setHeader("content-type", self._content_type)
if self._content_encoding:
self._req.setHeader("content-encoding", self._content_encoding)
self._req.setHeader("content-length", str(size))
def write(self, data):
self._req.write(data)
def close(self):
self._req.finish()
def fail(self, why):
if self._opened:
# The content-type is already set, and the response code
# has already been sent, so we can't provide a clean error
# indication. We can emit text (which a browser might interpret
# as something else), and if we sent a Size header, they might
# notice that we've truncated the data. Keep the error message
# small to improve the chances of having our error response be
# shorter than the intended results.
#
# We don't have a lot of options, unfortunately.
self._req.write("problem during download\n")
else:
# We haven't written anything yet, so we can provide a sensible
# error message.
msg = str(why.type)
msg.replace("\n", "|")
self._req.setResponseCode(http.INTERNAL_SERVER_ERROR, msg)
self._req.setHeader("content-type", "text/plain")
# TODO: HTML-formatted exception?
self._req.write(str(why))
self._req.finish()
def register_canceller(self, cb):
pass
def finish(self):
pass
class TypedFile(static.File):
# serve data from a named file, but using a Content-Type derived from a
# different filename
isLeaf = True
def __init__(self, path, requested_filename):
static.File.__init__(self, path)
gte = static.getTypeAndEncoding
self.type, self.encoding = gte(requested_filename,
self.contentTypes,
self.contentEncodings,
self.defaultType)
class FileDownloader(resource.Resource):
def __init__(self, filenode, name):
IFileNode(filenode)
self._filenode = filenode
self._name = name
def render(self, req):
gte = static.getTypeAndEncoding
type, encoding = gte(self._name,
static.File.contentTypes,
static.File.contentEncodings,
defaultType="text/plain")
d = self._filenode.download(WebDownloadTarget(req, type, encoding))
# exceptions during download are handled by the WebDownloadTarget
d.addErrback(lambda why: None)
return server.NOT_DONE_YET
2006-12-04 12:15:36 +00:00
class BlockingFileError(Exception):
"""We cannot auto-create a parent directory, because there is a file in
the way"""
LOCALHOST = "127.0.0.1"
class NeedLocalhostError:
implements(inevow.IResource)
def locateChild(self, ctx, segments):
return rend.NotFound
def renderHTTP(self, ctx):
req = inevow.IRequest(ctx)
req.setResponseCode(http.FORBIDDEN)
req.setHeader("content-type", "text/plain")
return "localfile= or localdir= requires a local connection"
class NeedAbsolutePathError:
implements(inevow.IResource)
def locateChild(self, ctx, segments):
return rend.NotFound
def renderHTTP(self, ctx):
req = inevow.IRequest(ctx)
req.setResponseCode(http.FORBIDDEN)
req.setHeader("content-type", "text/plain")
return "localfile= or localdir= requires an absolute path"
class LocalFileDownloader(resource.Resource):
def __init__(self, filenode, local_filename):
self._local_filename = local_filename
IFileNode(filenode)
self._filenode = filenode
def render(self, req):
target = download.FileName(self._local_filename)
d = self._filenode.download(target)
def _done(res):
req.write(self._filenode.get_uri())
req.finish()
d.addCallback(_done)
return server.NOT_DONE_YET
class FileJSONMetadata(rend.Page):
def __init__(self, filenode):
self._filenode = filenode
def renderHTTP(self, ctx):
req = inevow.IRequest(ctx)
req.setHeader("content-type", "text/plain")
return self.renderNode(self._filenode)
def renderNode(self, filenode):
file_uri = filenode.get_uri()
pieces = unpack_uri(file_uri)
data = "filenode\n"
data += "JSONny stuff here\n"
data += "uri=%s, size=%s" % (file_uri, pieces['size'])
return data
class FileXMLMetadata(FileJSONMetadata):
def renderNode(self, filenode):
file_uri = filenode.get_uri()
pieces = unpack_uri(file_uri)
data = "<xmlish>\n"
data += "filenode\n"
data += "stuff here\n"
data += "uri=%s, size=%s" % (file_uri, pieces['size'])
return data
class FileURI(FileJSONMetadata):
def renderNode(self, filenode):
file_uri = filenode.get_uri()
return file_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
IDirectoryNode before it sees any of the IFileNodes inside. If the
visitor returns a Deferred, I do not call the visitor again until it has
fired.
"""
def _walk_if_we_could_use_generators(self, rootnode, rootpath=()):
# this is what we'd be doing if we didn't have the Deferreds and thus
# could use generators
yield rootpath, rootnode
for childname, childnode in rootnode.list().items():
childpath = rootpath + (childname,)
if IFileNode.providedBy(childnode):
yield childpath, childnode
elif IDirectoryNode.providedBy(childnode):
for res in self._walk_if_we_could_use_generators(childnode,
childpath):
yield res
def walk(self, rootnode, visitor, rootpath=()):
d = rootnode.list()
def _listed(listing):
return listing.items()
d.addCallback(_listed)
d.addCallback(self._handle_items, visitor, rootpath)
return d
def _handle_items(self, items, visitor, rootpath):
if not items:
return
childname, childnode = items[0]
childpath = rootpath + (childname,)
d = defer.maybeDeferred(visitor, childpath, childnode)
if IDirectoryNode.providedBy(childnode):
d.addCallback(lambda res: self.walk(childnode, visitor, childpath))
d.addCallback(lambda res:
self._handle_items(items[1:], visitor, rootpath))
return d
class LocalDirectoryDownloader(resource.Resource, DirnodeWalkerMixin):
def __init__(self, dirnode, localdir):
self._dirnode = dirnode
self._localdir = localdir
def _handle(self, path, node):
localfile = os.path.join(self._localdir, os.sep.join(path))
if IDirectoryNode.providedBy(node):
fileutil.make_dirs(localfile)
elif IFileNode.providedBy(node):
target = download.FileName(localfile)
return node.download(target)
def render(self, req):
d = self.walk(self._dirnode, self._handle)
def _done(res):
req.setHeader("content-type", "text/plain")
return "operation complete"
d.addCallback(_done)
return d
class DirectoryJSONMetadata(rend.Page):
def __init__(self, dirnode):
self._dirnode = dirnode
def renderHTTP(self, ctx):
req = inevow.IRequest(ctx)
req.setHeader("content-type", "text/plain")
return self.renderNode(self._dirnode)
def renderNode(self, node):
data = "dirnode\n"
data += "JSONny stuff here\n"
d = node.list()
def _got(children, data):
for name, childnode in children.iteritems():
data += "name=%s, child_uri=%s" % (name, childnode.get_uri())
return data
d.addCallback(_got, data)
def _done(data):
data += "done\n"
return data
d.addCallback(_done)
return d
class DirectoryXMLMetadata(DirectoryJSONMetadata):
def renderNode(self, node):
data = "<xmlish>\n"
data += "dirnode\n"
data += "stuff here\n"
d = node.list()
def _got(children, data):
for name, childnode in children.iteritems():
data += "name=%s, child_uri=%s" % (name, childnode.get_uri())
return data
d.addCallback(_got, data)
def _done(data):
data += "</done>\n"
return data
d.addCallback(_done)
return d
class DirectoryURI(DirectoryJSONMetadata):
def renderNode(self, node):
return node.get_uri()
class DirectoryReadonlyURI(DirectoryJSONMetadata):
def renderNode(self, node):
return node.get_immutable_uri()
class POSTHandler(rend.Page):
def __init__(self, node):
self._node = node
2007-07-08 03:11:30 +00:00
def renderHTTP(self, ctx):
req = inevow.IRequest(ctx)
2007-07-08 03:46:30 +00:00
if "t" in req.args:
t = req.args["t"][0]
else:
t = req.fields["t"].value
name = None
if "name" in req.args:
name = req.args["name"][0]
elif name in req.fields:
name = req.fields["name"].value
2007-07-08 03:46:30 +00:00
when_done = None
if "when_done" in req.args:
when_done = req.args["when_done"][0]
2007-07-08 03:46:30 +00:00
if t == "mkdir":
if not name:
raise RuntimeError("mkdir requires a name")
d = self._node.create_empty_directory(name)
def _done(res):
return "directory created"
d.addCallback(_done)
elif t == "uri":
2007-07-08 03:46:30 +00:00
if not name:
raise RuntimeError("set-uri requires a name")
if "uri" in req.args:
newuri = req.args["uri"][0]
2007-07-08 03:46:30 +00:00
else:
newuri = req.fields["uri"].value
# sanity checking
assert(is_dirnode_uri(newuri) or unpack_uri(newuri))
d = self._node.set_uri(name, newuri)
def _done(res):
return newuri
d.addCallback(_done)
elif t == "delete":
2007-07-08 03:46:30 +00:00
if not name:
raise RuntimeError("delete requires a name")
d = self._node.delete(name)
def _done(res):
return "thing deleted"
d.addCallback(_done)
elif t == "upload":
contents = req.fields["file"]
2007-07-08 03:46:30 +00:00
name = name or contents.filename
uploadable = upload.FileHandle(contents.file)
d = self._node.add_file(name, uploadable)
def _done(newnode):
return newnode.get_uri()
d.addCallback(_done)
else:
print "BAD t=%s" % t
return "BAD t=%s" % t
if when_done:
d.addCallback(lambda res: url.URL.fromString(when_done))
return d
class DELETEHandler(rend.Page):
def __init__(self, node, name):
self._node = node
self._name = name
def renderHTTP(self, ctx):
req = inevow.IRequest(ctx)
d = self._node.delete(self._name)
def _done(res):
# what should this return??
return "%s deleted" % self._name
d.addCallback(_done)
def _trap_missing(f):
f.trap(KeyError)
req.setResponseCode(http.NOT_FOUND)
req.setHeader("content-type", "text/plain")
return "no such child %s" % self._name
d.addErrback(_trap_missing)
return d
class PUTHandler(rend.Page):
def __init__(self, node, path, t, localfile, localdir):
self._node = node
self._path = path
self._t = t
self._localfile = localfile
self._localdir = localdir
def renderHTTP(self, ctx):
req = inevow.IRequest(ctx)
t = self._t
localfile = self._localfile
localdir = self._localdir
self._uploader = get_uploader_service(ctx)
# we must traverse the path, creating new directories as necessary
d = self._get_or_create_directories(self._node, self._path[:-1])
name = self._path[-1]
if localfile:
d.addCallback(self._upload_localfile, localfile, name)
elif localdir:
d.addCallback(self._get_or_create_directories, self._path[-1:])
d.addCallback(self._upload_localdir, localdir)
elif t == "uri":
d.addCallback(self._attach_uri, req.content, name)
elif t == "mkdir":
d.addCallback(self._mkdir, name)
else:
d.addCallback(self._upload_file, req.content, name)
def _check_blocking(f):
f.trap(BlockingFileError)
req.setResponseCode(http.FORBIDDEN)
req.setHeader("content-type", "text/plain")
return str(f)
d.addErrback(_check_blocking)
return d
def _get_or_create_directories(self, node, path):
if not IDirectoryNode.providedBy(node):
raise BlockingFileError
if not path:
return node
d = node.get(path[0])
def _maybe_create(f):
f.trap(KeyError)
return node.create_empty_directory(path[0])
d.addErrback(_maybe_create)
d.addCallback(self._get_or_create_directories, path[1:])
return d
def _mkdir(self, node, name):
d = node.create_empty_directory(name)
def _done(newnode):
return newnode.get_uri()
d.addCallback(_done)
return d
def _upload_file(self, node, contents, name):
uploadable = upload.FileHandle(contents)
d = self._uploader.upload(uploadable)
def _uploaded(uri):
d1 = node.set_uri(name, uri)
d1.addCallback(lambda res: uri)
return d1
d.addCallback(_uploaded)
def _done(uri):
log.msg("webish upload complete")
return uri
d.addCallback(_done)
return d
def _upload_localfile(self, node, localfile, name):
uploadable = upload.FileName(localfile)
d = node.add_file(name, uploadable)
d.addCallback(lambda filenode: filenode.get_uri())
return d
def _attach_uri(self, parentnode, contents, name):
newuri = contents.read().strip()
d = parentnode.set_uri(name, newuri)
def _done(res):
return newuri
d.addCallback(_done)
return d
def _upload_localdir(self, node, localdir):
# build up a list of files to upload
all_files = []
all_dirs = []
for root, dirs, files in os.walk(localdir):
if root == localdir:
path = ()
else:
relative_root = root[len(localdir)+1:]
path = tuple(relative_root.split(os.sep))
for d in dirs:
all_dirs.append(path + (d,))
for f in files:
all_files.append(path + (f,))
d = defer.succeed(None)
for dir in all_dirs:
if dir:
d.addCallback(self._makedir, node, dir)
for f in all_files:
d.addCallback(self._upload_one_file, node, localdir, f)
return d
def _makedir(self, res, node, dir):
d = defer.succeed(None)
# get the parent. As long as os.walk gives us parents before
# children, this ought to work
d.addCallback(lambda res: node.get_child_at_path(dir[:-1]))
# then create the child directory
d.addCallback(lambda parent: parent.create_empty_directory(dir[-1]))
return d
def _upload_one_file(self, res, node, localdir, f):
# get the parent. We can be sure this exists because we already
# went through and created all the directories we require.
localfile = os.path.join(localdir, *f)
d = node.get_child_at_path(f[:-1])
d.addCallback(self._upload_localfile, localfile, f[-1])
return d
class Manifest(rend.Page):
docFactory = getxmlfile("manifest.xhtml")
def __init__(self, dirnode, dirpath):
self._dirnode = dirnode
self._dirpath = dirpath
def dirpath_as_string(self):
return "/" + "/".join(self._dirpath)
def render_title(self, ctx):
return T.title["Manifest of %s" % self.dirpath_as_string()]
def render_header(self, ctx):
return T.p["Manifest of %s" % self.dirpath_as_string()]
def data_items(self, ctx, data):
return self._dirnode.build_manifest()
def render_row(self, ctx, refresh_cap):
ctx.fillSlots("refresh_capability", refresh_cap)
return ctx.tag
2006-12-04 12:15:36 +00:00
class VDrive(rend.Page):
def __init__(self, node, name):
self.node = node
self.name = name
def get_child_at_path(self, path):
if path:
return self.node.get_child_at_path(path)
return defer.succeed(self.node)
def locateChild(self, ctx, segments):
req = inevow.IRequest(ctx)
method = req.method
path = segments
# when we're pointing at a directory (like /vdrive/public/my_pix),
# Directory.addSlash causes a redirect to /vdrive/public/my_pix/,
# which appears here as ['my_pix', '']. This is supposed to hit the
# same Directory as ['my_pix'].
if path and path[-1] == '':
path = path[:-1]
t = ""
if "t" in req.args:
t = req.args["t"][0]
localfile = None
if "localfile" in req.args:
localfile = req.args["localfile"][0]
if localfile != os.path.abspath(localfile):
return NeedAbsolutePathError(), ()
localdir = None
if "localdir" in req.args:
localdir = req.args["localdir"][0]
if localdir != os.path.abspath(localdir):
return NeedAbsolutePathError(), ()
if localfile or localdir:
if req.getHost().host != LOCALHOST:
return NeedLocalhostError(), ()
# TODO: think about clobbering/revealing config files and node secrets
if method == "GET":
# the node must exist, and our operation will be performed on the
# node itself.
d = self.get_child_at_path(path)
def file_or_dir(node):
if IFileNode.providedBy(node):
filename = "unknown"
if path:
filename = path[-1]
if "filename" in req.args:
filename = req.args["filename"][0]
if localfile:
# write contents to a local file
return LocalFileDownloader(node, localfile), ()
elif t == "":
# send contents as the result
return FileDownloader(node, filename), ()
elif t == "json":
return FileJSONMetadata(node), ()
elif t == "xml":
return FileXMLMetadata(node), ()
elif t == "uri":
return FileURI(node), ()
elif t == "readonly-uri":
return FileURI(node), ()
else:
raise RuntimeError("bad t=%s" % t)
elif IDirectoryNode.providedBy(node):
if localdir:
# recursive download to a local directory
return LocalDirectoryDownloader(node, localdir), ()
elif t == "":
# send an HTML representation of the directory
2007-07-08 04:31:02 +00:00
return Directory(self.name, node, path), ()
elif t == "json":
return DirectoryJSONMetadata(node), ()
elif t == "xml":
return DirectoryXMLMetadata(node), ()
elif t == "uri":
return DirectoryURI(node), ()
elif t == "readonly-uri":
return DirectoryReadonlyURI(node), ()
elif t == "manifest":
return Manifest(node, path), ()
else:
raise RuntimeError("bad t=%s" % t)
else:
raise RuntimeError("unknown node type")
d.addCallback(file_or_dir)
elif method == "POST":
# the node must exist, and our operation will be performed on the
# node itself.
d = self.get_child_at_path(path)
def _got(node):
return POSTHandler(node), ()
d.addCallback(_got)
elif method == "DELETE":
# the node must exist, and our operation will be performed on its
# parent node.
assert path # you can't delete the root
d = self.get_child_at_path(path[:-1])
def _got(node):
return DELETEHandler(node, path[-1]), ()
d.addCallback(_got)
elif method in ("PUT",):
# the node may or may not exist, and our operation may involve
# all the ancestors of the node.
return PUTHandler(self.node, path, t, localfile, localdir), ()
else:
return rend.NotFound
def _trap_KeyError(f):
f.trap(KeyError)
return rend.FourOhFour(), ()
d.addErrback(_trap_KeyError)
return d
class Root(rend.Page):
addSlash = True
docFactory = getxmlfile("welcome.xhtml")
def locateChild(self, ctx, segments):
client = IClient(ctx)
req = inevow.IRequest(ctx)
vdrive = client.getServiceNamed("vdrive")
if segments[0] == "vdrive":
if len(segments) < 2:
return rend.NotFound
if segments[1] == "global":
d = vdrive.get_public_root()
name = "public vdrive"
elif segments[1] == "private":
d = vdrive.get_private_root()
name = "private vdrive"
else:
return rend.NotFound
d.addCallback(lambda dirnode: VDrive(dirnode, name))
d.addCallback(lambda vd: vd.locateChild(ctx, segments[2:]))
return d
elif segments[0] == "uri":
if len(segments) == 1:
if "uri" in req.args:
uri = req.args["uri"][0]
there = url.URL.fromContext(ctx)
there = there.clear("uri")
there = there.child("uri").child(uri)
return there, ()
if len(segments) < 2:
return rend.NotFound
uri = segments[1]
d = vdrive.get_node(uri)
d.addCallback(lambda node: VDrive(node, "<from-uri>"))
d.addCallback(lambda vd: vd.locateChild(ctx, segments[2:]))
return d
elif segments[0] == "xmlrpc":
pass # TODO
elif segments[0] == "download_uri":
dl = get_downloader_service(ctx)
filename = "unknown_filename"
if "filename" in req.args:
filename = req.args["filename"][0]
if len(segments) > 1:
# http://host/download_uri/URIGOESHERE
uri = segments[1]
elif "uri" in req.args:
# http://host/download_uri?uri=URIGOESHERE
uri = req.args["uri"][0]
else:
return rend.NotFound
child = FileDownloader(FileNode(uri, IClient(ctx)), filename)
return child, ()
return rend.Page.locateChild(self, ctx, segments)
child_webform_css = webform.defaultCSS
child_tahoe_css = nevow_File(util.sibpath(__file__, "web/tahoe.css"))
#NOTchild_welcome = Welcome()
def NOTchild_global_vdrive(self, ctx):
client = IClient(ctx)
vdrive = client.getServiceNamed("vdrive")
if vdrive.have_public_root():
d = vdrive.get_public_root()
d.addCallback(lambda dirnode: Directory(dirnode, "/"))
return d
else:
return static.Data("sorry, still initializing", "text/plain")
def NOTchild_private_vdrive(self, ctx):
client = IClient(ctx)
vdrive = client.getServiceNamed("vdrive")
if vdrive.have_private_root():
d = vdrive.get_private_root()
d.addCallback(lambda dirnode: Directory(dirnode, "~"))
return d
else:
return static.Data("sorry, still initializing", "text/plain")
def data_version(self, ctx, data):
v = IClient(ctx).get_versions()
return "tahoe: %s, zfec: %s, foolscap: %s, twisted: %s" % \
(v['allmydata'], v['zfec'], v['foolscap'], v['twisted'])
def data_my_nodeid(self, ctx, data):
return idlib.b2a(IClient(ctx).nodeid)
def data_introducer_furl(self, ctx, data):
return IClient(ctx).introducer_furl
def data_connected_to_introducer(self, ctx, data):
if IClient(ctx).connected_to_introducer():
return "yes"
return "no"
def data_connected_to_vdrive(self, ctx, data):
if IClient(ctx).getServiceNamed("vdrive").have_public_root():
return "yes"
return "no"
def data_num_peers(self, ctx, data):
#client = inevow.ISite(ctx)._client
client = IClient(ctx)
return len(list(client.get_all_peerids()))
def data_peers(self, ctx, data):
d = []
client = IClient(ctx)
for nodeid in sorted(client.get_all_peerids()):
row = (idlib.b2a(nodeid),)
d.append(row)
return d
def render_row(self, ctx, data):
(nodeid_a,) = data
ctx.fillSlots("peerid", nodeid_a)
return ctx.tag
def render_global_vdrive(self, ctx, data):
if IClient(ctx).getServiceNamed("vdrive").have_public_root():
return T.p["To view the global shared filestore, ",
T.a(href="vdrive/global")["Click Here!"],
]
return T.p["vdrive.furl not specified (or vdrive server not "
"responding), no vdrive available."]
def render_private_vdrive(self, ctx, data):
if IClient(ctx).getServiceNamed("vdrive").have_private_root():
return T.p["To view your personal private non-shared filestore, ",
T.a(href="vdrive/private")["Click Here!"],
]
return T.p["personal vdrive not available."]
# this is a form where users can download files by URI
def render_download_form(self, ctx, data):
form = T.form(action="uri", method="get",
enctype="multipart/form-data")[
T.fieldset[
T.legend(class_="freeform-form-label")["Download a file"],
"URI of file to download: ",
T.input(type="text", name="uri"), " ",
"Filename to download as: ",
T.input(type="text", name="filename"), " ",
T.input(type="submit", value="Download"),
]]
return T.div[form]
class WebishServer(service.MultiService):
name = "webish"
def __init__(self, webport):
service.MultiService.__init__(self)
self.root = Root()
#self.root.putChild("", url.here.child("welcome"))#Welcome())
self.site = site = appserver.NevowSite(self.root)
s = strports.service(webport, site)
s.setServiceParent(self)
self.listener = s # stash it so the tests can query for the portnum
def startService(self):
service.MultiService.startService(self)
# to make various services available to render_* methods, we stash a
# reference to the client on the NevowSite. This will be available by
# adapting the 'context' argument to a special marker interface named
# IClient.
self.site.remember(self.parent, IClient)
# I thought you could do the same with an existing interface, but
# apparently 'ISite' does not exist
#self.site._client = self.parent