]> git.rkrishnan.org Git - tahoe-lafs/tahoe-lafs.git/blob - src/allmydata/web/root.py
web: make nickname more visible in the welcome page, closes #361
[tahoe-lafs/tahoe-lafs.git] / src / allmydata / web / root.py
1
2 import time
3
4 from twisted.internet import address
5 from twisted.web import http
6 from nevow import rend, url, tags as T
7 from nevow.inevow import IRequest
8 from nevow.static import File as nevow_File # TODO: merge with static.File?
9 from nevow.util import resource_filename
10 from formless import webform
11
12 import allmydata # to display import path
13 from allmydata import get_package_versions_string
14 from allmydata import provisioning
15 from allmydata.util import idlib
16 from allmydata.interfaces import IFileNode
17 from allmydata.web import filenode, directory, unlinked, status
18 from allmydata.web.common import abbreviate_size, IClient, getxmlfile, \
19      WebError, get_arg, RenderMixin
20
21
22
23 class URIHandler(RenderMixin, rend.Page):
24     # I live at /uri . There are several operations defined on /uri itself,
25     # mostly involed with creation of unlinked files and directories.
26
27     def render_GET(self, ctx):
28         req = IRequest(ctx)
29         uri = get_arg(req, "uri", None)
30         if uri is None:
31             raise WebError("GET /uri requires uri=")
32         there = url.URL.fromContext(ctx)
33         there = there.clear("uri")
34         # I thought about escaping the childcap that we attach to the URL
35         # here, but it seems that nevow does that for us.
36         there = there.child(uri)
37         return there
38
39     def render_PUT(self, ctx):
40         req = IRequest(ctx)
41         # either "PUT /uri" to create an unlinked file, or
42         # "PUT /uri?t=mkdir" to create an unlinked directory
43         t = get_arg(req, "t", "").strip()
44         if t == "":
45             mutable = bool(get_arg(req, "mutable", "").strip())
46             if mutable:
47                 return unlinked.PUTUnlinkedSSK(ctx)
48             else:
49                 return unlinked.PUTUnlinkedCHK(ctx)
50         if t == "mkdir":
51             return unlinked.PUTUnlinkedCreateDirectory(ctx)
52         errmsg = ("/uri accepts only PUT, PUT?t=mkdir, POST?t=upload, "
53                   "and POST?t=mkdir")
54         raise WebError(errmsg, http.BAD_REQUEST)
55
56     def render_POST(self, ctx):
57         # "POST /uri?t=upload&file=newfile" to upload an
58         # unlinked file or "POST /uri?t=mkdir" to create a
59         # new directory
60         req = IRequest(ctx)
61         t = get_arg(req, "t", "").strip()
62         if t in ("", "upload"):
63             mutable = bool(get_arg(req, "mutable", "").strip())
64             if mutable:
65                 return unlinked.POSTUnlinkedSSK(ctx)
66             else:
67                 return unlinked.POSTUnlinkedCHK(ctx)
68         if t == "mkdir":
69             return unlinked.POSTUnlinkedCreateDirectory(ctx)
70         errmsg = ("/uri accepts only PUT, PUT?t=mkdir, POST?t=upload, "
71                   "and POST?t=mkdir")
72         raise WebError(errmsg, http.BAD_REQUEST)
73
74     def childFactory(self, ctx, name):
75         # 'name' is expected to be a URI
76         client = IClient(ctx)
77         try:
78             node = client.create_node_from_uri(name)
79             return directory.make_handler_for(node)
80         except (TypeError, AssertionError):
81             raise WebError("'%s' is not a valid file- or directory- cap"
82                            % name)
83
84 class FileHandler(rend.Page):
85     # I handle /file/$FILECAP[/IGNORED] , which provides a URL from which a
86     # file can be downloaded correctly by tools like "wget".
87
88     def childFactory(self, ctx, name):
89         req = IRequest(ctx)
90         if req.method not in ("GET", "HEAD"):
91             raise WebError("/file can only be used with GET or HEAD")
92         # 'name' must be a file URI
93         client = IClient(ctx)
94         try:
95             node = client.create_node_from_uri(name)
96         except (TypeError, AssertionError):
97             raise WebError("'%s' is not a valid file- or directory- cap"
98                            % name)
99         if not IFileNode.providedBy(node):
100             raise WebError("'%s' is not a file-cap" % name)
101         return filenode.FileNodeDownloadHandler(node)
102
103     def renderHTTP(self, ctx):
104         raise WebError("/file must be followed by a file-cap and a name",
105                        http.NOT_FOUND)
106
107 class Root(rend.Page):
108
109     addSlash = True
110     docFactory = getxmlfile("welcome.xhtml")
111
112     child_uri = URIHandler()
113     child_cap = URIHandler()
114     child_file = FileHandler()
115     child_named = FileHandler()
116
117     child_webform_css = webform.defaultCSS
118     child_tahoe_css = nevow_File(resource_filename('allmydata.web', 'tahoe.css'))
119
120     child_provisioning = provisioning.ProvisioningTool()
121     child_status = status.Status()
122     child_helper_status = status.HelperStatus()
123     child_statistics = status.Statistics()
124
125     def data_version(self, ctx, data):
126         return get_package_versions_string()
127     def data_import_path(self, ctx, data):
128         return str(allmydata)
129     def data_my_nodeid(self, ctx, data):
130         return idlib.nodeid_b2a(IClient(ctx).nodeid)
131     def data_my_nickname(self, ctx, data):
132         return IClient(ctx).nickname
133
134     def render_services(self, ctx, data):
135         ul = T.ul()
136         client = IClient(ctx)
137         try:
138             ss = client.getServiceNamed("storage")
139             allocated_s = abbreviate_size(ss.allocated_size())
140             allocated = "about %s allocated" % allocated_s
141             sizelimit = "no size limit"
142             if ss.sizelimit is not None:
143                 sizelimit = "size limit is %s" % abbreviate_size(ss.sizelimit)
144             ul[T.li["Storage Server: %s, %s" % (allocated, sizelimit)]]
145         except KeyError:
146             ul[T.li["Not running storage server"]]
147
148         try:
149             h = client.getServiceNamed("helper")
150             stats = h.get_stats()
151             active_uploads = stats["chk_upload_helper.active_uploads"]
152             ul[T.li["Helper: %d active uploads" % (active_uploads,)]]
153         except KeyError:
154             ul[T.li["Not running helper"]]
155
156         return ctx.tag[ul]
157
158     def data_introducer_furl(self, ctx, data):
159         return IClient(ctx).introducer_furl
160     def data_connected_to_introducer(self, ctx, data):
161         if IClient(ctx).connected_to_introducer():
162             return "yes"
163         return "no"
164
165     def data_helper_furl(self, ctx, data):
166         try:
167             uploader = IClient(ctx).getServiceNamed("uploader")
168         except KeyError:
169             return None
170         furl, connected = uploader.get_helper_info()
171         return furl
172     def data_connected_to_helper(self, ctx, data):
173         try:
174             uploader = IClient(ctx).getServiceNamed("uploader")
175         except KeyError:
176             return "no" # we don't even have an Uploader
177         furl, connected = uploader.get_helper_info()
178         if connected:
179             return "yes"
180         return "no"
181
182     def data_known_storage_servers(self, ctx, data):
183         ic = IClient(ctx).introducer_client
184         servers = [c
185                    for c in ic.get_all_connectors().values()
186                    if c.service_name == "storage"]
187         return len(servers)
188
189     def data_connected_storage_servers(self, ctx, data):
190         ic = IClient(ctx).introducer_client
191         return len(ic.get_all_connections_for("storage"))
192
193     def data_services(self, ctx, data):
194         ic = IClient(ctx).introducer_client
195         c = [ (service_name, nodeid, rsc)
196               for (nodeid, service_name), rsc
197               in ic.get_all_connectors().items() ]
198         c.sort()
199         return c
200
201     def render_service_row(self, ctx, data):
202         (service_name, nodeid, rsc) = data
203         ctx.fillSlots("peerid", idlib.nodeid_b2a(nodeid))
204         ctx.fillSlots("nickname", rsc.nickname)
205         if rsc.rref:
206             rhost = rsc.remote_host
207             if nodeid == IClient(ctx).nodeid:
208                 rhost_s = "(loopback)"
209             elif isinstance(rhost, address.IPv4Address):
210                 rhost_s = "%s:%d" % (rhost.host, rhost.port)
211             else:
212                 rhost_s = str(rhost)
213             connected = "Yes: to " + rhost_s
214             since = rsc.last_connect_time
215         else:
216             connected = "No"
217             since = rsc.last_loss_time
218
219         TIME_FORMAT = "%H:%M:%S %d-%b-%Y"
220         ctx.fillSlots("connected", connected)
221         ctx.fillSlots("since", time.strftime(TIME_FORMAT, time.localtime(since)))
222         ctx.fillSlots("announced", time.strftime(TIME_FORMAT,
223                                                  time.localtime(rsc.announcement_time)))
224         ctx.fillSlots("version", rsc.version)
225         ctx.fillSlots("service_name", rsc.service_name)
226
227         return ctx.tag
228
229     def render_download_form(self, ctx, data):
230         # this is a form where users can download files by URI
231         form = T.form(action="uri", method="get",
232                       enctype="multipart/form-data")[
233             T.fieldset[
234             T.legend(class_="freeform-form-label")["Download a file"],
235             "URI to download: ",
236             T.input(type="text", name="uri"), " ",
237             "Filename to download as: ",
238             T.input(type="text", name="filename"), " ",
239             T.input(type="submit", value="Download!"),
240             ]]
241         return T.div[form]
242
243     def render_view_form(self, ctx, data):
244         # this is a form where users can download files by URI, or jump to a
245         # named directory
246         form = T.form(action="uri", method="get",
247                       enctype="multipart/form-data")[
248             T.fieldset[
249             T.legend(class_="freeform-form-label")["View a file or directory"],
250             "URI to view: ",
251             T.input(type="text", name="uri"), " ",
252             T.input(type="submit", value="View!"),
253             ]]
254         return T.div[form]
255
256     def render_upload_form(self, ctx, data):
257         # this is a form where users can upload unlinked files
258         form = T.form(action="uri", method="post",
259                       enctype="multipart/form-data")[
260             T.fieldset[
261             T.legend(class_="freeform-form-label")["Upload a file"],
262             "Choose a file: ",
263             T.input(type="file", name="file", class_="freeform-input-file"),
264             T.input(type="hidden", name="t", value="upload"),
265             " Mutable?:", T.input(type="checkbox", name="mutable"),
266             T.input(type="submit", value="Upload!"),
267             ]]
268         return T.div[form]
269
270     def render_mkdir_form(self, ctx, data):
271         # this is a form where users can create new directories
272         form = T.form(action="uri", method="post",
273                       enctype="multipart/form-data")[
274             T.fieldset[
275             T.legend(class_="freeform-form-label")["Create a directory"],
276             T.input(type="hidden", name="t", value="mkdir"),
277             T.input(type="hidden", name="redirect_to_result", value="true"),
278             T.input(type="submit", value="Create Directory!"),
279             ]]
280         return T.div[form]
281