New clean layout to be pip installable
@ -3,22 +3,38 @@
|
|||||||
# import default settings value from src/default_settings.py
|
# import default settings value from src/default_settings.py
|
||||||
# you can refer to this file if you forgot what
|
# you can refer to this file if you forgot what
|
||||||
# settings is for and what it is set to by default
|
# settings is for and what it is set to by default
|
||||||
# DO NOT ALTER THIS LINE
|
# You probably do not want to alter this line
|
||||||
from src.default_settings import *
|
from zerobin.default_settings import *
|
||||||
|
|
||||||
# debug will get you error message and auto reload
|
# debug will get you error message and auto reload
|
||||||
# don't set this to True in production
|
# don't set this to True in production
|
||||||
DEBUG = False
|
DEBUG = True
|
||||||
|
|
||||||
|
# Should the application serve static files on it's own ?
|
||||||
|
# IF yes, set the absolute path to the static files.
|
||||||
|
# If no, set it to None
|
||||||
|
# In dev this is handy, in prod you probably want the HTTP servers
|
||||||
|
# to serve it, but it's OK for small traffic to set it to True in prod too.
|
||||||
|
STATIC_FILES_ROOT = os.path.join(ROOT_DIR, 'static')
|
||||||
|
|
||||||
# absolute path where the paste files should be store
|
# absolute path where the paste files should be store
|
||||||
# default in projectdirectory/static/content/
|
# default in projectdirectory/static/content/
|
||||||
# use "/" even under Windows
|
# use "/" even under Windows
|
||||||
PASTE_FILES_ROOT = os.path.join(STATIC_FILES_ROOT, 'content')
|
PASTE_FILES_ROOT = os.path.join(ROOT_DIR, 'static', 'content')
|
||||||
|
|
||||||
|
# a tuple of absolute paths of directory where to look the template for
|
||||||
|
# the first one will be the first to be looked into
|
||||||
|
# if you want to override a template, create a new dir, write the
|
||||||
|
# template with the same name as the one you want to override in it
|
||||||
|
# then add the dir path at the top of this tuple
|
||||||
|
TEMPLATE_DIRS = (
|
||||||
|
os.path.join(ROOT_DIR, 'views'),
|
||||||
|
)
|
||||||
|
|
||||||
# Port and host the embeded python server should be using
|
# Port and host the embeded python server should be using
|
||||||
# You can also specify them using the --host and --port script options
|
# You can also specify them using the --host and --port script options
|
||||||
# which have priority on these settings
|
# which have priority on these settings
|
||||||
HOST = "127.0.0.1"
|
HOST = "0.0.0.0"
|
||||||
PORT = "8000"
|
PORT = "8000"
|
||||||
|
|
||||||
# User and group the server should run as. Set to None if it should be the
|
# User and group the server should run as. Set to None if it should be the
|
||||||
@ -26,23 +42,16 @@ PORT = "8000"
|
|||||||
USER = None
|
USER = None
|
||||||
GROUP = None
|
GROUP = None
|
||||||
|
|
||||||
# limit size of pasted text in bytes. Be carefull allowing too much
|
|
||||||
# size can slow down user's browser
|
|
||||||
MAX_SIZE = 1024 * 500
|
|
||||||
MAX_SIZE_KB = int(math.ceil(MAX_SIZE / 1024.0))
|
|
||||||
|
|
||||||
# Names/links to insert in the menu bar.
|
# Names/links to insert in the menu bar.
|
||||||
# Any link with "mailto:" will be escaped to prevent spam
|
# Any link with "mailto:" will be escaped to prevent spam
|
||||||
MENU = (
|
MENU = (
|
||||||
('Home', '/'), # internal link
|
('Home', '/'), # internal link. First link will be highlited
|
||||||
('Download 0bin', 'https://github.com/sametmax/0bin'), # external link
|
('Download 0bin', 'https://github.com/sametmax/0bin'), # external link
|
||||||
('Contact', 'mailto:your@email.com') # email
|
('Contact', 'mailto:your@email.com') # email
|
||||||
)
|
)
|
||||||
|
|
||||||
# this import a file named settings_local.py if it exists
|
# limit size of pasted text in bytes. Be carefull allowing too much size can slow down user's
|
||||||
# you may want to create such a file to have different settings
|
# browser
|
||||||
# on each machine
|
MAX_SIZE = 1024 * 500
|
||||||
try:
|
MAX_SIZE_KB = int(math.ceil(MAX_SIZE / 1024.0))
|
||||||
from settings_local import *
|
|
||||||
except ImportError:
|
|
||||||
pass
|
|
@ -1,3 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
|
|
49
src/utils.py
@ -1,49 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
import time
|
|
||||||
import os
|
|
||||||
import glob
|
|
||||||
import tempfile
|
|
||||||
|
|
||||||
|
|
||||||
try:
|
|
||||||
from privilege import drop_privileges_permanently, coerce_user, coerce_group
|
|
||||||
except (AttributeError):
|
|
||||||
pass # privilege does't work on several plateform
|
|
||||||
|
|
||||||
|
|
||||||
def drop_privileges(user=None, group=None, wait=5):
|
|
||||||
"""
|
|
||||||
Try to set the process user and group to another one.
|
|
||||||
If no group is provided, it's set to the same as the user.
|
|
||||||
You can wait for a certain time before doing so.
|
|
||||||
"""
|
|
||||||
if wait:
|
|
||||||
time.sleep(wait)
|
|
||||||
if user:
|
|
||||||
group = group or user
|
|
||||||
try:
|
|
||||||
user = coerce_user(user)
|
|
||||||
group = coerce_group(group)
|
|
||||||
|
|
||||||
lock_files = glob.glob(os.path.join(tempfile.gettempdir(),
|
|
||||||
'bottle.*.lock'))
|
|
||||||
for lock_file in lock_files:
|
|
||||||
os.chown(lock_file, user, group)
|
|
||||||
|
|
||||||
drop_privileges_permanently(user, group, ())
|
|
||||||
except Exception:
|
|
||||||
print "Failed to drop privileges. Running with current user."
|
|
||||||
|
|
||||||
|
|
||||||
def dmerge(*args):
|
|
||||||
"""
|
|
||||||
return new directionay being the sum of all merged dictionaries passed as arguments
|
|
||||||
"""
|
|
||||||
|
|
||||||
dictionary = {}
|
|
||||||
|
|
||||||
for arg in args:
|
|
||||||
dictionary.update(arg)
|
|
||||||
|
|
||||||
return dictionary
|
|
131
start.py
@ -2,133 +2,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# vim: ai ts=4 sts=4 et sw=4
|
# vim: ai ts=4 sts=4 et sw=4
|
||||||
|
|
||||||
"""
|
from zerobin.routes import main
|
||||||
Main script including controller, rooting, dependancy management, and
|
|
||||||
server run.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import sys
|
main()
|
||||||
import os
|
|
||||||
import thread
|
|
||||||
|
|
||||||
from datetime import datetime, timedelta
|
|
||||||
|
|
||||||
# add project dir and libs dir to the PYTHON PATH to ensure they are
|
|
||||||
# importable
|
|
||||||
import settings
|
|
||||||
sys.path.insert(0, os.path.dirname(settings.ROOT_DIR))
|
|
||||||
sys.path.append(os.path.join(settings.ROOT_DIR, 'libs'))
|
|
||||||
|
|
||||||
import bottle
|
|
||||||
from bottle import (Bottle, run, static_file, view, request)
|
|
||||||
|
|
||||||
import clize
|
|
||||||
|
|
||||||
|
|
||||||
from src.paste import Paste
|
|
||||||
from src.utils import drop_privileges, dmerge
|
|
||||||
|
|
||||||
|
|
||||||
app = Bottle()
|
|
||||||
|
|
||||||
global_vars = {
|
|
||||||
'settings': settings
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/')
|
|
||||||
@view('home')
|
|
||||||
def index():
|
|
||||||
return global_vars
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/paste/create', method='POST')
|
|
||||||
def create_paste():
|
|
||||||
|
|
||||||
try:
|
|
||||||
content = unicode(request.forms.get('content', ''), 'utf8')
|
|
||||||
except UnicodeDecodeError:
|
|
||||||
return {'status': 'error',
|
|
||||||
'message': u"Encoding error: the paste couldn't be saved."}
|
|
||||||
|
|
||||||
if '{"iv":' not in content: # reject silently non encrypted content
|
|
||||||
return ''
|
|
||||||
|
|
||||||
if content:
|
|
||||||
# check size of the paste. if more than settings return error without saving paste.
|
|
||||||
# prevent from unusual use of the system.
|
|
||||||
# need to be improved
|
|
||||||
if len(content) < settings.MAX_SIZE:
|
|
||||||
expiration = request.forms.get('expiration', u'burn_after_reading')
|
|
||||||
paste = Paste(expiration=expiration, content=content)
|
|
||||||
paste.save()
|
|
||||||
return {'status': 'ok',
|
|
||||||
'paste': paste.uuid}
|
|
||||||
|
|
||||||
return {'status': 'error',
|
|
||||||
'message': u"Serveur error: the paste couldn't be saved. Please try later."}
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/paste/:paste_id')
|
|
||||||
@view('paste')
|
|
||||||
def display_paste(paste_id):
|
|
||||||
|
|
||||||
now = datetime.now()
|
|
||||||
keep_alive = False
|
|
||||||
try:
|
|
||||||
paste = Paste.load(paste_id)
|
|
||||||
# Delete the paste if it expired:
|
|
||||||
if 'burn_after_reading' in str(paste.expiration):
|
|
||||||
# burn_after_reading contains the paste creation date
|
|
||||||
# if this read appends 10 seconds after the creation date
|
|
||||||
# we don't delete the paste because it means it's the redirection
|
|
||||||
# to the paste that happens during the paste creation
|
|
||||||
try:
|
|
||||||
keep_alive = paste.expiration.split('#')[1]
|
|
||||||
keep_alive = datetime.strptime(keep_alive, '%Y-%m-%d %H:%M:%S.%f')
|
|
||||||
keep_alive = now < keep_alive + timedelta(seconds=10)
|
|
||||||
except IndexError:
|
|
||||||
keep_alive = False
|
|
||||||
if not keep_alive:
|
|
||||||
paste.delete()
|
|
||||||
|
|
||||||
elif paste.expiration < now:
|
|
||||||
paste.delete()
|
|
||||||
raise ValueError()
|
|
||||||
|
|
||||||
except (TypeError, ValueError):
|
|
||||||
#abort(404, u"This paste doesn't exist or has expired")
|
|
||||||
return error404(ValueError)
|
|
||||||
|
|
||||||
context = {'paste': paste, 'keep_alive': keep_alive}
|
|
||||||
return dmerge(context, global_vars)
|
|
||||||
|
|
||||||
|
|
||||||
@app.error(404)
|
|
||||||
@view('404')
|
|
||||||
def error404(code):
|
|
||||||
return global_vars
|
|
||||||
|
|
||||||
|
|
||||||
@clize.clize
|
|
||||||
def runserver(host=settings.HOST, port=settings.PORT, debug=settings.DEBUG,
|
|
||||||
serve_static=settings.DEBUG, user=settings.USER,
|
|
||||||
group=settings.GROUP):
|
|
||||||
|
|
||||||
if serve_static:
|
|
||||||
@app.route('/static/<filename:path>')
|
|
||||||
def server_static(filename):
|
|
||||||
return static_file(filename, root=settings.STATIC_FILES_ROOT)
|
|
||||||
|
|
||||||
thread.start_new_thread(drop_privileges, (user, group))
|
|
||||||
|
|
||||||
if debug:
|
|
||||||
bottle.debug(True)
|
|
||||||
run(app, host=host, port=port, reloader=True, server="cherrypy")
|
|
||||||
else:
|
|
||||||
run(app, host=host, port=port, server="cherrypy")
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
clize.run(runserver)
|
|
@ -2,21 +2,41 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# vim: ai ts=4 sts=4 et sw=4
|
# vim: ai ts=4 sts=4 et sw=4
|
||||||
|
|
||||||
|
|
||||||
|
######## NOT SETTINGS, JUST BOILER PLATE ##############
|
||||||
import os
|
import os
|
||||||
import math
|
import math
|
||||||
|
|
||||||
ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||||
STATIC_FILES_ROOT = os.path.join(ROOT_DIR, 'static')
|
LIBS_DIR = os.path.join(os.path.dirname(ROOT_DIR), 'libs')
|
||||||
|
|
||||||
|
######## END OF BOILER PLATE ##############
|
||||||
|
|
||||||
# debug will get you error message and auto reload
|
# debug will get you error message and auto reload
|
||||||
# don't set this to True in production
|
# don't set this to True in production
|
||||||
DEBUG = False
|
DEBUG = False
|
||||||
|
|
||||||
|
# Should the application serve static files on it's own ?
|
||||||
|
# IF yes, set the absolute path to the static files.
|
||||||
|
# If no, set it to None
|
||||||
|
# In dev this is handy, in prod you probably want the HTTP servers
|
||||||
|
# to serve it, but it's OK for small traffic to set it to True in prod too.
|
||||||
|
STATIC_FILES_ROOT = os.path.join(ROOT_DIR, 'static')
|
||||||
|
|
||||||
# absolute path where the paste files should be store
|
# absolute path where the paste files should be store
|
||||||
# default in projectdirectory/static/content/
|
# default in projectdirectory/static/content/
|
||||||
# use "/" even under Windows
|
# use "/" even under Windows
|
||||||
PASTE_FILES_ROOT = os.path.join(STATIC_FILES_ROOT, 'content')
|
PASTE_FILES_ROOT = os.path.join(STATIC_FILES_ROOT, 'content')
|
||||||
|
|
||||||
|
# a tuple of absolute paths of directory where to look the template for
|
||||||
|
# the first one will be the first to be looked into
|
||||||
|
# if you want to override a template, create a new dir, write the
|
||||||
|
# template with the same name as the one you want to override in it
|
||||||
|
# then add the dir path at the top of this tuple
|
||||||
|
TEMPLATE_DIRS = (
|
||||||
|
os.path.join(ROOT_DIR, 'views'),
|
||||||
|
)
|
||||||
|
|
||||||
# Port and host the embeded python server should be using
|
# Port and host the embeded python server should be using
|
||||||
# You can also specify them using the --host and --port script options
|
# You can also specify them using the --host and --port script options
|
||||||
# which have priority on these settings
|
# which have priority on these settings
|
@ -2,11 +2,10 @@
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import hashlib
|
import hashlib
|
||||||
import json
|
|
||||||
|
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
import settings
|
from utils import settings
|
||||||
|
|
||||||
|
|
||||||
class Paste(object):
|
class Paste(object):
|
||||||
@ -25,12 +24,10 @@ class Paste(object):
|
|||||||
|
|
||||||
|
|
||||||
def __init__(self, uuid=None, content=None,
|
def __init__(self, uuid=None, content=None,
|
||||||
expiration=None,
|
expiration=None):
|
||||||
comments=None):
|
|
||||||
|
|
||||||
self.content = content
|
self.content = content
|
||||||
self.expiration = expiration
|
self.expiration = expiration
|
||||||
self.comments = comments
|
|
||||||
|
|
||||||
if isinstance(self.content, unicode):
|
if isinstance(self.content, unicode):
|
||||||
self.content = self.content.encode('utf8')
|
self.content = self.content.encode('utf8')
|
||||||
@ -95,7 +92,6 @@ class Paste(object):
|
|||||||
uuid = os.path.basename(path)
|
uuid = os.path.basename(path)
|
||||||
expiration = paste.next().strip()
|
expiration = paste.next().strip()
|
||||||
content = paste.next().strip()
|
content = paste.next().strip()
|
||||||
comments = paste.read()[:-1] # remove the last coma
|
|
||||||
if "burn_after_reading" not in str(expiration):
|
if "burn_after_reading" not in str(expiration):
|
||||||
expiration = datetime.strptime(expiration, '%Y-%m-%d %H:%M:%S.%f')
|
expiration = datetime.strptime(expiration, '%Y-%m-%d %H:%M:%S.%f')
|
||||||
|
|
||||||
@ -104,8 +100,7 @@ class Paste(object):
|
|||||||
except (IOError, OSError):
|
except (IOError, OSError):
|
||||||
raise ValueError(u'Can not open paste from file %s' % path)
|
raise ValueError(u'Can not open paste from file %s' % path)
|
||||||
|
|
||||||
return Paste(uuid=uuid, comments=comments,
|
return Paste(uuid=uuid, expiration=expiration, content=content)
|
||||||
expiration=expiration, content=content)
|
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -120,9 +115,6 @@ class Paste(object):
|
|||||||
def save(self):
|
def save(self):
|
||||||
"""
|
"""
|
||||||
Save the content of this paste to a file.
|
Save the content of this paste to a file.
|
||||||
|
|
||||||
If comments are passed, they are expected to be serialized
|
|
||||||
already.
|
|
||||||
"""
|
"""
|
||||||
head, tail = self.uuid[:2], self.uuid[2:4]
|
head, tail = self.uuid[:2], self.uuid[2:4]
|
||||||
|
|
||||||
@ -157,8 +149,6 @@ class Paste(object):
|
|||||||
with open(self.path, 'w') as f:
|
with open(self.path, 'w') as f:
|
||||||
f.write(unicode(self.expiration) + '\n')
|
f.write(unicode(self.expiration) + '\n')
|
||||||
f.write(self.content + '\n')
|
f.write(self.content + '\n')
|
||||||
if self.comments:
|
|
||||||
f.write(self.comments)
|
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
@ -169,26 +159,3 @@ class Paste(object):
|
|||||||
"""
|
"""
|
||||||
os.remove(self.path)
|
os.remove(self.path)
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def add_comment_to(cls, uuid, **comment):
|
|
||||||
"""
|
|
||||||
Append a comment to the file of the paste with the given uuid.
|
|
||||||
The comment is serialized to json, and a comma is added at the
|
|
||||||
end of it. Then the result is appended to the paste file.
|
|
||||||
This way we can add sequencially all comments to the file by just
|
|
||||||
appending to it, and then extracting the comment by selecting
|
|
||||||
this big blob of text, adding [] around it and use it as a json list
|
|
||||||
with no extra processing.
|
|
||||||
"""
|
|
||||||
with open(cls.get_path(uuid), 'a') as f:
|
|
||||||
f.write(json.dumps(comment) + u',\n')
|
|
||||||
|
|
||||||
|
|
||||||
def add_comment(self, **comment):
|
|
||||||
"""
|
|
||||||
Append a comment to the file of this paste.
|
|
||||||
|
|
||||||
Use add_comment_to()
|
|
||||||
"""
|
|
||||||
self.add_comment_to(self.uuid, **comment)
|
|
141
zerobin/routes.py
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# vim: ai ts=4 sts=4 et sw=4
|
||||||
|
|
||||||
|
"""
|
||||||
|
Main script including controller, rooting, dependancy management, and
|
||||||
|
server run.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import thread
|
||||||
|
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
|
# add project dir and libs dir to the PYTHON PATH to ensure they are
|
||||||
|
# importable
|
||||||
|
from utils import settings
|
||||||
|
|
||||||
|
import bottle
|
||||||
|
from bottle import (Bottle, run, static_file, view, request)
|
||||||
|
|
||||||
|
import clize
|
||||||
|
|
||||||
|
from paste import Paste
|
||||||
|
from utils import drop_privileges, dmerge
|
||||||
|
|
||||||
|
|
||||||
|
app = Bottle()
|
||||||
|
GLOBAL_CONTEXT = {
|
||||||
|
'settings': settings
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/')
|
||||||
|
@view('home')
|
||||||
|
def index():
|
||||||
|
return GLOBAL_CONTEXT
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/paste/create', method='POST')
|
||||||
|
def create_paste():
|
||||||
|
|
||||||
|
try:
|
||||||
|
content = unicode(request.forms.get('content', ''), 'utf8')
|
||||||
|
except UnicodeDecodeError:
|
||||||
|
return {'status': 'error',
|
||||||
|
'message': u"Encoding error: the paste couldn't be saved."}
|
||||||
|
|
||||||
|
if '{"iv":' not in content: # reject silently non encrypted content
|
||||||
|
return ''
|
||||||
|
|
||||||
|
if content:
|
||||||
|
# check size of the paste. if more than settings return error without saving paste.
|
||||||
|
# prevent from unusual use of the system.
|
||||||
|
# need to be improved
|
||||||
|
if len(content) < settings.MAX_SIZE:
|
||||||
|
expiration = request.forms.get('expiration', u'burn_after_reading')
|
||||||
|
paste = Paste(expiration=expiration, content=content)
|
||||||
|
paste.save()
|
||||||
|
return {'status': 'ok',
|
||||||
|
'paste': paste.uuid}
|
||||||
|
|
||||||
|
return {'status': 'error',
|
||||||
|
'message': u"Serveur error: the paste couldn't be saved. Please try later."}
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/paste/:paste_id')
|
||||||
|
@view('paste')
|
||||||
|
def display_paste(paste_id):
|
||||||
|
|
||||||
|
now = datetime.now()
|
||||||
|
keep_alive = False
|
||||||
|
try:
|
||||||
|
paste = Paste.load(paste_id)
|
||||||
|
# Delete the paste if it expired:
|
||||||
|
if 'burn_after_reading' in str(paste.expiration):
|
||||||
|
# burn_after_reading contains the paste creation date
|
||||||
|
# if this read appends 10 seconds after the creation date
|
||||||
|
# we don't delete the paste because it means it's the redirection
|
||||||
|
# to the paste that happens during the paste creation
|
||||||
|
try:
|
||||||
|
keep_alive = paste.expiration.split('#')[1]
|
||||||
|
keep_alive = datetime.strptime(keep_alive, '%Y-%m-%d %H:%M:%S.%f')
|
||||||
|
keep_alive = now < keep_alive + timedelta(seconds=10)
|
||||||
|
except IndexError:
|
||||||
|
keep_alive = False
|
||||||
|
if not keep_alive:
|
||||||
|
paste.delete()
|
||||||
|
|
||||||
|
elif paste.expiration < now:
|
||||||
|
paste.delete()
|
||||||
|
raise ValueError()
|
||||||
|
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
#abort(404, u"This paste doesn't exist or has expired")
|
||||||
|
return error404(ValueError)
|
||||||
|
|
||||||
|
context = {'paste': paste, 'keep_alive': keep_alive}
|
||||||
|
return dmerge(context, GLOBAL_CONTEXT)
|
||||||
|
|
||||||
|
|
||||||
|
@app.error(404)
|
||||||
|
@view('404')
|
||||||
|
def error404(code):
|
||||||
|
return GLOBAL_CONTEXT
|
||||||
|
|
||||||
|
|
||||||
|
@clize.clize
|
||||||
|
def runserver(host='', port='', debug=None, serve_static='', user='',
|
||||||
|
group='', settings_file=''):
|
||||||
|
|
||||||
|
# merge the settings
|
||||||
|
if settings_file:
|
||||||
|
settings.update_with_file(settings_file)
|
||||||
|
|
||||||
|
settings.HOST = host or settings.HOST
|
||||||
|
settings.PORT = port or settings.PORT
|
||||||
|
settings.DEBUG = debug if debug is not None else settings.DEBUG
|
||||||
|
settings.STATIC_FILES_ROOT = serve_static or settings.STATIC_FILES_ROOT
|
||||||
|
settings.USER = user or settings.USER
|
||||||
|
settings.GROUP = group or settings.GROUP
|
||||||
|
|
||||||
|
# make sure the templates can be loaded
|
||||||
|
for d in reversed(settings.TEMPLATE_DIRS):
|
||||||
|
bottle.TEMPLATE_PATH.insert(0, d)
|
||||||
|
|
||||||
|
if serve_static:
|
||||||
|
@app.route('/static/<filename:path>')
|
||||||
|
def server_static(filename):
|
||||||
|
return static_file(filename, root=settings.STATIC_FILES_ROOT)
|
||||||
|
|
||||||
|
thread.start_new_thread(drop_privileges, (settings.USER, settings.GROUP))
|
||||||
|
|
||||||
|
if settings.DEBUG:
|
||||||
|
bottle.debug(True)
|
||||||
|
run(app, host=settings.HOST, port=settings.PORT, reloader=True, server="cherrypy")
|
||||||
|
else:
|
||||||
|
run(app, host=settings.HOST, port=settings.PORT, server="cherrypy")
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
clize.run(runserver)
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.3 KiB |
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.3 KiB |
Before Width: | Height: | Size: 968 B After Width: | Height: | Size: 968 B |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
106
zerobin/utils.py
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import time
|
||||||
|
import os
|
||||||
|
import glob
|
||||||
|
import tempfile
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import default_settings
|
||||||
|
sys.path.append(default_settings.LIBS_DIR)
|
||||||
|
|
||||||
|
try:
|
||||||
|
from privilege import drop_privileges_permanently, coerce_user, coerce_group
|
||||||
|
except (AttributeError):
|
||||||
|
pass # privilege does't work on several plateform
|
||||||
|
|
||||||
|
|
||||||
|
def drop_privileges(user=None, group=None, wait=5):
|
||||||
|
"""
|
||||||
|
Try to set the process user and group to another one.
|
||||||
|
If no group is provided, it's set to the same as the user.
|
||||||
|
You can wait for a certain time before doing so.
|
||||||
|
"""
|
||||||
|
if wait:
|
||||||
|
time.sleep(wait)
|
||||||
|
if user:
|
||||||
|
group = group or user
|
||||||
|
try:
|
||||||
|
user = coerce_user(user)
|
||||||
|
group = coerce_group(group)
|
||||||
|
|
||||||
|
lock_files = glob.glob(os.path.join(tempfile.gettempdir(),
|
||||||
|
'bottle.*.lock'))
|
||||||
|
for lock_file in lock_files:
|
||||||
|
os.chown(lock_file, user, group)
|
||||||
|
|
||||||
|
drop_privileges_permanently(user, group, ())
|
||||||
|
except Exception:
|
||||||
|
print "Failed to drop privileges. Running with current user."
|
||||||
|
|
||||||
|
|
||||||
|
def dmerge(*args):
|
||||||
|
"""
|
||||||
|
Return new directionay being the sum of all merged dictionaries passed
|
||||||
|
as arguments
|
||||||
|
"""
|
||||||
|
|
||||||
|
dictionary = {}
|
||||||
|
|
||||||
|
for arg in args:
|
||||||
|
dictionary.update(arg)
|
||||||
|
|
||||||
|
return dictionary
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class SettingsContainer(object):
|
||||||
|
"""
|
||||||
|
Singleton containing the settings for the whole app
|
||||||
|
"""
|
||||||
|
|
||||||
|
_instance = None
|
||||||
|
|
||||||
|
def __new__(cls, *args, **kwargs):
|
||||||
|
|
||||||
|
if not cls._instance:
|
||||||
|
cls._instance = super(SettingsContainer, cls).__new__(cls, *args,
|
||||||
|
**kwargs)
|
||||||
|
cls._instance.update_with_module(default_settings)
|
||||||
|
return cls._instance
|
||||||
|
|
||||||
|
|
||||||
|
def update_with_module(self, module):
|
||||||
|
"""
|
||||||
|
Update settings with values from the given module.
|
||||||
|
(Taking only variable with uppercased name)
|
||||||
|
"""
|
||||||
|
for name, value in module.__dict__.iteritems():
|
||||||
|
if name.isupper():
|
||||||
|
setattr(self, name, value)
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_module(cls, module):
|
||||||
|
"""
|
||||||
|
Create an instance of SettingsContainer with values based
|
||||||
|
on the one in the passed module.
|
||||||
|
"""
|
||||||
|
settings = cls()
|
||||||
|
settings.update_with_module(module)
|
||||||
|
return settings
|
||||||
|
|
||||||
|
|
||||||
|
def update_with_file(self, filepath):
|
||||||
|
"""
|
||||||
|
Update settings with values from the given module file.
|
||||||
|
User update_with_module() behind the scene
|
||||||
|
"""
|
||||||
|
sys.path.insert(0, os.path.dirname(filepath))
|
||||||
|
module_name = os.path.splitext(os.path.basename(filepath))[0]
|
||||||
|
return self.update_with_module(__import__(module_name))
|
||||||
|
|
||||||
|
|
||||||
|
settings = SettingsContainer()
|
@ -14,11 +14,24 @@
|
|||||||
<link href="/static/css/bootstrap.css" rel="stylesheet">
|
<link href="/static/css/bootstrap.css" rel="stylesheet">
|
||||||
<link href="/static/css/style.css" rel="stylesheet">
|
<link href="/static/css/style.css" rel="stylesheet">
|
||||||
|
|
||||||
<!-- Le HTML5 shim, for IE6-8 support of HTML5 elements -->
|
<!-- Le HTML5 shim, for IE7-8 support of HTML5 elements -->
|
||||||
<!--[if lt IE 9]>
|
<!--[if lt IE 9]>
|
||||||
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
|
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
|
||||||
<![endif]-->
|
<![endif]-->
|
||||||
|
|
||||||
|
<!-- Prompt IE 6 users to tell them to install chrome frame -->
|
||||||
|
<!--[if gte IE 0 ]>
|
||||||
|
<script defer src="//ajax.googleapis.com/ajax/libs/chrome-frame/1.0.3/CFInstall.min.js"></script>
|
||||||
|
<script defer>window.attachEvent('onload',
|
||||||
|
function() {
|
||||||
|
CFInstall.check({
|
||||||
|
mode: 'overlay',
|
||||||
|
cssText: 'margin-top: 400px;'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
<![endif]-->
|
||||||
|
|
||||||
<script src="/static/js/jquery-1.7.2.min.js"></script>
|
<script src="/static/js/jquery-1.7.2.min.js"></script>
|
||||||
<script src="/static/js/sjcl.js"></script>
|
<script src="/static/js/sjcl.js"></script>
|
||||||
<script src="/static/js/behavior.js"></script>
|
<script src="/static/js/behavior.js"></script>
|