def handle_puzzlelist(self): puzzles = { "__devel__": [[0, ""]], } for p in self.server.args["puzzles_dir"].glob("*"): if not p.is_dir() or p.match(".*"): continue catName = p.parts[-1] cat = moth.Category(str(p), self.seed) puzzles[catName] = [[i, str(i)] for i in cat.pointvals()] puzzles[catName].append([0, ""]) if len(puzzles) <= 1: logging.warning("No directories found matching {}/*".format( self.server.args["puzzles_dir"])) self.send_response(200) self.send_header("Content-Type", "application/json") self.end_headers() self.wfile.write(json.dumps(puzzles).encode("utf-8"))
def package(categoryname, categorydir, seed): zfraw = io.BytesIO() zf = zipfile.ZipFile(zfraw, 'x') zf.writestr("category_seed.txt", str(seed)) cat = moth.Category(categorydir, seed) mapping = {} answers = {} summary = {} for puzzle in cat: logging.info("Processing point value {}".format(puzzle.points)) hashmap = hashlib.sha1(str(seed).encode('utf-8')) hashmap.update(str(puzzle.points).encode('utf-8')) puzzlehash = hashmap.hexdigest() mapping[puzzle.points] = puzzlehash answers[puzzle.points] = puzzle.answers summary[puzzle.points] = puzzle.summary puzzledir = os.path.join("content", puzzlehash) for fn, f in puzzle.files.items(): payload = f.stream.read() zf.writestr(os.path.join(puzzledir, fn), payload) obj = puzzle.package() zf.writestr(os.path.join(puzzledir, 'puzzle.json'), json.dumps(obj)) write_kv_pairs(zf, 'map.txt', mapping) write_kv_pairs(zf, 'answers.txt', answers) write_kv_pairs(zf, 'summaries.txt', summary) # clean up zf.close() zfraw.seek(0) return zfraw
def serve_puzzles(self): body = io.StringIO() path = self.path.rstrip('/') parts = path.split("/") title = None fpath = None points = None cat = None puzzle = None try: fpath = os.path.join(self.puzzles_dir, parts[2]) points = int(parts[3]) except: pass if fpath: cat = moth.Category(fpath, seed) if points: puzzle = cat.puzzle(points) if not cat: title = "Puzzle Categories" body.write("<ul>") for i in sorted(glob.glob(os.path.join(self.puzzles_dir, "*", ""))): bn = os.path.basename(i.strip('/\\')) body.write( '<li><a href="/puzzles/{}">puzzles/{}/</a></li>'.format( bn, bn)) body.write("</ul>") elif not puzzle: # List all point values in a category title = "Puzzles in category `{}`".format(parts[2]) body.write("<ul>") for points in cat.pointvals(): body.write( '<li><a href="/puzzles/{cat}/{points}/">puzzles/{cat}/{points}/</a></li>' .format(cat=parts[2], points=points)) body.write("</ul>") elif len(parts) == 4: # Serve up a puzzle title = "{} puzzle {}".format(parts[2], parts[3]) body.write("<h2>Body</h2>") body.write(puzzle.html_body()) body.write("<h2>Files</h2>") body.write("<ul>") for name, puzzlefile in sorted(puzzle.files.items()): if puzzlefile.visible: visibility = 'listed as a generated file' else: visibility = 'unlisted' body.write( '<li><a href="/puzzles/{cat}/{points}/{filename}">{filename}</a> ({visibility})</li>' .format(cat=parts[2], points=puzzle.points, filename=name, visibility=visibility)) body.write("</ul>") body.write("<h2>Answers</h2>") body.write("<ul>") assert puzzle.answers, 'No answers defined' for a in puzzle.answers: body.write("<li><code>{}</code></li>".format(html.escape(a))) body.write("</ul>") body.write("<h2>Authors</h2><p>{}</p>".format(', '.join( puzzle.get_authors()))) body.write("<h2>Summary</h2><p>{}</p>".format(puzzle.summary)) if puzzle.logs: body.write("<h2>Debug Log</h2>") body.write('<ul class="log">') for l in puzzle.logs: body.write("<li>{}</li>".format(html.escape(l))) body.write("</ul>") elif len(parts) == 5: # Serve up a puzzle file try: pfile = puzzle.files[parts[4]] except KeyError: self.send_error( HTTPStatus.NOT_FOUND, "File not found. Did you add it to the Files: header or puzzle.add_stream?" ) return ctype = self.guess_type(pfile.name) self.send_response(HTTPStatus.OK) self.send_header("Content-Type", ctype) self.end_headers() shutil.copyfileobj(pfile.stream, self.wfile) return payload = page(title, body.getvalue()).encode('utf-8') self.send_response(HTTPStatus.OK) self.send_header("Content-Type", "text/html; charset=utf-8") self.send_header("Content-Length", len(payload)) self.end_headers() self.wfile.write(payload)
def build_category(categorydir, outdir): zipfileraw = tempfile.NamedTemporaryFile(delete=False) zf = zipfile.ZipFile(zipfileraw, 'x') category_seed = binascii.b2a_hex(os.urandom(20)) puzzles_dict = {} secrets = {} categoryname = os.path.basename(categorydir.strip(os.sep)) seedfn = os.path.join("category_seed.txt") zipfilename = os.path.join(outdir, "%s.zip" % categoryname) logging.info("Building {} from {}".format(zipfilename, categorydir)) if os.path.exists(zipfilename): # open and gather some state existing = zipfile.ZipFile(zipfilename, 'r') try: category_seed = existing.open(seedfn).read().strip() except: pass existing.close() logging.debug("Using PRNG seed {}".format(category_seed)) zf.writestr(seedfn, category_seed) cat = moth.Category(categorydir, category_seed) mapping = {} answers = {} summary = {} for puzzle in cat: logging.info("Processing point value {}".format(puzzle.points)) hashmap = hashlib.sha1(category_seed) hashmap.update(str(puzzle.points).encode('utf-8')) puzzlehash = hashmap.hexdigest() mapping[puzzle.points] = puzzlehash answers[puzzle.points] = puzzle.answers summary[puzzle.points] = puzzle.summary puzzledir = os.path.join('content', puzzlehash) files = [] for fn, f in puzzle.files.items(): if f.visible: files.append(fn) payload = f.stream.read() zf.writestr(os.path.join(puzzledir, fn), payload) puzzledict = { 'authors': puzzle.authors, 'hashes': puzzle.hashes(), 'files': files, 'body': puzzle.html_body(), } puzzlejson = json.dumps(puzzledict) zf.writestr(os.path.join(puzzledir, 'puzzle.json'), puzzlejson) generate_html(zf, puzzle, puzzledir, categoryname, puzzle.points, puzzle.get_authors(), files) write_kv_pairs(zf, 'map.txt', mapping) write_kv_pairs(zf, 'answers.txt', answers) write_kv_pairs(zf, 'summaries.txt', summary) # clean up zf.close() shutil.move(zipfileraw.name, zipfilename)
def serve_puzzles(self): body = [] path = self.path.rstrip('/') parts = path.split("/") #raise ValueError(parts) if len(parts) < 3: # List all categories body.append("# Puzzle Categories") for i in glob.glob(os.path.join("puzzles", "*", "")): body.append("* [{}](/{})".format(i, i)) self.serve_md('\n'.join(body)) return fpath = os.path.join("puzzles", parts[2]) cat = moth.Category(fpath, seed) if len(parts) == 3: # List all point values in a category body.append("# Puzzles in category `{}`".format(parts[2])) for points in cat.pointvals: body.append( "* [puzzles/{cat}/{points}](/puzzles/{cat}/{points}/)". format(cat=parts[2], points=points)) self.serve_md('\n'.join(body)) return pzl = cat.puzzle(int(parts[3])) if len(parts) == 4: body.append("# {} puzzle {}".format(parts[2], parts[3])) body.append("* Author: `{}`".format(pzl['author'])) body.append("* Summary: `{}`".format(pzl['summary'])) body.append('') body.append("## Body") body.append(pzl.body) body.append("## Answers") for a in pzl['answer']: body.append("* `{}`".format(a)) body.append("") body.append("## Files") for pzl_file in pzl['files']: body.append( "* [puzzles/{cat}/{points}/{filename}]({filename})".format( cat=parts[2], points=pzl.points, filename=pzl_file)) if len(pzl.logs) > 0: body.extend(["", "## Logs"]) body.append("* [Full Log File](_logs)".format( cat=parts[2], points=pzl.points)) body.extend(["", "### Logs Head"]) for log in pzl.logs[:10]: body.append("* `{}`".format(log)) body.extend(["", "### Logs Tail"]) for log in pzl.logs[-10:]: body.append("* `{}`".format(log)) self.serve_md('\n'.join(body)) return elif len(parts) == 5: if parts[4] == '_logs': self.serve_puzzle_logs(pzl.logs) else: try: self.serve_puzzle_file(pzl['files'][parts[4]]) except KeyError: self.send_error(HTTPStatus.NOT_FOUND, "File not found") return else: body.append("# Not Implemented Yet") self.serve_md('\n'.join(body))