### -*-python-*- ### ### CGI commands ### ### (c) 2013 Mark Wooding ### ###----- Licensing notice --------------------------------------------------- ### ### This file is part of Chopwood: a password-changing service. ### ### Chopwood is free software; you can redistribute it and/or modify ### it under the terms of the GNU Affero General Public License as ### published by the Free Software Foundation; either version 3 of the ### License, or (at your option) any later version. ### ### Chopwood is distributed in the hope that it will be useful, ### but WITHOUT ANY WARRANTY; without even the implied warranty of ### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ### GNU Affero General Public License for more details. ### ### You should have received a copy of the GNU Affero General Public ### License along with Chopwood; if not, see ### . from __future__ import with_statement import errno as E import os as OS from auto import PACKAGE, VERSION import agpl as AGPL import cgi as CGI import cmdutil as CU import dbmaint as D import httpauth as HA import operation as OP import output as O; OUT = O.OUT; PRINT = O.PRINT import service as S import subcommand as SC import util as U ###-------------------------------------------------------------------------- ### Utilities. def operate(what, op, services, *args, **kw): accts = CU.resolve_accounts(CU.USER, services) o, ii, rq, ops = OP.operate(op, accts, *args, **kw) CGI.page('operate.fhtml', header = dict(pragma = 'no-cache', cache_control = 'no-cache'), title = 'Chopwood: %s' % what, what = what, outcome = o, info = ii, results = ops) ###-------------------------------------------------------------------------- ### Commands. @CGI.subcommand('list', ['cgi-query'], 'List available accounts') def cmd_list_cgi(): CGI.page('list.fhtml', title = 'Chopwood: accounts list', accts = CU.list_accounts(CU.USER), nonce = HA.NONCE) @CGI.subcommand( 'set', ['cgi'], 'Set password for a collection of services.', params = [SC.Arg('first'), SC.Arg('second')], rparam = SC.Arg('services')) def cmd_set_cgi(first, second, services = []): if first != second: raise U.ExpectedError, (400, "Passwords don't match") operate('set passwords', 'set', services, first) @CGI.subcommand( 'reset', ['cgi'], 'Reset passwords for a collection of services.', rparam = SC.Arg('services')) def cmd_reset_cgi(services = []): operate('reset passwords', 'reset', services) @CGI.subcommand( 'clear', ['cgi'], 'Clear passwords for a collection of services.', rparam = SC.Arg('services')) def cmd_clear_cgi(services = []): operate('clear passwords', 'clear', services) @CGI.subcommand( 'fail', ['cgi-noauth'], 'Raise an exception, to test the error reporting machinery.', opts = [SC.Opt('partial', '-p', '--partial', 'Raise exception after producing partial output.')]) def cmd_fail_cgi(partial = False): if partial: OUT.header(content_type = 'text/html') PRINT("""\ Chopwood: filler text

Failure expected soon

This is some normal output which will be rudely interrupted.""") raise Exception, 'You asked for this.' ###-------------------------------------------------------------------------- ### Static content. ## A map of file names to content objects. See below. CONTENT = {} class PlainOutput (O.FileOutput): def header(me, **kw): pass class StaticContent (object): def __init__(me, type): me._type = type def emit(me): OUT.header(content_type = me._type) me._emit() def _write(me, dest): with open(dest, 'w') as f: with OUT.redirect_to(PlainOutput(f)): me.emit() def write(me, dest): new = dest + '.new' try: OS.unlink(new) except OSError, e: if e.errno != E.ENOENT: raise me._write(new) OS.rename(new, dest) class TemplateContent (StaticContent): def __init__(me, template, *args, **kw): super(TemplateContent, me).__init__(*args, **kw) me._template = template def _emit(me): CGI.format_tmpl(CGI.TMPL[me._template]) class HTMLContent (StaticContent): def __init__(me, title, template, type = 'text/html', *args, **kw): super(HTMLContent, me).__init__(type = type, *args, **kw) me._template = template me._title = title def emit(me): CGI.page(me._template, title = me._title) CONTENT.update({ 'chpwd.css': TemplateContent(template = 'chpwd.css', type = 'text/css'), 'chpwd.js': TemplateContent(template = 'chpwd.js', type = 'text/javascript'), 'about.html': HTMLContent('Chopwood: about this program', template = 'about.fhtml'), 'cookies.html': HTMLContent('Chopwood: use of cookies', template = 'cookies.fhtml') }) @CGI.subcommand( 'static', ['cgi-noauth'], 'Output a static file.', rparam = SC.Arg('path')) def cmd_static_cgi(path): name = '/'.join(path) try: content = CONTENT[name] except KeyError: raise U.ExpectedError, (404, "Unknown file `%s'" % name) content.emit() @SC.subcommand( 'static', ['admin'], 'Write the static files to DIR.', params = [SC.Arg('dir')]) def cmd_static_admin(dir): try: OS.makedirs(dir, 0777) except OSError, e: if e.errno != E.EEXIST: raise for f, c in CONTENT.iteritems(): c.write(OS.path.join(dir, f)) TARBALL = '%s-%s.tar.gz' % (PACKAGE, VERSION) @CGI.subcommand(TARBALL, ['cgi-noauth'], """\ Download source code (in `.tar.gz' format).""") def cmd_source_cgi(): OUT.header(content_type = 'application/octet-stream') AGPL.source(OUT) @CGI.subcommand('source', ['cgi-noauth'], """\ Redirect to the source code tarball (so that it's correctly named).""") def cmd_sourceredirect_cgi(): CGI.redirect(CGI.action(TARBALL)) ###----- That's all, folks --------------------------------------------------