chiark / gitweb /
backend.py: Use configured delimiter for joining fields.
[chopwood] / cmd-cgi.py
CommitLineData
a2916c06
MW
1### -*-python-*-
2###
3### CGI commands
4###
5### (c) 2013 Mark Wooding
6###
7
8###----- Licensing notice ---------------------------------------------------
9###
10### This file is part of Chopwood: a password-changing service.
11###
12### Chopwood is free software; you can redistribute it and/or modify
13### it under the terms of the GNU Affero General Public License as
14### published by the Free Software Foundation; either version 3 of the
15### License, or (at your option) any later version.
16###
17### Chopwood is distributed in the hope that it will be useful,
18### but WITHOUT ANY WARRANTY; without even the implied warranty of
19### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20### GNU Affero General Public License for more details.
21###
22### You should have received a copy of the GNU Affero General Public
23### License along with Chopwood; if not, see
24### <http://www.gnu.org/licenses/>.
25
26from __future__ import with_statement
27
28import errno as E
29import os as OS
30
31from auto import PACKAGE, VERSION
32import agpl as AGPL
33import cgi as CGI
34import cmdutil as CU
35import dbmaint as D
36import httpauth as HA
37import operation as OP
38import output as O; OUT = O.OUT; PRINT = O.PRINT
39import service as S
40import subcommand as SC
41import util as U
42
43###--------------------------------------------------------------------------
44### Utilities.
45
46def operate(what, op, services, *args, **kw):
47 accts = CU.resolve_accounts(CU.USER, services)
48 o, ii, rq, ops = OP.operate(op, accts, *args, **kw)
49 CGI.page('operate.fhtml',
50 header = dict(pragma = 'no-cache', cache_control = 'no-cache'),
51 title = 'Chopwood: %s' % what,
52 what = what,
53 outcome = o, info = ii, results = ops)
54
55###--------------------------------------------------------------------------
56### Commands.
57
58@CGI.subcommand('list', ['cgi-query'], 'List available accounts')
59def cmd_list_cgi():
60 CGI.page('list.fhtml',
6a749ccf 61 header = dict(pragma = 'no-cache', cache_control = 'no-cache'),
a2916c06
MW
62 title = 'Chopwood: accounts list',
63 accts = CU.list_accounts(CU.USER),
64 nonce = HA.NONCE)
65
66@CGI.subcommand(
67 'set', ['cgi'], 'Set password for a collection of services.',
9e574017 68 methods = ['POST'],
a2916c06
MW
69 params = [SC.Arg('first'), SC.Arg('second')],
70 rparam = SC.Arg('services'))
71def cmd_set_cgi(first, second, services = []):
72 if first != second: raise U.ExpectedError, (400, "Passwords don't match")
73 operate('set passwords', 'set', services, first)
74
75@CGI.subcommand(
76 'reset', ['cgi'],
77 'Reset passwords for a collection of services.',
9e574017 78 methods = ['POST'],
a2916c06
MW
79 rparam = SC.Arg('services'))
80def cmd_reset_cgi(services = []):
81 operate('reset passwords', 'reset', services)
82
83@CGI.subcommand(
84 'clear', ['cgi'],
85 'Clear passwords for a collection of services.',
9e574017 86 methods = ['POST'],
a2916c06
MW
87 rparam = SC.Arg('services'))
88def cmd_clear_cgi(services = []):
89 operate('clear passwords', 'clear', services)
90
91@CGI.subcommand(
170f1769 92 'logout', ['cgi'],
9e574017
MW
93 'Log out of the web interface.',
94 methods = ['POST'])
170f1769
MW
95def cmd_logout_cgi():
96 CGI.redirect(CGI.action('login', why = 'LOGOUT'),
97 set_cookie = HA.bake_cookie('logged-out'))
98
99@CGI.subcommand(
a2916c06
MW
100 'fail', ['cgi-noauth'],
101 'Raise an exception, to test the error reporting machinery.',
102 opts = [SC.Opt('partial', '-p', '--partial',
103 'Raise exception after producing partial output.')])
104def cmd_fail_cgi(partial = False):
105 if partial:
106 OUT.header(content_type = 'text/html')
107 PRINT("""\
108<html>
109<head><title>Chopwood: filler text</title></head>
110<body>
111<h1>Failure expected soon
112<p>This is some normal output which will be rudely interrupted.""")
113 raise Exception, 'You asked for this.'
114
5b7c6334
MW
115@CGI.subcommand(
116 'warn', ['cgi-noauth'],
117 'Raise an exception, to test the error reporting machinery.')
118def cmd_warn_cgi():
119 OUT.header(content_type = 'text/html')
120 OUT.warn("Here's a very important warning.")
121 CGI.format_tmpl(CGI.TMPL['wrapper.fhtml'],
122 title = "Warning test", warnings = OUT.warnings,
123 payload = "<h1>Chopwood: warning test</h1>\n"
124 "<p>There ought to be a warning below.\n")
125
a2916c06
MW
126###--------------------------------------------------------------------------
127### Static content.
128
129## A map of file names to content objects. See below.
130CONTENT = {}
131
132class PlainOutput (O.FileOutput):
133 def header(me, **kw):
134 pass
135
136class StaticContent (object):
137 def __init__(me, type):
138 me._type = type
139 def emit(me):
140 OUT.header(content_type = me._type)
141 me._emit()
142 def _write(me, dest):
143 with open(dest, 'w') as f:
144 with OUT.redirect_to(PlainOutput(f)):
145 me.emit()
146 def write(me, dest):
147 new = dest + '.new'
148 try: OS.unlink(new)
149 except OSError, e:
150 if e.errno != E.ENOENT: raise
151 me._write(new)
152 OS.rename(new, dest)
153
154class TemplateContent (StaticContent):
155 def __init__(me, template, *args, **kw):
156 super(TemplateContent, me).__init__(*args, **kw)
157 me._template = template
158 def _emit(me):
159 CGI.format_tmpl(CGI.TMPL[me._template])
160
161class HTMLContent (StaticContent):
162 def __init__(me, title, template, type = 'text/html', *args, **kw):
163 super(HTMLContent, me).__init__(type = type, *args, **kw)
164 me._template = template
165 me._title = title
166 def emit(me):
167 CGI.page(me._template, title = me._title)
168
169CONTENT.update({
170 'chpwd.css': TemplateContent(template = 'chpwd.css',
171 type = 'text/css'),
172 'chpwd.js': TemplateContent(template = 'chpwd.js',
173 type = 'text/javascript'),
174 'about.html': HTMLContent('Chopwood: about this program',
175 template = 'about.fhtml'),
176 'cookies.html': HTMLContent('Chopwood: use of cookies',
177 template = 'cookies.fhtml')
178})
179
180@CGI.subcommand(
181 'static', ['cgi-noauth'], 'Output a static file.',
182 rparam = SC.Arg('path'))
183def cmd_static_cgi(path):
184 name = '/'.join(path)
185 try: content = CONTENT[name]
186 except KeyError: raise U.ExpectedError, (404, "Unknown file `%s'" % name)
187 content.emit()
188
189@SC.subcommand(
190 'static', ['admin'], 'Write the static files to DIR.',
191 params = [SC.Arg('dir')])
192def cmd_static_admin(dir):
193 try: OS.makedirs(dir, 0777)
194 except OSError, e:
195 if e.errno != E.EEXIST: raise
196 for f, c in CONTENT.iteritems():
197 c.write(OS.path.join(dir, f))
198
199TARBALL = '%s-%s.tar.gz' % (PACKAGE, VERSION)
200@CGI.subcommand(TARBALL, ['cgi-noauth'], """\
201Download source code (in `.tar.gz' format).""")
202def cmd_source_cgi():
203 OUT.header(content_type = 'application/octet-stream')
204 AGPL.source(OUT)
205
206@CGI.subcommand('source', ['cgi-noauth'], """\
d674bfda 207Redirect to the source code tarball (so that it's correctly named).""")
a2916c06
MW
208def cmd_sourceredirect_cgi():
209 CGI.redirect(CGI.action(TARBALL))
210
211###----- That's all, folks --------------------------------------------------