#!/usr/bin/python3 # # Generate documentation for how this machine works by # parsing our bash scripts! import cgi, re import markdown from modgrammar import * def generate_documentation(): print("""
Here’s how you can build your own mail server from scratch. This document is generated automatically from our setup script.
" + cgi.escape(content) + "
\n" return "" class EditConf(Grammar): grammar = ( L('tools/editconf.py '), FILENAME, SPACE, OPTIONAL((LIST_OF( L("-w") | L("-s") | L("-c ';'"), sep=SPACE, ), SPACE)), REST_OF_LINE, OPTIONAL(SPACE), EOL ) def value(self): conffile = self[1] options = [""] mode = 1 for c in self[4].string: if mode == 1 and c in (" ", "\t") and options[-1] != "": # new word options.append("") elif mode < 0: # escaped character options[-1] += c mode = -mode elif c == "\\": # escape next character mode = -mode elif mode == 1 and c == '"': mode = 2 elif mode == 2 and c == '"': mode = 1 else: options[-1] += c if options[-1] == "": options.pop(-1) return "" + cgi.escape(self.string.strip()) + "
" + "\n".join(cgi.escape(s) for s in options) + "
" + cgi.escape(cmd) + "
replace
" + cgi.escape(self[3].string.replace(".*", ". . .")) + "with
" + cgi.escape(self[5].string.replace("\\n", "\n").replace("\\t", "\t")) + "\n" class AptGet(Grammar): grammar = (ZERO_OR_MORE(SPACE), L("apt_install "), REST_OF_LINE, EOL) def value(self): return shell_line("apt-get install -y " + re.sub(r"\s+", " ", self[2].string)) class UfwAllow(Grammar): grammar = (ZERO_OR_MORE(SPACE), L("ufw_allow "), REST_OF_LINE, EOL) def value(self): return shell_line("ufw allow " + self[2].string) class RestartService(Grammar): grammar = (ZERO_OR_MORE(SPACE), L("restart_service "), REST_OF_LINE, EOL) def value(self): return shell_line("service " + self[2].string + " restart") class OtherLine(Grammar): grammar = (REST_OF_LINE, EOL) def value(self): if self.string.strip() == "": return "" return "" + cgi.escape(wrap_lines(bash.strip())) + "
\n" class BashElement(Grammar): grammar = Comment | Source | CatEOF | SuppressedLine | HideOutput | EditConf | CaptureOutput | SedReplace | AptGet | UfwAllow | RestartService | OtherLine def value(self): return self[0].value() class BashScript(Grammar): grammar = (OPTIONAL(HashBang), REPEAT(BashElement)) def value(self): return [line.value() for line in self[1]] @staticmethod def parse(fn): if fn in ("setup/functions.sh", "/etc/mailinabox.conf"): return "" parser = BashScript.parser() string = open(fn).read() string = re.sub(r"\s*\\\n\s*", " ", string) string = re.sub(".* #NODOC\n", "", string) string = re.sub("\n\s*if .*|\n\s*fi|\n\s*else|\n\s*elif .*", "", string) string = re.sub("hide_output ", "", string) result = parser.parse_string(string) v = "\n" % ("https://github.com/mail-in-a-box/mailinabox/tree/master/" + fn, fn) v += "".join(result.value()) v = v.replace("\n" + cgi.escape(self.string.rstrip()) + "
", "")
v = re.sub("([\w\W]*?)
", lambda m : "" + strip_indent(m.group(1)) + "
", v)
v = re.sub(r"\$?PRIMARY_HOSTNAME", "box.yourdomain.com", v)
v = re.sub(r"\$?STORAGE_ROOT", "/path/to/user-data", v)
v = v.replace("`pwd`", "/path/to/mailinabox")
return v
def wrap_lines(text, cols=60):
ret = ""
words = re.split("(\s+)", text)
linelen = 0
for w in words:
if linelen + len(w) > cols-1:
ret += " \\\n"
ret += " "
linelen = 0
if linelen == 0 and w.strip() == "": continue
ret += w
linelen += len(w)
return ret
if __name__ == '__main__':
generate_documentation()