diff --git a/Dockerfile.moth-devel b/Dockerfile.moth-devel index 6e8466a..a667a8e 100644 --- a/Dockerfile.moth-devel +++ b/Dockerfile.moth-devel @@ -9,7 +9,8 @@ RUN apk --no-cache add \ && \ pip3 install \ scapy==2.4.2 \ - pillow==5.4.1 + pillow==5.4.1 \ + PyYAML==5.1.1 COPY devel /app/ diff --git a/devel/moth.py b/devel/moth.py index 6a52715..76ed787 100644 --- a/devel/moth.py +++ b/devel/moth.py @@ -13,6 +13,7 @@ import random import string import tempfile import shlex +import yaml messageChars = b'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' @@ -126,59 +127,101 @@ class Puzzle: def read_stream(self, stream): header = True + line = "" + if stream.read(3) == "---": + header = "yaml" + else: + header = "moth" + + stream.seek(0) + + if header == "yaml": + self.read_yaml_header(stream) + elif header == "moth": + self.read_moth_header(stream) + for line in stream: - if header: - line = line.strip() - if not line: - header = False - continue - key, val = line.split(':', 1) - key = key.lower() - val = val.strip() - if key == 'author': - self.authors.append(val) - elif key == 'summary': - self.summary = val - elif key == 'answer': - self.answers.append(val) - elif key == 'pattern': - self.pattern = val - elif key == 'hint': - self.hint = val - elif key == 'name': - pass - elif key == 'file': - parts = shlex.split(val) - name = parts[0] - hidden = False - stream = open(name, 'rb') - try: - name = parts[1] - hidden = (parts[2].lower() == "hidden") - except IndexError: - pass - self.files[name] = PuzzleFile(stream, name, not hidden) - elif key == 'script': - stream = open(val, 'rb') - # Make sure this shows up in the header block of the HTML output. - self.files[val] = PuzzleFile(stream, val, visible=False) - self.scripts.append(val) - elif key == "objective": - self.objective = val - elif key == "success": - self.success = val - elif key == "success.acceptable": - self.success.acceptable = val - elif key == "success.mastery": - self.success.mastery = val - elif key == "solution": - self.solution = val - elif key == "ksa": - self.ksas.append(val) - else: - raise ValueError("Unrecognized header field: {}".format(key)) + self.body.write(line) + + def read_yaml_header(self, stream): + contents = "" + header = False + for line in stream: + if line.strip() == "---" and header: # Handle last line + break + elif line.strip() == "---": # Handle first line + header = True + continue else: - self.body.write(line) + contents += line + + config = yaml.safe_load(contents) + for key, value in config.items(): + key = key.lower() + self.handle_header_key(key, value) + + def read_moth_header(self, stream): + for line in stream: + line = line.strip() + if not line: + break + + key, val = line.split(':', 1) + key = key.lower() + val = val.strip() + self.handle_header_key(key, val) + + def handle_header_key(self, key, val): + if key == 'author': + self.authors.append(val) + elif key == 'summary': + self.summary = val + elif key == 'answer': + if not isinstance(val, str): + raise ValueError("Answers must be strings, got %s, instead" % (type(val),)) + self.answers.append(val) + elif key == "answers": + for answer in val: + if not isinstance(answer, str): + raise ValueError("Answers must be strings, got %s, instead" % (type(answer),)) + self.answers.append(answer) + elif key == 'pattern': + self.pattern = val + elif key == 'hint': + self.hint = val + elif key == 'name': + pass + elif key == 'file': + parts = shlex.split(val) + name = parts[0] + hidden = False + stream = open(name, 'rb') + try: + name = parts[1] + hidden = (parts[2].lower() == "hidden") + except IndexError: + pass + self.files[name] = PuzzleFile(stream, name, not hidden) + elif key == 'script': + stream = open(val, 'rb') + # Make sure this shows up in the header block of the HTML output. + self.files[val] = PuzzleFile(stream, val, visible=False) + self.scripts.append(val) + elif key == "objective": + self.objective = val + elif key == "success": + self.success = val + elif key == "success.acceptable": + self.success.acceptable = val + elif key == "success.mastery": + self.success.mastery = val + elif key == "solution": + self.solution = val + elif key == "ksa": + self.ksas.append(val) + else: + raise ValueError("Unrecognized header field: {}".format(key)) + def read_directory(self, path): try: