1 """
2 Common code for the nevow based interface.
3 """
4
5
6
7
8
9
10
11 import re
12 import urllib
13
14 from nevow import appserver
15 from nevow import tags as T
16 from nevow import loaders
17 from nevow import inevow
18 from nevow import rend
19 from nevow import static
20 try:
21 from twisted.web import http
22 except ImportError:
23 from twisted.protocols import http
24
25 from gavo import base
26 from gavo import svcs
27 from gavo import utils
28 from gavo.base import meta
29 from gavo.protocols import creds
30
31
32
33 static.File.contentTypes[".ascii"] = "application/octet-stream"
34 static.File.contentTypes[".f"] = "text/x-fortran"
35 static.File.contentTypes[".vot"] = base.votableType,
36 static.File.contentTypes[".rd"] = "application/x-gavo-descriptor+xml"
37 static.File.contentTypes[".f90"] = "text/x-fortran"
38 static.File.contentTypes[".skyglow"] = "text/plain"
39 static.File.contentTypes[".fitstable"] = "application/fits"
40 static.File.contentTypes[".fits"] = "image/fits"
41
42 static.File.contentTypes[".shtml"] = "text/nevow-template"
43
44
49
50
52 """returns the first value of key in the nevow context ctx.
53 """
54 return utils.getfirst(inevow.IRequest(ctx).args, key, default)
55
56
106
107
109 """returns the value of fun(*args) if the logged in user is in reqGroup,
110 requests authentication otherwise.
111 """
112 request = inevow.IRequest(ctx)
113 if creds.hasCredentials(request.getUser(), request.getPassword(), reqGroup):
114 return fun(*args)
115 else:
116 raise svcs.Authenticate()
117
118
120 """is the stan loader with a doctype and a namespace added.
121 """
122
123 DOCTYPE = T.xml('<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"'
124 ' "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\n')
125
126 - def __init__(self, rootEl, pattern=None):
129
130
131 JSEXT = ".js"
132
133
135 """A base for renderer (python) mixins within the DC.
136
137 Including standard stylesheets/js/whatever:
138 <head n:render="commonhead">...</head>
139
140 Rendering internal links (important for off-root operation):
141
142 * <tag href|src="/foo" n:render="rootlink"/>
143
144 """
146 """returns unicode(data); use this when render="string" might have to
147 format non-ASCII.
148 """
149 return ctx.tag[unicode(data)]
150
152 tag = ctx.tag
153 def munge(key):
154 if key in tag.attributes:
155 tag.attributes[key] = base.makeSitePath(tag.attributes[key])
156 munge("src")
157 munge("href")
158 return tag
159
161
162
163
164 originalChildren = ctx.tag.children[:]
165 ctx.tag.clear()
166 res = ctx.tag[
167 T.meta(**{"http-equiv": "Content-type",
168 "content": "text/html;charset=UTF-8"}),
169 T.link(rel="stylesheet", href=base.makeSitePath("/formal.css"),
170 type="text/css"),
171 T.link(rel="stylesheet", href=base.makeSitePath(
172 "/static/css/gavo_dc.css"), type="text/css"),
173 T.script(src=base.makeSitePath("/static/js/jquery-gavo.js"),
174 type="text/javascript"),
175 T.script(type='text/javascript', src=base.makeSitePath(
176 'static/js/formal'+JSEXT)),
177 T.script(src=base.makeSitePath("/static/js/gavo"+JSEXT),
178 type="text/javascript"),
179 originalChildren,
180 ]
181 if base.getConfig("web", "operatorCSS"):
182 res[
183 T.link(rel="stylesheet", type="text/css",
184 href=base.getConfig("web", "operatorCSS"))]
185 return res
186
188 """renders data as a url-escaped string.
189 """
190 if isinstance(data, unicode):
191 data = data.encode("utf-8")
192 return urllib.quote(data)
193
195 """looks up the text child in the DaCHS configuration and inserts
196 the value as a (unicode) string.
197
198 The config key is either [section]item or just item for something
199 in [general]. Behaviour for undefined config items is undefined.
200 """
201 configKey = ctx.tag.children[0].strip()
202 mat = re.match("\[([^]]*)\](.*)", configKey)
203 if mat:
204 content = base.getConfig(mat.group(1), mat.group(2))
205 else:
206 content = base.getConfig(configKey)
207 ctx.tag.clear()
208 return ctx.tag[content]
209
210
212 """A wrapper class for upload destinations that limits writes to
213 limit bytes and sends a 413 if the limit is reached.
214 """
215 message = (b"If you receive this error using sync, try again\r\n"
216 b"with async (the limits are more generous there). If you\r\n"
217 b"receive this message with async, see if the upload can be\r\n"
218 b"reduced by transmitting only columns absolutely necessary\r\n")
219
220 - def __init__(self, original, request, limit):
231
233 return getattr(self.original, attrName)
234
236 self.writtenSoFar += len(bytes)
237 if self.writtenSoFar>=self.limit:
238 self.request.setResponseCode(413, "Request Entity Too Large")
239 self.request.setHeader("content-length", str(len(self.message)))
240 self.request.setHeader("content-type", "text/plain")
241 self.request.write(self.message)
242 self.request.finishRequest(0)
243
244 else:
245 self.original.write(bytes)
246
247
248 -class Request(appserver.NevowRequest):
249 """a custom request class used in DaCHS' application server.
250
251 The main change is that we enforce a limit to the size of the payload.
252 This is especially crucial because nevow blocks while parsing the
253 header payload.
254 """
256 appserver.NevowRequest.gotLength(self, length)
257
258
259
260
261
262 isSync = True
263 try:
264 path = self.channel._path.split("?")[0]
265 isSync = path.endswith("/sync")
266 except Exception as msg:
267 base.ui.notifyError("In gotLength: %s"%msg)
268
269 if isSync:
270 maxUploadSize = base.getConfig("web", "maxSyncUploadSize")
271 else:
272 maxUploadSize = base.getConfig("web", "maxUploadSize")
273
274
275
276
277 if length and length>maxUploadSize:
278 maxUploadSize = 0
279
280 if self.content:
281 self.content = _UploadLimiter(self.content, self, maxUploadSize)
282
284
285
286
287
288 if not hasattr(self, "client"):
289 self.client = None
290
291 try:
292 self.channel.factory.log(self)
293 except Exception as exc:
294 base.ui.notifyError("Request with lost connection wasn't logged: %s"%
295 exc)
296 return appserver.NevowRequest.connectionLost(self, reason)
297
298
300 """A simple resource just producing bytes passed in during construction,
301 declared as a special content type.
302 """
306
312