--- /dev/null
+
+import re, os.path
+from zope.interface import implements
+from twisted.application import service
+from twisted.trial import unittest
+from twisted.internet import defer
+from twisted.web import client, error
+from twisted.python import failure
+from allmydata import webish, interfaces, dirnode, uri
+from allmydata.encode import NotEnoughPeersError
+import itertools
+
+# create a fake uploader/downloader, and a couple of fake dirnodes, then
+# create a webserver that works against them
+
+class MyClient(service.MultiService):
+ nodeid = "fake_nodeid"
+ def get_versions(self):
+ return {'allmydata': "fake",
+ 'foolscap': "fake",
+ 'twisted': "fake",
+ 'zfec': "fake",
+ }
+ introducer_furl = "None"
+ def connected_to_introducer(self):
+ return False
+ def get_all_peerids(self):
+ return []
+
+class MyDownloader(service.Service):
+ implements(interfaces.IDownloader)
+ name = "downloader"
+ def __init__(self, files):
+ self.files = files
+
+ def download(self, uri, target):
+ print "DOWNLOADING", uri
+ if uri not in self.files:
+ e = NotEnoughPeersError()
+ f = failure.Failure(e)
+ target.fail(f)
+ return defer.fail(f)
+ data = self.files[uri]
+ target.open(len(data))
+ target.write(data)
+ target.close()
+ return defer.maybeDeferred(target.finish)
+
+uri_counter = itertools.count()
+
+class MyUploader(service.Service):
+ implements(interfaces.IUploader)
+ name = "uploader"
+ def __init__(self, files):
+ self.files = files
+
+ def upload(self, uploadable):
+ f = uploadable.get_filehandle()
+ data = f.read()
+ uri = str(uri_counter.next())
+ self.files[uri] = data
+ uploadable.close_filehandle(f)
+ return defer.succeed(uri)
+
+class MyDirectoryNode(dirnode.MutableDirectoryNode):
+
+ def __init__(self, nodes, uri=None):
+ self._nodes = nodes
+ if uri is None:
+ uri = str(uri_counter.next())
+ self._uri = str(uri)
+ self._nodes[self._uri] = self
+ self.children = {}
+ self._mutable = True
+
+ def get_immutable_uri(self):
+ return self.get_uri() + "RO"
+
+ def get(self, name):
+ def _try():
+ uri = self.children[name]
+ if uri not in self._nodes:
+ raise IndexError("this isn't supposed to happen")
+ return self._nodes[uri]
+ return defer.maybeDeferred(_try)
+
+ def set_uri(self, name, child_uri):
+ self.children[name] = child_uri
+ return defer.succeed(None)
+
+ def create_empty_directory(self, name):
+ node = MyDirectoryNode(self._nodes)
+ self.children[name] = node.get_uri()
+ return defer.succeed(node)
+
+ def list(self):
+ kids = dict([(name, self._nodes[uri])
+ for name,uri in self.children.iteritems()])
+ return defer.succeed(kids)
+
+class MyFileNode(dirnode.FileNode):
+ pass
+
+
+class MyVirtualDrive(service.Service):
+ name = "vdrive"
+ public_root = None
+ private_root = None
+ def have_public_root(self):
+ return bool(self.public_root)
+ def have_private_root(self):
+ return bool(self.private_root)
+ def get_public_root(self):
+ return defer.succeed(self.public_root)
+ def get_private_root(self):
+ return defer.succeed(self.private_root)
+
+class Web(unittest.TestCase):
+ def setUp(self):
+ self.s = MyClient()
+ self.s.startService()
+ s = webish.WebishServer("0")
+ s.setServiceParent(self.s)
+ port = s.listener._port.getHost().port
+ self.webish_url = "http://localhost:%d" % port
+
+ v = MyVirtualDrive()
+ v.setServiceParent(self.s)
+
+ self.nodes = {} # maps URI to node
+ self.files = {} # maps file URI to contents
+ dl = MyDownloader(self.files)
+ dl.setServiceParent(self.s)
+ ul = MyUploader(self.files)
+ ul.setServiceParent(self.s)
+
+ v.public_root = MyDirectoryNode(self.nodes)
+ v.private_root = MyDirectoryNode(self.nodes)
+ foo = MyDirectoryNode(self.nodes)
+ self._foo_node = foo
+ self._foo_uri = foo.get_uri()
+ self._foo_readonly_uri = foo.get_immutable_uri()
+ v.public_root.children["foo"] = foo.get_uri()
+
+ self.BAR_CONTENTS = "bar.txt contents"
+
+ bar_uri = uri.pack_uri("SI"+"0"*30,
+ "K"+"0"*15,
+ "EH"+"0"*30,
+ 25, 100, 123)
+ bar_txt = MyFileNode(bar_uri, self.s)
+ self._bar_txt_uri = bar_txt.get_uri()
+ self.nodes[bar_uri] = bar_txt
+ self.files[bar_txt.get_uri()] = self.BAR_CONTENTS
+ foo.children["bar.txt"] = bar_txt.get_uri()
+
+ foo.children["sub"] = MyDirectoryNode(self.nodes).get_uri()
+
+ blocking_uri = uri.pack_uri("SI"+"1"*30,
+ "K"+"1"*15,
+ "EH"+"1"*30,
+ 25, 100, 124)
+ blocking_file = MyFileNode(blocking_uri, self.s)
+ self.nodes[blocking_uri] = blocking_file
+ self.files[blocking_uri] = "blocking contents"
+ foo.children["blockingfile"] = blocking_file.get_uri()
+
+ # public/
+ # public/foo/
+ # public/foo/bar.txt
+ # public/foo/sub/
+ # public/foo/blockingfile
+ self.NEWFILE_CONTENTS = "newfile contents\n"
+
+ def tearDown(self):
+ return self.s.stopService()
+
+ def failUnlessIsBarDotTxt(self, res):
+ self.failUnlessEqual(res, self.BAR_CONTENTS)
+
+ def GET(self, urlpath):
+ url = self.webish_url + urlpath
+ return client.getPage(url, method="GET")
+
+ def PUT(self, urlpath, data):
+ url = self.webish_url + urlpath
+ return client.getPage(url, method="PUT", postdata=data)
+
+ def DELETE(self, urlpath):
+ url = self.webish_url + urlpath
+ return client.getPage(url, method="DELETE")
+
+ def POST(self, urlpath, data):
+ url = self.webish_url + urlpath
+ return client.getPage(url, method="POST", postdata=data)
+
+ def shouldFail(self, res, expected_failure, which, substring=None):
+ print "SHOULDFAIL", res
+ if isinstance(res, failure.Failure):
+ res.trap(expected_failure)
+ if substring:
+ self.failUnless(substring in str(res),
+ "substring '%s' not in '%s'"
+ % (substring, str(res)))
+ else:
+ self.fail("%s was supposed to raise %s, not get '%s'" %
+ (which, expected_failure, res))
+
+ def should404(self, res, which):
+ if isinstance(res, failure.Failure):
+ res.trap(error.Error)
+ self.failUnlessEqual(res.value.status, "404")
+ else:
+ self.fail("%s was supposed to raise %s, not get '%s'" %
+ (which, expected_failure, res))
+
+ def test_create(self): # YES
+ pass
+
+ def test_welcome(self): # YES
+ d = self.GET("")
+ return d
+
+ def test_GET_FILEURL(self): # YES
+ d = self.GET("/vdrive/global/foo/bar.txt")
+ d.addCallback(self.failUnlessIsBarDotTxt)
+ return d
+
+ def test_GET_FILEURL_missing(self): # YES
+ d = self.GET("/vdrive/global/foo/missing")
+ def _oops(f):
+ print f
+ print dir(f)
+ print f.value
+ print dir(f.value)
+ print f.value.args
+ print f.value.response
+ print f.value.status
+ return f
+ #d.addBoth(_oops)
+ d.addBoth(self.should404, "test_GET_FILEURL_missing")
+ return d
+
+ def test_PUT_NEWFILEURL(self): # YES
+ d = self.PUT("/vdrive/global/foo/new.txt", self.NEWFILE_CONTENTS)
+ def _check(res):
+ self.failUnless("new.txt" in self._foo_node.children)
+ new_uri = self._foo_node.children["new.txt"]
+ new_contents = self.files[new_uri]
+ self.failUnlessEqual(new_contents, self.NEWFILE_CONTENTS)
+ self.failUnlessEqual(res.strip(), new_uri)
+ d.addCallback(_check)
+ return d
+
+ def test_PUT_NEWFILEURL_mkdirs(self): # YES
+ d = self.PUT("/vdrive/global/foo/newdir/new.txt", self.NEWFILE_CONTENTS)
+ def _check(res):
+ self.failIf("new.txt" in self._foo_node.children)
+ self.failUnless("newdir" in self._foo_node.children)
+ newdir_uri = self._foo_node.children["newdir"]
+ newdir_node = self.nodes[newdir_uri]
+ self.failUnless("new.txt" in newdir_node.children)
+ new_uri = newdir_node.children["new.txt"]
+ new_contents = self.files[new_uri]
+ self.failUnlessEqual(new_contents, self.NEWFILE_CONTENTS)
+ self.failUnlessEqual(res.strip(), new_uri)
+ d.addCallback(_check)
+ return d
+
+ def test_PUT_NEWFILEURL_blocked(self): # YES
+ d = self.PUT("/vdrive/global/foo/blockingfile/new.txt",
+ self.NEWFILE_CONTENTS)
+ d.addBoth(self.shouldFail, error.Error, "PUT_NEWFILEURL_blocked",
+ "403 Forbidden")
+ return d
+
+ def test_DELETE_FILEURL(self):
+ d = self.DELETE("/vdrive/global/foo/bar.txt")
+ return d
+
+ def test_DELETE_FILEURL_missing(self):
+ d = self.DELETE("/vdrive/global/foo/missing")
+ return d
+
+ def test_GET_FILEURL_json(self): # YES
+ # twisted.web.http.parse_qs ignores any query args without an '=', so
+ # I can't do "GET /path?json", I have to do "GET /path/t=json"
+ # instead. This may make it tricky to emulate the S3 interface
+ # completely.
+ d = self.GET("/vdrive/global/foo/bar.txt?t=json")
+ def _got(json):
+ # TODO
+ self.failUnless("JSON" in json, json)
+ d.addCallback(_got)
+ return d
+
+ def test_GET_FILEURL_json_missing(self): # YES
+ d = self.GET("/vdrive/global/foo/missing?json")
+ d.addBoth(self.should404, "test_GET_FILEURL_json_missing")
+ return d
+
+ def test_GET_FILEURL_localfile(self): # YES
+ localfile = os.path.abspath("web/GET_FILEURL_localfile")
+ os.makedirs("web")
+ d = self.GET("/vdrive/global/foo/bar.txt?localfile=%s" % localfile)
+ def _done(res):
+ self.failUnless(os.path.exists(localfile))
+ data = open(localfile, "rb").read()
+ self.failUnlessEqual(data, self.BAR_CONTENTS)
+ d.addCallback(_done)
+ return d
+
+ def test_GET_FILEURL_localfile_nonlocal(self): # YES
+ # TODO: somehow pretend that we aren't local, and verify that the
+ # server refuses to write to local files, probably by changing the
+ # server's idea of what counts as "local".
+ old_LOCALHOST = webish.LOCALHOST
+ webish.LOCALHOST = "127.0.0.2"
+ localfile = os.path.abspath("web/GET_FILEURL_localfile_nonlocal")
+ os.makedirs("web")
+ d = self.GET("/vdrive/global/foo/bar.txt?localfile=%s" % localfile)
+ d.addBoth(self.shouldFail, error.Error, "localfile non-local",
+ "403 Forbidden")
+ def _check(res):
+ self.failIf(os.path.exists(localfile))
+ d.addCallback(_check)
+ def _reset(res):
+ print "RESETTING", res
+ webish.LOCALHOST = old_LOCALHOST
+ return res
+ d.addBoth(_reset)
+ return d
+
+ def test_PUT_NEWFILEURL_localfile(self): # YES
+ localfile = os.path.abspath("web/PUT_NEWFILEURL_localfile")
+ os.makedirs("web")
+ f = open(localfile, "wb")
+ f.write(self.NEWFILE_CONTENTS)
+ f.close()
+ d = self.PUT("/vdrive/global/foo/new.txt?localfile=%s" % localfile, "")
+ def _check(res):
+ self.failUnless("new.txt" in self._foo_node.children)
+ new_uri = self._foo_node.children["new.txt"]
+ new_contents = self.files[new_uri]
+ self.failUnlessEqual(new_contents, self.NEWFILE_CONTENTS)
+ self.failUnlessEqual(res.strip(), new_uri)
+ d.addCallback(_check)
+ return d
+
+ def test_PUT_NEWFILEURL_localfile_mkdirs(self): # YES
+ localfile = os.path.abspath("web/PUT_NEWFILEURL_localfile_mkdirs")
+ os.makedirs("web")
+ f = open(localfile, "wb")
+ f.write(self.NEWFILE_CONTENTS)
+ f.close()
+ d = self.PUT("/vdrive/global/foo/newdir/new.txt?localfile=%s" %
+ localfile, "")
+ def _check(res):
+ self.failIf("new.txt" in self._foo_node.children)
+ self.failUnless("newdir" in self._foo_node.children)
+ newdir_uri = self._foo_node.children["newdir"]
+ newdir_node = self.nodes[newdir_uri]
+ self.failUnless("new.txt" in newdir_node.children)
+ new_uri = newdir_node.children["new.txt"]
+ new_contents = self.files[new_uri]
+ self.failUnlessEqual(new_contents, self.NEWFILE_CONTENTS)
+ self.failUnlessEqual(res.strip(), new_uri)
+ d.addCallback(_check)
+ return d
+
+ def test_GET_FILEURL_uri(self): # YES
+ d = self.GET("/vdrive/global/foo/bar.txt?t=uri")
+ def _check(res):
+ self.failUnlessEqual(res, self._bar_txt_uri)
+ d.addCallback(_check)
+ return d
+
+ def test_GET_FILEURL_uri_missing(self): # YES
+ d = self.GET("/vdrive/global/foo/missing?t=uri")
+ d.addBoth(self.should404, "test_GET_FILEURL_uri_missing")
+ return d
+
+ def test_GET_DIRURL(self): # YES
+ d = self.GET("/vdrive/global/foo")
+ def _check(res):
+ self.failUnless(re.search(r'<td><a href="bar.txt">bar.txt</a></td>'
+ '\s+<td>FILE</td>'
+ '\s+<td>123</td>'
+ , res))
+ self.failUnless(re.search(r'<td><a href="sub">sub</a></td>'
+ '\s+<td>DIR</td>', res))
+ d.addCallback(_check)
+ return d
+
+ def test_GET_DIRURL_json(self): # YES
+ d = self.GET("/vdrive/global/foo?t=json")
+ def _got(json):
+ # TODO
+ self.failUnless("JSON" in json, json)
+ d.addCallback(_got)
+ return d
+
+ def test_GET_DIRURL_uri(self): # YES
+ d = self.GET("/vdrive/global/foo?t=uri")
+ def _check(res):
+ self.failUnlessEqual(res, self._foo_uri)
+ d.addCallback(_check)
+ return d
+
+ def test_GET_DIRURL_readonly_uri(self): # YES
+ d = self.GET("/vdrive/global/foo?t=readonly-uri")
+ def _check(res):
+ self.failUnlessEqual(res, self._foo_readonly_uri)
+ d.addCallback(_check)
+ return d
+
+ def test_PUT_NEWDIRURL(self): # YES
+ d = self.PUT("/vdrive/global/foo/newdir?t=mkdir", "")
+ def _check(res):
+ self.failUnless("newdir" in self._foo_node.children)
+ newdir_uri = self._foo_node.children["newdir"]
+ newdir_node = self.nodes[newdir_uri]
+ self.failIf(newdir_node.children)
+ d.addCallback(_check)
+ return d
+
+ def test_PUT_NEWDIRURL_mkdirs(self): # YES
+ d = self.PUT("/vdrive/global/foo/subdir/newdir?t=mkdir", "")
+ def _check(res):
+ self.failIf("newdir" in self._foo_node.children)
+ self.failUnless("subdir" in self._foo_node.children)
+ subdir_node = self.nodes[self._foo_node.children["subdir"]]
+ self.failUnless("newdir" in subdir_node.children)
+ newdir_node = self.nodes[subdir_node.children["newdir"]]
+ self.failIf(newdir_node.children)
+ d.addCallback(_check)
+ return d
+
+ def test_DELETE_DIRURL(self):
+ d = self.DELETE("/vdrive/global/foo")
+ return d
+
+ def test_DELETE_DIRURL_missing(self):
+ d = self.DELETE("/vdrive/global/missing")
+ return d
+
+ def test_GET_DIRURL_localdir(self):
+ localdir = os.path.abspath("web/GET_DIRURL_localdir")
+ os.makedirs("web")
+ d = self.GET("/vdrive/global/foo?localdir=%s" % localdir)
+ return d
+
+ def test_PUT_NEWDIRURL_localdir(self):
+ localdir = os.path.abspath("web/PUT_NEWDIRURL_localdir")
+ os.makedirs("web")
+ # create some files there
+ d = self.GET("/vdrive/global/foo/newdir?localdir=%s" % localdir)
+ return d
+
+ def test_PUT_NEWDIRURL_localdir_mkdirs(self):
+ localdir = os.path.abspath("web/PUT_NEWDIRURL_localdir_mkdirs")
+ os.makedirs("web")
+ # create some files there
+ d = self.GET("/vdrive/global/foo/subdir/newdir?localdir=%s" % localdir)
+ return d
+
+ def test_POST_upload(self):
+ form = "TODO"
+ d = self.POST("/vdrive/global/foo", form)
+ return d
+
+ def test_POST_mkdir(self):
+ form = "TODO"
+ d = self.POST("/vdrive/global/foo", form)
+ return d
+
+ def test_POST_put_uri(self):
+ form = "TODO"
+ d = self.POST("/vdrive/global/foo", form)
+ return d
+
+ def test_POST_delete(self):
+ form = "TODO, bar.txt"
+ d = self.POST("/vdrive/global/foo", form)
+ return d
+
+ def test_URI_GET(self):
+ d = self.GET("/uri/%s/bar.txt" % foo_uri)
+ return d
+
+ def test_PUT_NEWFILEURL_uri(self):
+ d = self.PUT("/vdrive/global/foo/new.txt?uri", new_uri)
+ return d
+
+ def test_XMLRPC(self):
+ pass
+
+
+
+"""
+ # GET / (welcome)
+ # GET FILEURL
+# PUT NEWFILEURL
+# DELETE FILEURL
+ # GET FILEURL?t=json
+# GET FILEURL?localfile=$FILENAME
+# PUT NEWFILEURL?localfile=$FILENAME
+# GET FILEURL?t=uri
+# GET DIRURL
+# GET DIRURL?t=json
+# GET DIRURL?t=uri
+# GET DIRURL?t=readonly-uri
+# PUT NEWDIRURL?t=mkdir
+# DELETE DIRURL
+# GET DIRURL?localdir=$DIRNAME
+# PUT NEWDIRURL?localdir=$DIRNAME
+# POST DIRURL?t=upload-form
+# POST DIRURL?t=mkdir-form
+# POST DIRURL?t=put-uri-form
+# POST DIRURL?t=delete-form
+# GET .../url/$URI
+# and a few others
+# PUT NEWFILEURL?t=uri
+# /xmlrpc
+"""
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
from allmydata.uri import unpack_uri
from allmydata.interfaces import IDownloadTarget, IDirectoryNode, IFileNode
from allmydata.dirnode import FileNode
-from allmydata import upload
+from allmydata import upload, download
from zope.interface import implements, Interface
import urllib
from formless import annotate, webform
def get_uploader_service(ctx):
return IClient(ctx).getServiceNamed("uploader")
-class Welcome(rend.Page):
- addSlash = True
- docFactory = getxmlfile("welcome.xhtml")
-
- 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="../global_vdrive")["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="../private_vdrive")["Click Here!"],
- ]
- return T.p["personal vdrive not available."]
-
- # this is a form where users can download files by URI
-
- def bind_download(self, ctx):
- uriarg = annotate.Argument("uri",
- annotate.String("URI of file to download: "))
- namearg = annotate.Argument("filename",
- annotate.String("Filename to download as: "))
- ctxarg = annotate.Argument("ctx", annotate.Context())
- meth = annotate.Method(arguments=[uriarg, namearg, ctxarg],
- label="Download File by URI")
- # buttons always use value=data.label
- # MethodBindingRenderer uses value=(data.action or data.label)
- return annotate.MethodBinding("download", meth, action="Download")
-
- def download(self, uri, filename, ctx):
- log.msg("webish downloading URI")
- target = url.here.sibling("download_uri").add("uri", uri)
- if filename:
- target = target.add("filename", filename)
- return target
-
- def render_forms(self, ctx, data):
- return webform.renderForms()
-
class Directory(rend.Page):
addSlash = True
self._dirname = dirname
def childFactory(self, ctx, name):
+ print "Directory.childFactory", name
if name.startswith("freeform"): # ick
return None
if name == "@manifest": # ick, this time it's my fault
return Manifest(self._dirnode, self._dirname)
- if self._dirname == "/":
- dirname = "/" + name
- else:
- dirname = self._dirname + "/" + name
- d = self._dirnode.get(name)
- def _got_child(res):
- if IFileNode.providedBy(res):
- dl = get_downloader_service(ctx)
- return Downloader(dl, name, res)
- elif IDirectoryNode.providedBy(res):
- return Directory(res, dirname)
- else:
- raise RuntimeError("what is this %s" % res)
- d.addCallback(_got_child)
- return d
+ return rend.NotFound
def render_title(self, ctx, data):
+ print "DIRECTORY.render_title"
return ctx.tag["Directory '%s':" % self._dirname]
def render_header(self, ctx, data):
self.contentEncodings,
self.defaultType)
-class Downloader(resource.Resource):
- def __init__(self, downloader, name, filenode):
- self._downloader = downloader
+class FileDownloader(resource.Resource):
+ def __init__(self, name, filenode):
self._name = name
IFileNode(filenode)
self._filenode = filenode
- def render(self, ctx):
- req = inevow.IRequest(ctx)
+ def render(self, req):
gte = static.getTypeAndEncoding
type, encoding = gte(self._name,
static.File.contentTypes,
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 LocalFileDownloader(resource.Resource):
+ def __init__(self, filenode, local_filename):
+ self._local_filename = local_filename
+ IFileNode(filenode)
+ self._filenode = filenode
+
+ def render(self, req):
+ print "LOCALFILEDOWNLOADER", self._local_filename
+ 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):
+ file_uri = self._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 renderHTTP(self, ctx):
+ file_uri = self._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 renderHTTP(self, ctx):
+ file_uri = self._filenode.get_uri()
+ return file_uri
+
+class LocalDirectoryDownloader(resource.Resource):
+ def __init__(self, dirnode):
+ self._dirnode = dirnode
+
+ def renderHTTP(self, ctx):
+ dl = get_downloader_service(ctx)
+ pass # TODO
+
+class DirectoryJSONMetadata(rend.Page):
+ def __init__(self, dirnode):
+ self._dirnode = dirnode
+
+ def renderHTTP(self, ctx):
+ file_uri = self._dirnode.get_uri()
+ data = "dirnode\n"
+ data += "JSONny stuff here\n"
+ d = self._dirnode.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 renderHTTP(self, ctx):
+ file_uri = self._dirnode.get_uri()
+ pieces = unpack_uri(file_uri)
+ data = "<xmlish>\n"
+ data += "dirnode\n"
+ data += "stuff here\n"
+ d = self._dirnode.list()
+ def _got(children, data):
+ for name, childnode in children:
+ data += "name=%s, child_uri=%s" % (name, childnode.get_uri())
+ return data
+ d.addCallback(_got)
+ def _done(data):
+ data += "</done>\n"
+ return data
+ d.addCallback(_done)
+ return d
+
+class DirectoryURI(DirectoryJSONMetadata):
+ def renderHTTP(self, ctx):
+ dir_uri = self._dirnode.get_uri()
+ return dir_uri
+
+class DirectoryReadonlyURI(DirectoryJSONMetadata):
+ def renderHTTP(self, ctx):
+ dir_uri = self._dirnode.get_immutable_uri()
+ return dir_uri
+
+class POSTHandler(rend.Page):
+ def __init__(self, node):
+ self._node = node
+
+ # TODO: handler methods
+
+class DELETEHandler(rend.Page):
+ def __init__(self, node, name):
+ self._node = node
+ self._name = name
+
+ def renderHTTP(self, ctx):
+ d = self._node.delete(self._name)
+ def _done(res):
+ # what should this return??
+ return "%s deleted" % self._name
+ d.addCallback(_done)
+ 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._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)
+ print "CREATING", path[0]
+ 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 = self._uploader.upload(uploadable)
+ def _uploaded(uri):
+ print "SETTING URI", name, 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 _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):
+ pass # TODO
+
class Manifest(rend.Page):
docFactory = getxmlfile("manifest.xhtml")
def __init__(self, dirnode, dirname):
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]
+
+ print "VDrive.locateChild", method, segments, req.args
+ t = ""
+ if "t" in req.args:
+ t = req.args["t"][0]
+
+ localfile = None
+ if "localfile" in req.args:
+ localfile = req.args["localfile"][0]
+ localdir = None
+ if "localdir" in req.args:
+ localdir = req.args["localdir"][0]
+ if (localfile or localdir) and 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.
+ name = path[-1]
+ d = self.get_child_at_path(path)
+ def file_or_dir(node):
+ if IFileNode.providedBy(node):
+ if localfile:
+ # write contents to a local file
+ return LocalFileDownloader(node, localfile), ()
+ elif t == "":
+ # send contents as the result
+ print "FileDownloader"
+ return FileDownloader(name, node), ()
+ elif t == "json":
+ print "Localfilejsonmetadata"
+ return FileJSONMetadata(node), ()
+ elif t == "xml":
+ return FileXMLMetadata(node), ()
+ elif t == "uri":
+ return FileURI(node), ()
+ else:
+ raise RuntimeError("bad t=%s" % t)
+ elif IDirectoryNode.providedBy(node):
+ print "GOT DIR"
+ if localdir:
+ # recursive download to a local directory
+ return LocalDirectoryDownloader(node, localdir), ()
+ elif t == "":
+ # send an HTML representation of the directory
+ print "GOT HTML DIR"
+ return Directory(node, name), ()
+ 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), ()
+ 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)
+ d.addCallback(lambda node: POSTHandler(node), ())
+ 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])
+ d.addCallback(lambda node: DELETEHandler(node, path[-1]), )
+ 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):
- if segments[0] == "download_uri":
+ client = IClient(ctx)
+ vdrive = client.getServiceNamed("vdrive")
+ print "Root.locateChild", segments
+
+ 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) < 2:
+ return rend.NotFound
+ uri = segments[1]
+ d = vdrive.get_node(uri)
+ d.addCallback(lambda node: VDrive(node), uri)
+ d.addCallback(lambda vd: vd.locateChild(ctx, segments[2:]))
+ return d
+ elif segments[0] == "xmlrpc":
+ pass # TODO
+ elif segments[0] == "download_uri":
req = inevow.IRequest(ctx)
dl = get_downloader_service(ctx)
filename = "unknown_filename"
uri = req.args["uri"][0]
else:
return rend.NotFound
- child = Downloader(dl, filename, FileNode(uri, IClient(ctx)))
+ child = FileDownloader(filename, FileNode(uri, IClient(ctx)))
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"))
- child_welcome = Welcome()
+ #child_welcome = Welcome()
def child_global_vdrive(self, ctx):
client = IClient(ctx)
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="../global_vdrive")["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="../private_vdrive")["Click Here!"],
+ ]
+ return T.p["personal vdrive not available."]
+
+ # this is a form where users can download files by URI
+
+ def bind_download(self, ctx):
+ uriarg = annotate.Argument("uri",
+ annotate.String("URI of file to download: "))
+ namearg = annotate.Argument("filename",
+ annotate.String("Filename to download as: "))
+ ctxarg = annotate.Argument("ctx", annotate.Context())
+ meth = annotate.Method(arguments=[uriarg, namearg, ctxarg],
+ label="Download File by URI")
+ # buttons always use value=data.label
+ # MethodBindingRenderer uses value=(data.action or data.label)
+ return annotate.MethodBinding("download", meth, action="Download")
+
+ def download(self, uri, filename, ctx):
+ log.msg("webish downloading URI")
+ target = url.here.sibling("download_uri").add("uri", uri)
+ if filename:
+ target = target.add("filename", filename)
+ return target
+
+ def render_forms(self, ctx, data):
+ return webform.renderForms()
+
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.root.putChild("", url.here.child("welcome"))#Welcome())
self.site = site = appserver.NevowSite(self.root)
s = strports.service(webport, site)