mirror of
https://github.com/tahoe-lafs/tahoe-lafs.git
synced 2025-01-21 03:55:27 +00:00
checkpointing new webapi: not all tests pass yet
This commit is contained in:
parent
21e12f383d
commit
6570253d6b
525
src/allmydata/test/test_web.py
Normal file
525
src/allmydata/test/test_web.py
Normal file
@ -0,0 +1,525 @@
|
|||||||
|
|
||||||
|
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
|
||||||
|
"""
|
@ -2,13 +2,14 @@
|
|||||||
from twisted.application import service, strports
|
from twisted.application import service, strports
|
||||||
from twisted.web import static, resource, server, html, http
|
from twisted.web import static, resource, server, html, http
|
||||||
from twisted.python import util, log
|
from twisted.python import util, log
|
||||||
|
from twisted.internet import defer
|
||||||
from nevow import inevow, rend, loaders, appserver, url, tags as T
|
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 nevow.static import File as nevow_File # TODO: merge with static.File?
|
||||||
from allmydata.util import idlib
|
from allmydata.util import idlib
|
||||||
from allmydata.uri import unpack_uri
|
from allmydata.uri import unpack_uri
|
||||||
from allmydata.interfaces import IDownloadTarget, IDirectoryNode, IFileNode
|
from allmydata.interfaces import IDownloadTarget, IDirectoryNode, IFileNode
|
||||||
from allmydata.dirnode import FileNode
|
from allmydata.dirnode import FileNode
|
||||||
from allmydata import upload
|
from allmydata import upload, download
|
||||||
from zope.interface import implements, Interface
|
from zope.interface import implements, Interface
|
||||||
import urllib
|
import urllib
|
||||||
from formless import annotate, webform
|
from formless import annotate, webform
|
||||||
@ -24,84 +25,6 @@ def get_downloader_service(ctx):
|
|||||||
def get_uploader_service(ctx):
|
def get_uploader_service(ctx):
|
||||||
return IClient(ctx).getServiceNamed("uploader")
|
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):
|
class Directory(rend.Page):
|
||||||
addSlash = True
|
addSlash = True
|
||||||
@ -112,27 +35,15 @@ class Directory(rend.Page):
|
|||||||
self._dirname = dirname
|
self._dirname = dirname
|
||||||
|
|
||||||
def childFactory(self, ctx, name):
|
def childFactory(self, ctx, name):
|
||||||
|
print "Directory.childFactory", name
|
||||||
if name.startswith("freeform"): # ick
|
if name.startswith("freeform"): # ick
|
||||||
return None
|
return None
|
||||||
if name == "@manifest": # ick, this time it's my fault
|
if name == "@manifest": # ick, this time it's my fault
|
||||||
return Manifest(self._dirnode, self._dirname)
|
return Manifest(self._dirnode, self._dirname)
|
||||||
if self._dirname == "/":
|
return rend.NotFound
|
||||||
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
|
|
||||||
|
|
||||||
def render_title(self, ctx, data):
|
def render_title(self, ctx, data):
|
||||||
|
print "DIRECTORY.render_title"
|
||||||
return ctx.tag["Directory '%s':" % self._dirname]
|
return ctx.tag["Directory '%s':" % self._dirname]
|
||||||
|
|
||||||
def render_header(self, ctx, data):
|
def render_header(self, ctx, data):
|
||||||
@ -380,15 +291,13 @@ class TypedFile(static.File):
|
|||||||
self.contentEncodings,
|
self.contentEncodings,
|
||||||
self.defaultType)
|
self.defaultType)
|
||||||
|
|
||||||
class Downloader(resource.Resource):
|
class FileDownloader(resource.Resource):
|
||||||
def __init__(self, downloader, name, filenode):
|
def __init__(self, name, filenode):
|
||||||
self._downloader = downloader
|
|
||||||
self._name = name
|
self._name = name
|
||||||
IFileNode(filenode)
|
IFileNode(filenode)
|
||||||
self._filenode = filenode
|
self._filenode = filenode
|
||||||
|
|
||||||
def render(self, ctx):
|
def render(self, req):
|
||||||
req = inevow.IRequest(ctx)
|
|
||||||
gte = static.getTypeAndEncoding
|
gte = static.getTypeAndEncoding
|
||||||
type, encoding = gte(self._name,
|
type, encoding = gte(self._name,
|
||||||
static.File.contentTypes,
|
static.File.contentTypes,
|
||||||
@ -400,6 +309,242 @@ class Downloader(resource.Resource):
|
|||||||
d.addErrback(lambda why: None)
|
d.addErrback(lambda why: None)
|
||||||
return server.NOT_DONE_YET
|
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):
|
class Manifest(rend.Page):
|
||||||
docFactory = getxmlfile("manifest.xhtml")
|
docFactory = getxmlfile("manifest.xhtml")
|
||||||
def __init__(self, dirnode, dirname):
|
def __init__(self, dirnode, dirname):
|
||||||
@ -419,10 +564,148 @@ class Manifest(rend.Page):
|
|||||||
ctx.fillSlots("refresh_capability", refresh_cap)
|
ctx.fillSlots("refresh_capability", refresh_cap)
|
||||||
return ctx.tag
|
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):
|
class Root(rend.Page):
|
||||||
|
|
||||||
|
addSlash = True
|
||||||
|
docFactory = getxmlfile("welcome.xhtml")
|
||||||
|
|
||||||
def locateChild(self, ctx, segments):
|
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)
|
req = inevow.IRequest(ctx)
|
||||||
dl = get_downloader_service(ctx)
|
dl = get_downloader_service(ctx)
|
||||||
filename = "unknown_filename"
|
filename = "unknown_filename"
|
||||||
@ -436,14 +719,14 @@ class Root(rend.Page):
|
|||||||
uri = req.args["uri"][0]
|
uri = req.args["uri"][0]
|
||||||
else:
|
else:
|
||||||
return rend.NotFound
|
return rend.NotFound
|
||||||
child = Downloader(dl, filename, FileNode(uri, IClient(ctx)))
|
child = FileDownloader(filename, FileNode(uri, IClient(ctx)))
|
||||||
return child, ()
|
return child, ()
|
||||||
return rend.Page.locateChild(self, ctx, segments)
|
return rend.Page.locateChild(self, ctx, segments)
|
||||||
|
|
||||||
child_webform_css = webform.defaultCSS
|
child_webform_css = webform.defaultCSS
|
||||||
child_tahoe_css = nevow_File(util.sibpath(__file__, "web/tahoe.css"))
|
child_tahoe_css = nevow_File(util.sibpath(__file__, "web/tahoe.css"))
|
||||||
|
|
||||||
child_welcome = Welcome()
|
#child_welcome = Welcome()
|
||||||
|
|
||||||
def child_global_vdrive(self, ctx):
|
def child_global_vdrive(self, ctx):
|
||||||
client = IClient(ctx)
|
client = IClient(ctx)
|
||||||
@ -465,6 +748,80 @@ class Root(rend.Page):
|
|||||||
else:
|
else:
|
||||||
return static.Data("sorry, still initializing", "text/plain")
|
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):
|
class WebishServer(service.MultiService):
|
||||||
name = "webish"
|
name = "webish"
|
||||||
@ -472,7 +829,7 @@ class WebishServer(service.MultiService):
|
|||||||
def __init__(self, webport):
|
def __init__(self, webport):
|
||||||
service.MultiService.__init__(self)
|
service.MultiService.__init__(self)
|
||||||
self.root = Root()
|
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)
|
self.site = site = appserver.NevowSite(self.root)
|
||||||
s = strports.service(webport, site)
|
s = strports.service(webport, site)
|
||||||
|
Loading…
Reference in New Issue
Block a user