import os.path from twisted.application import service, strports from twisted.web import static, resource, server, html, http from twisted.python import util, log from twisted.internet import defer 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") 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): 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 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)]) #extract and display file size try: size = unpack_uri(target.get_uri())['size'] except AssertionError: size = "?" ctx.fillSlots("size", size) 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) ctx.fillSlots("size", "-") else: raise RuntimeError("unknown thing %s" % (target,)) return ctx.tag 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], ] 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 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 = "\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 = "\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 += "\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 def renderHTTP(self, ctx): req = inevow.IRequest(ctx) 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 when_done = None if "when_done" in req.args: when_done = req.args["when_done"][0] 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": if not name: raise RuntimeError("set-uri requires a name") if "uri" in req.args: newuri = req.args["uri"][0] 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": 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"] 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 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 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, "")) 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