chiark / gitweb /
format.py: Allow general format controls more widely.
[chopwood] / cgi.py
diff --git a/cgi.py b/cgi.py
index 02797cec108ea59458e51fca804d3b96a1d363bf..26295e038ccc04dbfeb53e4f715d872706cd9501 100644 (file)
--- a/cgi.py
+++ b/cgi.py
@@ -59,7 +59,7 @@ CONF.DEFAULTS.update(
 ## Some handy regular expressions.
 R_URLESC = RX.compile('%([0-9a-fA-F]{2})')
 R_URLBAD = RX.compile('[^-\\w,.!]')
-R_HTMLBAD = RX.compile('[&<>]')
+R_HTMLBAD = RX.compile('[&<>\'"]')
 
 def urldecode(s):
   """Decode a single form-url-encoded string S."""
@@ -77,17 +77,18 @@ def htmlescape(s):
 
 ## Some standard character sequences, and HTML entity names for prettier
 ## versions.
-_quotify = U.StringSubst({
+html_quotify = U.StringSubst({
+  "<": '&lt;',
+  ">": '&gt;',
+  "&": '&amp;',
   "`": '&lsquo;',
   "'": '&rsquo;',
+  '"': '&quot;',
   "``": '&ldquo;',
   "''": '&rdquo;',
   "--": '&ndash;',
   "---": '&mdash;'
 })
-def html_quotify(s):
-  """Return a pretty HTML version of S."""
-  return _quotify(htmlescape(s))
 
 ###--------------------------------------------------------------------------
 ### Output machinery.
@@ -145,7 +146,7 @@ def cookie(name, value, **kw):
                                  T.gmtime(U.NOW + maxage))
   return '; '.join(['%s=%s' % (urlencode(name), urlencode(value))] +
                    [v is not True and '%s=%s' % (k, v) or k
-                    for k, v in attr.iteritems()])
+                    for k, v in attr.iteritems() if v])
 
 def action(*v, **kw):
   """
@@ -166,47 +167,6 @@ def static(name):
   """Build a URL for the static file NAME."""
   return htmlescape(CFG.STATIC + '/' + name)
 
-@CTX.contextmanager
-def html(title, **kw):
-  """
-  Context manager for HTML output.
-
-  Keyword arguments are output as HTTP headers (if no header has been written
-  yet).  A `<head>' element is written, and a `<body>' opened, before the
-  context body is executed; the elements are closed off properly at the end.
-  """
-
-  kw = dict(kw, content_type = 'text/html')
-  OUT.header(**kw)
-
-  ## Write the HTML header.
-  PRINT("""\
-<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01//EN'
-          'http://www.w3c.org/TR/html4/strict.dtd'>
-<html>
-<head>
-  <title>%(title)s</title>
-  <link rel=stylesheet type='text/css' media=screen href='%(style)s'>
-  <meta http-equiv='Content-Script-Type' content='text/javascript'>
-  <script type='text/javascript' src='%(script)s'></script>
-</head>""" % dict(title = html_quotify(title),
-                 style = static('chpwd.css'),
-                 script = static('chpwd.js')))
-
-  ## Write the body.
-  PRINT('<body>')
-  yield None
-  PRINT('''\
-
-<div class=credits>
-  <a href="%(about)s">Chopwood</a>, version %(version)s:
-  copyright &copy; 2012 Mark Wooding
-</div>
-
-</body>
-</html>''' % dict(about = static('about.html'),
-                  version = VERSION))
-
 def redirect(where, **kw):
   """
   Write a complete redirection to some other URL.
@@ -237,7 +197,8 @@ def set_template_keywords():
     package = PACKAGE,
     version = VERSION,
     script = CFG.SCRIPT_NAME,
-    static = CFG.STATIC)
+    static = CFG.STATIC,
+    allowop = CFG.ALLOWOP)
 
 class TemplateFinder (object):
   """
@@ -270,7 +231,7 @@ class FormatHTML (F.SimpleFormatOperation):
   """
   ~H: escape output suitable for inclusion in HTML.
 
-  With `:', instead apply form-urlencoding.
+  With `:', additionally apply quotification.
   """
   def _convert(me, arg):
     if me.colonp: return html_quotify(arg)
@@ -305,7 +266,7 @@ def cgi_errors(hook = None):
     if hook: hook()
     if isinstance(e, U.ExpectedError) and not OUT.headerp:
       page('error.fhtml',
-           headers = dict(status = e.code),
+           header = dict(status = e.code),
            title = 'Chopwood: error', error = e)
     else:
       exty, exval, extb = SYS.exc_info()
@@ -319,7 +280,7 @@ def cgi_errors(hook = None):
           format_tmpl(TMPL['exception.fhtml'], toplevel = False)
         else:
           page('exception.fhtml',
-               headers = dict(status = 500),
+               header = dict(status = 500),
                title = 'Chopwood: internal error',
                toplevel = True)
 
@@ -332,6 +293,7 @@ SPECIAL = {}
 PARAM = []
 PARAMDICT = {}
 PATH = []
+SSLP = False
 
 ## Regular expressions for splitting apart query and cookie strings.
 R_QSPLIT = RX.compile('[;&]')
@@ -387,8 +349,13 @@ def cgiparse():
   `PATH'
         The trailing `PATH_INFO' path, split at `/' markers, with any
         trailing empty component removed.
+
+  `SSLP'
+        True if the client connection is carried over SSL or TLS.
   """
 
+  global SSLP
+
   def getenv(var):
     try: return ENV[var]
     except KeyError: raise U.ExpectedError, (500, "No `%s' supplied" % var)
@@ -443,6 +410,10 @@ def cgiparse():
     if pp and not pp[-1]: pp.pop()
     PATH[:] = pp
 
+  ## Check the crypto for the connection.
+  if ENV.get('SSL_PROTOCOL'):
+    SSLP = True
+
 ###--------------------------------------------------------------------------
 ### CGI subcommands.