mirror of
https://github.com/freedombox/FreedomBox.git
synced 2026-01-21 07:55:00 +00:00
This issue was easy to solve once I started using the Live HTTP Headers Firefox extension to debug it: https://addons.mozilla.org/en-US/firefox/addon/live-http-headers/ With this particular InternalRedirect, CherryPy inexplicably responds with a 301 ("Moved Permanently") unless we manually tell it to use a 307 ("Moved Temporarily") response code. I don't know why CherryPy does this, and it may be indicative of a deeper problem that I don't currently have time to debug, as the request-response process for redirecting from http://(server)/plinth to https://(server)/plinth/firstboot is ridiculous and contains about twice as many requests as I would've expected.
187 lines
6.7 KiB
Python
Executable File
187 lines
6.7 KiB
Python
Executable File
#!/usr/bin/env python
|
|
|
|
import os, stat, sys, argparse
|
|
from gettext import gettext as _
|
|
import cfg
|
|
if not os.path.join(cfg.file_root, "vendor") in sys.path:
|
|
sys.path.append(os.path.join(cfg.file_root, "vendor"))
|
|
|
|
import cherrypy
|
|
from cherrypy import _cpserver
|
|
from cherrypy.process.plugins import Daemonizer
|
|
Daemonizer(cherrypy.engine).subscribe()
|
|
|
|
import plugin_mount
|
|
import util as u
|
|
|
|
from logger import Logger
|
|
#from modules.auth import AuthController, require, member_of, name_is
|
|
|
|
from withsqlite.withsqlite import sqlite_db
|
|
import socket
|
|
|
|
__version__ = "0.2.14"
|
|
__author__ = "James Vasile"
|
|
__copyright__ = "Copyright 2011-2013, James Vasile"
|
|
__license__ = "GPLv3 or later"
|
|
__maintainer__ = "James Vasile"
|
|
__email__ = "james@jamesvasile.com"
|
|
__status__ = "Development"
|
|
|
|
import urlparse
|
|
|
|
def error_page(status, dynamic_msg, stock_msg):
|
|
return u.page_template(template="err", title=status, main="<p>%s</p>%s" % (dynamic_msg, stock_msg))
|
|
|
|
def error_page_404(status, message, traceback, version):
|
|
return error_page(status, message, """<p>If you believe this
|
|
missing page should exist, please file a bug with either the Plinth
|
|
project (<a href="https://github.com/NickDaly/Plinth/issues">it has
|
|
an issue tracker</a>) or the people responsible for the module you
|
|
are trying to access.</p>
|
|
|
|
<p>Sorry for the mistake.</p>
|
|
""")
|
|
|
|
def error_page_500(status, message, traceback, version):
|
|
cfg.log.error("500 Internal Server Error. Trackback is above.")
|
|
more="""<p>This is an internal error and not something you caused
|
|
or can fix. Please report the error on the <a
|
|
href="https://github.com/jvasile/Plinth/issues">bug tracker</a> so
|
|
we can fix it.</p>"""
|
|
return error_page(status, message, "<p>%s</p><pre>%s</pre>" % (more, "\n".join(traceback.split("\n"))))
|
|
|
|
class Root(plugin_mount.PagePlugin):
|
|
@cherrypy.expose
|
|
def index(self):
|
|
## TODO: firstboot hijacking root should probably be in the firstboot module with a hook in plinth.py
|
|
with sqlite_db(cfg.store_file, table="firstboot") as db:
|
|
if not 'state' in db:
|
|
# if we created a new user db, make sure it can't be read by everyone
|
|
userdb_fname = '{}.sqlite3'.format(cfg.user_db)
|
|
os.chmod(userdb_fname, stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP)
|
|
# cherrypy.InternalRedirect throws a 301, causing the
|
|
# browser to cache the redirect, preventing the user from
|
|
# navigating to /plinth until the browser is restarted.
|
|
raise cherrypy.HTTPRedirect('firstboot', 307)
|
|
elif db['state'] < 5:
|
|
cfg.log("First Boot state = %d" % db['state'])
|
|
raise cherrypy.InternalRedirect('firstboot/state%d' % db['state'])
|
|
if cherrypy.session.get(cfg.session_key, None):
|
|
raise cherrypy.InternalRedirect('router')
|
|
else:
|
|
raise cherrypy.InternalRedirect('help/about')
|
|
|
|
def load_modules():
|
|
"""Import all the symlinked .py files in the modules directory and
|
|
all the .py files in directories linked in the modules directory
|
|
(but don't dive deeper than that). Also, ignore the installed
|
|
directory."""
|
|
for name in os.listdir("modules"):
|
|
if name.endswith(".py") and not name.startswith('.'):
|
|
cfg.log.info("importing modules/%s" % name)
|
|
try:
|
|
__import__("modules.%s" % (name[:-3]))
|
|
except ImportError, e:
|
|
cfg.log.error(_("Couldn't import modules/%s: %s") % (name, e))
|
|
else:
|
|
cfg.log("skipping %s" % name)
|
|
|
|
def parse_arguments():
|
|
parser = argparse.ArgumentParser(description='Plinth web interface for the FreedomBox.')
|
|
parser.add_argument('--pidfile',
|
|
help='specify a file in which the server may write its pid')
|
|
# FIXME make this work with basehref for static files.
|
|
parser.add_argument('--server_dir',
|
|
help='specify where to host the server.')
|
|
parser.add_argument("--debug", action="store_true",
|
|
help="Debug flag. Don't use.")
|
|
|
|
args=parser.parse_args()
|
|
set_config(args, "pidfile", "plinth.pid")
|
|
set_config(args, "server_dir", "/")
|
|
set_config(args, "debug", False)
|
|
|
|
return cfg
|
|
|
|
def set_config(args, element, default):
|
|
"""Sets *cfg* elements based on *args* values, or uses a reasonable default.
|
|
|
|
- If values are passed in from *args*, use those.
|
|
- If values are read from the config file, use those.
|
|
- If no values have been given, use default.
|
|
|
|
"""
|
|
try:
|
|
# cfg.(element) = args.(element)
|
|
setattr(cfg, element, getattr(args, element))
|
|
except AttributeError:
|
|
# if it fails, we didn't receive that argument.
|
|
try:
|
|
# if not cfg.(element): cfg.(element) = default
|
|
if not getattr(cfg, element):
|
|
setattr(cfg, element, default)
|
|
except AttributeError:
|
|
# it wasn't in the config file, but set the default anyway.
|
|
setattr(cfg, element, default)
|
|
|
|
def setup():
|
|
cfg = parse_arguments()
|
|
|
|
try:
|
|
if cfg.pidfile:
|
|
from cherrypy.process.plugins import PIDFile
|
|
PIDFile(cherrypy.engine, cfg.pidfile).subscribe()
|
|
except AttributeError:
|
|
pass
|
|
|
|
os.chdir(cfg.python_root)
|
|
cherrypy.config.update({'error_page.404': error_page_404})
|
|
cherrypy.config.update({'error_page.500': error_page_500})
|
|
cfg.log = Logger()
|
|
load_modules()
|
|
cfg.html_root = Root()
|
|
|
|
cfg.users = plugin_mount.UserStoreModule.get_plugins()[0]
|
|
cfg.page_plugins = plugin_mount.PagePlugin.get_plugins()
|
|
cfg.log("Loaded %d page plugins" % len(cfg.page_plugins))
|
|
cfg.forms = plugin_mount.FormPlugin.get_plugins()
|
|
|
|
# Add an extra server
|
|
server = _cpserver.Server()
|
|
server.socket_host = '127.0.0.1'
|
|
server.socket_port = 52854
|
|
server.subscribe()
|
|
|
|
# Configure default server
|
|
cherrypy.config.update(
|
|
{'server.socket_host': cfg.host,
|
|
'server.socket_port': cfg.port,
|
|
'server.thread_pool':10,
|
|
'tools.staticdir.root': cfg.file_root,
|
|
'tools.sessions.on':True,
|
|
'tools.auth.on':True,
|
|
'tools.sessions.storage_type':"file",
|
|
'tools.sessions.timeout':90,
|
|
'tools.sessions.storage_path':"%s/cherrypy_sessions" % cfg.data_dir,})
|
|
|
|
config = {
|
|
'/': {'tools.staticdir.root': '%s/static' % cfg.file_root,
|
|
'tools.proxy.on': True,},
|
|
'/static': {'tools.staticdir.on': True,
|
|
'tools.staticdir.dir': "."},
|
|
'/favicon.ico':{'tools.staticfile.on': True,
|
|
'tools.staticfile.filename':
|
|
"%s/static/theme/favicon.ico" % cfg.file_root}}
|
|
cherrypy.tree.mount(cfg.html_root, cfg.server_dir, config=config)
|
|
cherrypy.engine.signal_handler.subscribe()
|
|
|
|
def main():
|
|
setup()
|
|
|
|
cherrypy.engine.start()
|
|
cherrypy.engine.block()
|
|
|
|
if __name__ == '__main__':
|
|
main()
|