#!/usr/bin/env python3
# Copyright (C) 2016-2017 Florian Festi
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see
{_(box.__doc__) if box.__doc__ else ""}
{_("Create boxes and more with a laser cutter!")}
{_(''' Boxes.py is an Open Source box generator written in Python. It features both finished parametrized generators as well as a Python API for writing your own. It features finger and (flat) dovetail joints, flex cuts, holes and slots for screws, hinges, gears, pulleys and much more.''')}
%s
" % html.escape(s) for s in str(e).split("\n")) + """ """).encode("utf-8") ] def serveStatic(self, environ, start_response): filename = environ["PATH_INFO"][len("/static/"):] path = os.path.join(self.staticdir, filename) if (not re.match(r"[a-zA-Z0-9_/-]+\.[a-zA-Z0-9]+", filename) or not os.path.exists(path)): if re.match(r"samples/.*-thumb.jpg", filename): path = os.path.join(self.staticdir, "nothing.png") else: start_response("404 Not Found", [('Content-type', 'text/plain')]) return [b"Not found"] type_, encoding = mimetypes.guess_type(filename) if encoding is None: encoding = "utf-8" # Images do not have charset. Just bytes. Except text based svg. # Todo: fallback if type_ is None? if type_ is not None and "image" in type_ and type_ != "image/svg+xml": start_response("200 OK", [('Content-type', "%s" % type_)]) else: start_response("200 OK", [('Content-type', "%s; charset=%s" % (type_, encoding))]) f = open(path, 'rb') return environ['wsgi.file_wrapper'](f, 512*1024) def getURL(self, environ): url = environ['wsgi.url_scheme']+'://' if environ.get('HTTP_HOST'): url += environ['HTTP_HOST'] else: url += environ['SERVER_NAME'] if environ['wsgi.url_scheme'] == 'https': if environ['SERVER_PORT'] != '443': url += ':' + environ['SERVER_PORT'] else: if environ['SERVER_PORT'] != '80': url += ':' + environ['SERVER_PORT'] url += quote(self.url_prefix) url += quote(environ.get('SCRIPT_NAME', '')) url += quote(environ.get('PATH_INFO', '')) if environ.get('QUERY_STRING'): url += '?' + environ['QUERY_STRING'] return url def serve(self, environ, start_response): # serve favicon from static for generated SVGs if environ["PATH_INFO"] == "favicon.ico": environ["PATH_INFO"] = "/static/favicon.ico" if environ["PATH_INFO"].startswith("/static/"): return self.serveStatic(environ, start_response) status = '200 OK' headers = [('Content-type', 'text/html; charset=utf-8'), ('X-XSS-Protection', '1; mode=block'), ('X-Content-Type-Options', 'nosniff'), ('x-frame-options', 'SAMEORIGIN'), ('Referrer-Policy', 'no-referrer')] d = parse_qs(environ.get('QUERY_STRING', '')) name = environ["PATH_INFO"][1:] args = [unquote_plus(arg) for arg in environ.get('QUERY_STRING', '').split("&")] render = "0" for arg in args: if arg.startswith("render="): render = arg[len("render="):] lang = self.getLanguage(args, environ.get("HTTP_ACCEPT_LANGUAGE", "")) _ = lang.gettext box_cls = self.boxes.get(name, None) if not box_cls: start_response(status, headers) lang_name = lang.info().get('language', None) if lang_name not in self._cache: self._cache[lang_name] = list(self.menu(lang)) return self._cache[lang_name] if name == "TrayLayout2": box = box_cls(self, webargs=True) else: box = box_cls() if render == "0": defaults = { } for a in args: kv = a.split('=') if len(kv) == 2: k, v = kv defaults[k] = html.escape(v, True) start_response(status, headers) return self.args2html_cached(name, box, lang, "./" + name, defaults=defaults) else: args = ["--"+ arg for arg in args if not arg.startswith("render=")] try: box.parseArgs(args) except (ArgumentParserError) as e: start_response(status, headers) return self.errorMessage(name, e, _) if name == "TrayLayout": start_response(status, headers) box.fillDefault(box.x, box.y) layout2 = boxes.generators.traylayout.TrayLayout2(self, webargs=True) layout2.argparser.set_defaults(layout=str(box)) return self.args2html( name, layout2, lang, action="TrayLayout2") if name == "TrayLayout2": try: box.parse(box.layout.split("\n")) except Exception as e: start_response(status, headers) return self.errorMessage(name, e, _) try: fd, box.output = tempfile.mkstemp() box.metadata["url"] = self.getURL(environ) box.open() box.render() box.close() except Exception as e: if not isinstance(e, ValueError): print("Exception during rendering:") traceback.print_exc() start_response("500 Internal Server Error", headers) return self.errorMessage(name, e, _) http_headers = box.formats.http_headers.get( box.format, [('Content-type', 'application/unknown; charset=utf-8')])[:] # Prevent crawlers. http_headers.append(('X-Robots-Tag', 'noindex,nofollow')) if box.format != "svg" or render == "2": extension = box.format if extension == "svg_Ponoko": extension = "svg" http_headers.append(('Content-Disposition', 'attachment; filename="%s.%s"' % (box.__class__.__name__, extension))) start_response(status, http_headers) result = open(box.output, 'rb').readlines() os.close(fd) os.remove(box.output) return (l for l in result) if __name__=="__main__": parser = argparse.ArgumentParser() parser.add_argument("--host", default="localhost") parser.add_argument("--port", type=int, default=8000) parser.add_argument("--url_prefix", default="", help="URL path to Boxes.py instance") parser.add_argument("--static_url", default="static", help="URL of static content") args = parser.parse_args() boxserver = BServer(url_prefix=args.url_prefix, static_url=args.static_url) fc = FileChecker() fc.start() httpd = make_server(args.host, args.port, boxserver.serve) print(f"BoxesServer serving on {args.host}:{args.port}...") try: httpd.serve_forever() except KeyboardInterrupt: fc.stop() httpd.server_close() print("BoxesServer stops.") else: boxserver = BServer(url_prefix='/boxes.py', static_url="https://florianfesti.github.io/boxes/static") application = boxserver.serve