1
0
mirror of https://github.com/Tygs/0bin.git synced 2023-08-10 21:13:00 +03:00

97 Commits

Author SHA1 Message Date
183150a6b0 Release v1.0.1 2020-08-18 14:41:46 +02:00
0b1fad5a2b Merge branch 'v2' 2020-08-18 14:38:46 +02:00
fab8f31064 Change compress tooling to avoid svg tag scrambling 2020-08-18 14:38:31 +02:00
eb5ecdb04e Merge branch 'v2' of ksamuel-github:/Tygs/0bin into v2 2020-08-18 13:26:33 +02:00
cf3a05ab53 Migrate build system to pydoit 2020-08-18 13:26:29 +02:00
2bba894d67 buy bitcoin guide 2020-08-18 09:17:58 +02:00
ea3ee454b6 Merge branch 'v2' 2020-08-15 16:30:26 +02:00
b54574d5ca binance link to buy btc responsive 2020-08-15 16:21:25 +02:00
ff00c972b1 Merge branch 'v2' of github.com:Tygs/0bin into v2 2020-08-15 16:17:29 +02:00
d753f92207 binance link to buy btc responsive 2020-08-15 16:17:18 +02:00
6951d3da56 Fix README rst typo 2020-08-15 14:47:17 +02:00
b73ce9684f Tweak command to remove expired pastes 2020-08-15 14:46:06 +02:00
262db62265 Add command to remove expired pastes 2020-08-15 14:40:26 +02:00
32ab32aead Reader for prod 2020-08-15 14:17:35 +02:00
0f780ddabc binance link to buy btc responsive 2020-08-15 14:13:00 +02:00
93db217998 binance link to buy btc 2020-08-15 14:06:19 +02:00
8dfbf91e38 Merge branch 'v2' of github.com:Tygs/0bin into v2 2020-08-15 13:27:59 +02:00
c9f1d22054 center previous pastes 2020-08-15 13:27:51 +02:00
285da1c596 Edit README and setup.cfg for prod 2020-08-15 13:24:46 +02:00
784b07de14 previous paste css firefox 2020-08-15 12:56:05 +02:00
0c18aae662 Merge branch 'v2' of github.com:Tygs/0bin into v2 2020-08-15 12:50:22 +02:00
d851f8c74c base template 2020-08-15 12:50:07 +02:00
0e936db771 Augment compression quality for images 2020-08-15 12:08:04 +02:00
c4e3718c01 Merge branch 'v2' of ksamuel-github:/Tygs/0bin into v2 2020-08-15 12:07:47 +02:00
6d00fd5972 Compress image prior to uploading 2020-08-15 12:05:22 +02:00
0d824b70bd Merge branch 'v2' of github.com:Tygs/0bin into v2 2020-08-15 11:53:36 +02:00
c95fe5a2f7 css responsive icons menu 2020-08-15 11:53:32 +02:00
233b66fa68 Regenerate prod files 2020-08-15 11:09:34 +02:00
d953e28e23 Fix delete error message and missing button 2020-08-15 10:54:55 +02:00
d11081fc54 merge 2020-08-15 10:33:19 +02:00
762c18eb74 favicon & css img 2020-08-15 10:29:22 +02:00
9caaaf51f7 Fix link to close alert messages 2020-08-15 10:27:52 +02:00
67433e4a17 Remove upload artefact on second attempt 2020-08-15 10:18:44 +02:00
eb22cd90b2 Add copy btc to clipboard 2020-08-15 10:02:57 +02:00
3a40ea6694 merge 2020-08-15 09:21:56 +02:00
23133e362c css btc tip 2020-08-14 18:20:31 +02:00
183f9c978b Remove additional.min.js 2020-08-14 18:19:43 +02:00
855c26332a Update bundling machinery 2020-08-14 18:18:17 +02:00
d6d987f476 Update README 2020-08-14 18:15:45 +02:00
862cc3bdfa css paste title 2020-08-14 17:10:35 +02:00
701fed35b2 Fix WSGI prod file 2020-08-14 16:54:57 +02:00
b9831c7f60 In dev mode, display the full admin URL at server start 2020-08-14 16:45:06 +02:00
2d9dab6591 Merge branch 'v2' of ksamuel-github:/Tygs/0bin into v2 2020-08-14 16:41:55 +02:00
952d77a830 Add btc tip 2020-08-14 16:41:45 +02:00
6d98c4e6a8 merge 2020-08-14 16:03:51 +02:00
052fc4167a admin css design 2020-08-14 16:02:05 +02:00
65f5578a6c Merge branch 'v2' of ksamuel-github:/Tygs/0bin into v2 2020-08-14 15:09:04 +02:00
d9a79f46dc Add paste title 2020-08-14 15:08:55 +02:00
2b5dcc9ba9 Merge branch 'v2' of github.com:Tygs/0bin into v2 2020-08-14 11:32:19 +02:00
89d25030d4 cleaning reader mode and admin css 2020-08-14 11:32:14 +02:00
6bf5333b14 Reader mode param in URL 2020-08-14 11:18:59 +02:00
d133494e9d Fix upload button glitch 2020-08-14 10:26:04 +02:00
9b2f9911f2 Add batch paste methods and fix dl link glitch 2020-08-14 10:07:36 +02:00
e37ed06a47 Add dl for text 2020-08-13 19:04:57 +02:00
e667e1f947 Add active class on previousPasteMenu 2020-08-13 18:23:11 +02:00
2abbab5305 Handle paste 2020-08-13 18:14:19 +02:00
1f35f96fe3 Add padding to paste 2020-08-13 17:26:25 +02:00
d0cca8cdd9 Merge ok 2020-08-13 17:18:59 +02:00
cc3ed69710 Replace pre reader mode by a div 2020-08-13 17:17:46 +02:00
8b72a90a32 merge 2020-08-13 17:01:45 +02:00
128cd64adf css pre for read mode 2020-08-13 16:54:23 +02:00
cda601a355 Add toggle for reader mode 2020-08-13 16:53:17 +02:00
1939a6b89e merge 2020-08-13 16:23:38 +02:00
8f7ed5cddc Small fix on settings style and requirements 2020-08-13 15:54:50 +02:00
836fa17bc4 Merge branch 'v2' of github.com:Tygs/0bin into v2 2020-08-13 15:25:49 +02:00
ad016e50bd css & fix image display 2020-08-13 15:25:19 +02:00
ba14ae969e Sticky footer 2020-08-13 14:54:34 +02:00
6c1dad2626 Put config app dir 2020-08-13 14:36:42 +02:00
2d3f3998de Fix favicon 404 in dev 2020-08-12 17:34:19 +02:00
64e3a285f9 Remove privilege lib 2020-08-12 17:30:39 +02:00
3308e6d74f Move from cherrypy to paste 2020-08-12 17:26:21 +02:00
aaf8e647d6 Add licence file 2020-08-12 16:52:47 +02:00
dc6514a11f Remove expiration 2020-08-12 16:10:37 +02:00
92251432bd Fix notifications and 404 history cleaning 2020-08-12 16:08:19 +02:00
e5faaac077 Delete paste in admin 2020-08-12 15:39:07 +02:00
b6732d7348 404 theme css 2020-08-12 15:27:46 +02:00
425a947051 404 css jolifygationnement merge 2020-08-12 15:23:42 +02:00
0c87b2339f 404 css jolifygationnement 2020-08-12 15:22:47 +02:00
3b4c57e850 Add empty admin page with login 2020-08-12 15:21:49 +02:00
500ca82b69 Merge branch 'v2' of ksamuel-github:/Tygs/0bin into v2 2020-08-12 14:32:29 +02:00
eb05aaa103 email on bottom site + footer menu 2020-08-12 14:28:58 +02:00
130bedf6ec email on bottom site + footer menu 2020-08-12 14:25:22 +02:00
1007faa8f5 Merge branch 'admin' into v2 2020-08-12 14:14:39 +02:00
65578a4c72 Merge branch 'v2' of ksamuel-github:/Tygs/0bin into v2 2020-08-12 14:14:28 +02:00
6a0370b1f5 Autofit textarea 2020-08-12 14:14:13 +02:00
7bf0ba4ddd Remove image copy to clipboard 2020-08-12 14:02:13 +02:00
c34f9f77e7 merge new prettyffy 2020-08-12 11:41:02 +02:00
e5637fd7bf change prettify code error in behavior 2020-08-12 11:35:35 +02:00
a057e31e5d change prettify code error in behavior 2020-08-12 11:34:33 +02:00
2aeaa0c482 Fix email this 2020-08-12 11:21:18 +02:00
b2f49953f5 Fix progress bar 2020-08-12 11:15:01 +02:00
23d7b73514 Fix 404 2020-08-12 11:01:31 +02:00
7c6adb32ab Fix previousPaste menu 2020-08-12 11:00:41 +02:00
601aa5a16b Post merge fixes 2020-08-12 10:19:31 +02:00
933118c1cd Merge new theme 2020-08-12 10:11:51 +02:00
2e67a09bca Merge branch 'v2' of github.com:Tygs/0bin into v2 2020-08-12 09:34:14 +02:00
a8e6763cc0 new theme 2020-08-12 09:26:17 +02:00
58 changed files with 2799 additions and 8740 deletions

28
.gitignore vendored
View File

@ -7,8 +7,7 @@
*_index
*.orig
*.swp
.* # all hidden files...
!.gitignore # ...except gitignore
# binaries
@ -16,25 +15,30 @@
*.pyo
__pycache__
.mypy*
*.db
/tools/zerobinpaste.js
/tools/zerobinpaste.min
/tools/zerobinpaste
# files generated by setuptools
# build and env
build
0bin
env
venv
*.egg-info
dist
*.tar.gz
*.egg
*.in
# others
content
# IDE
*.sublime-project
*.sublime-workspace
settings_local.py
build
*-workspace
*_old*
.vscode
var
# JS
package-lock.json
node_modules

597
.pylintrc Normal file
View File

@ -0,0 +1,597 @@
[MASTER]
# A comma-separated list of package or module names from where C extensions may
# be loaded. Extensions are loading into the active Python interpreter and may
# run arbitrary code.
extension-pkg-whitelist=
# Specify a score threshold to be exceeded before program exits with error.
fail-under=10
# Add files or directories to the blacklist. They should be base names, not
# paths.
ignore=CVS
# Add files or directories matching the regex patterns to the blacklist. The
# regex matches against base names, not paths.
ignore-patterns=
# Python code to execute, usually for sys.path manipulation such as
# pygtk.require().
#init-hook=
# Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the
# number of processors available to use.
jobs=1
# Control the amount of potential inferred values when inferring a single
# object. This can help the performance when dealing with large functions or
# complex, nested conditions.
limit-inference-results=100
# List of plugins (as comma separated values of python module names) to load,
# usually to register additional checkers.
load-plugins=
# Pickle collected data for later comparisons.
persistent=yes
# When enabled, pylint would attempt to guess common misconfiguration and emit
# user-friendly hints instead of false-positive error messages.
suggestion-mode=yes
# Allow loading of arbitrary C extensions. Extensions are imported into the
# active Python interpreter and may run arbitrary code.
unsafe-load-any-extension=no
[MESSAGES CONTROL]
# Only show warnings with the listed confidence levels. Leave empty to show
# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED.
confidence=
# Disable the message, report, category or checker with the given id(s). You
# can either give multiple identifiers separated by comma (,) or put this
# option multiple times (only on the command line, not in the configuration
# file where it should appear only once). You can also use "--disable=all" to
# disable everything first and then reenable specific checks. For example, if
# you want to run only the similarities checker, you can use "--disable=all
# --enable=similarities". If you want to run only the classes checker, but have
# no Warning level messages displayed, use "--disable=all --enable=classes
# --disable=W".
disable=print-statement,
parameter-unpacking,
unpacking-in-except,
old-raise-syntax,
backtick,
long-suffix,
old-ne-operator,
old-octal-literal,
import-star-module-level,
non-ascii-bytes-literal,
raw-checker-failed,
bad-inline-option,
locally-disabled,
file-ignored,
suppressed-message,
useless-suppression,
deprecated-pragma,
use-symbolic-message-instead,
apply-builtin,
basestring-builtin,
buffer-builtin,
cmp-builtin,
coerce-builtin,
execfile-builtin,
file-builtin,
long-builtin,
raw_input-builtin,
reduce-builtin,
standarderror-builtin,
unicode-builtin,
xrange-builtin,
coerce-method,
delslice-method,
getslice-method,
setslice-method,
no-absolute-import,
old-division,
dict-iter-method,
dict-view-method,
next-method-called,
metaclass-assignment,
indexing-exception,
raising-string,
reload-builtin,
oct-method,
hex-method,
nonzero-method,
cmp-method,
input-builtin,
round-builtin,
intern-builtin,
unichr-builtin,
map-builtin-not-iterating,
zip-builtin-not-iterating,
range-builtin-not-iterating,
filter-builtin-not-iterating,
using-cmp-argument,
eq-without-hash,
div-method,
idiv-method,
rdiv-method,
exception-message-attribute,
invalid-str-codec,
sys-max-int,
bad-python3-import,
deprecated-string-function,
deprecated-str-translate-call,
deprecated-itertools-function,
deprecated-types-field,
next-method-defined,
dict-items-not-iterating,
dict-keys-not-iterating,
dict-values-not-iterating,
deprecated-operator-function,
deprecated-urllib-function,
xreadlines-attribute,
deprecated-sys-function,
exception-escape,
comprehension-escape,
invalid-name,
attribute-defined-outside-init
# Enable the message, report, category or checker with the given id(s). You can
# either give multiple identifier separated by comma (,) or put this option
# multiple time (only on the command line, not in the configuration file where
# it should appear only once). See also the "--disable" option for examples.
enable=c-extension-no-member
[REPORTS]
# Python expression which should return a score less than or equal to 10. You
# have access to the variables 'error', 'warning', 'refactor', and 'convention'
# which contain the number of messages in each category, as well as 'statement'
# which is the total number of statements analyzed. This score is used by the
# global evaluation report (RP0004).
evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
# Template used to display messages. This is a python new-style format string
# used to format the message information. See doc for all details.
#msg-template=
# Set the output format. Available formats are text, parseable, colorized, json
# and msvs (visual studio). You can also give a reporter class, e.g.
# mypackage.mymodule.MyReporterClass.
output-format=text
# Tells whether to display a full report or only the messages.
reports=no
# Activate the evaluation score.
score=yes
[REFACTORING]
# Maximum number of nested blocks for function / method body
max-nested-blocks=5
# Complete name of functions that never returns. When checking for
# inconsistent-return-statements if a never returning function is called then
# it will be considered as an explicit return statement and no message will be
# printed.
never-returning-functions=sys.exit
[TYPECHECK]
# List of decorators that produce context managers, such as
# contextlib.contextmanager. Add to this list to register other decorators that
# produce valid context managers.
contextmanager-decorators=contextlib.contextmanager
# List of members which are set dynamically and missed by pylint inference
# system, and so shouldn't trigger E1101 when accessed. Python regular
# expressions are accepted.
generated-members=
# Tells whether missing members accessed in mixin class should be ignored. A
# mixin class is detected if its name ends with "mixin" (case insensitive).
ignore-mixin-members=yes
# Tells whether to warn about missing members when the owner of the attribute
# is inferred to be None.
ignore-none=yes
# This flag controls whether pylint should warn about no-member and similar
# checks whenever an opaque object is returned when inferring. The inference
# can return multiple potential results while evaluating a Python object, but
# some branches might not be evaluated, which results in partial inference. In
# that case, it might be useful to still emit no-member and other checks for
# the rest of the inferred objects.
ignore-on-opaque-inference=yes
# List of class names for which member attributes should not be checked (useful
# for classes with dynamically set attributes). This supports the use of
# qualified names.
ignored-classes=optparse.Values,thread._local,_thread._local,SettingsContainer
# List of module names for which member attributes should not be checked
# (useful for modules/projects where namespaces are manipulated during runtime
# and thus existing member attributes cannot be deduced by static analysis). It
# supports qualified module names, as well as Unix pattern matching.
ignored-modules=
# Show a hint with possible names when a member name was not found. The aspect
# of finding the hint is based on edit distance.
missing-member-hint=yes
# The minimum edit distance a name should have in order to be considered a
# similar match for a missing member name.
missing-member-hint-distance=1
# The total number of similar names that should be taken in consideration when
# showing a hint for a missing member.
missing-member-max-choices=1
# List of decorators that change the signature of a decorated function.
signature-mutators=
[LOGGING]
# The type of string formatting that logging methods do. `old` means using %
# formatting, `new` is for `{}` formatting.
logging-format-style=old
# Logging modules to check that the string format arguments are in logging
# function parameter format.
logging-modules=logging
[SIMILARITIES]
# Ignore comments when computing similarities.
ignore-comments=yes
# Ignore docstrings when computing similarities.
ignore-docstrings=yes
# Ignore imports when computing similarities.
ignore-imports=no
# Minimum lines number of a similarity.
min-similarity-lines=4
[FORMAT]
# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.
expected-line-ending-format=
# Regexp for a line that is allowed to be longer than the limit.
ignore-long-lines=^\s*(# )?<?https?://\S+>?$
# Number of spaces of indent required inside a hanging or continued line.
indent-after-paren=4
# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
# tab).
indent-string=' '
# Maximum number of characters on a single line.
max-line-length=100
# Maximum number of lines in a module.
max-module-lines=1000
# List of optional constructs for which whitespace checking is disabled. `dict-
# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}.
# `trailing-comma` allows a space between comma and closing bracket: (a, ).
# `empty-line` allows space-only lines.
no-space-check=trailing-comma,
dict-separator
# Allow the body of a class to be on the same line as the declaration if body
# contains single statement.
single-line-class-stmt=no
# Allow the body of an if to be on the same line as the test if there is no
# else.
single-line-if-stmt=no
[MISCELLANEOUS]
# List of note tags to take in consideration, separated by a comma.
notes=FIXME,
XXX,
TODO
# Regular expression of note tags to take in consideration.
#notes-rgx=
[VARIABLES]
# List of additional names supposed to be defined in builtins. Remember that
# you should avoid defining new builtins when possible.
additional-builtins=
# Tells whether unused global variables should be treated as a violation.
allow-global-unused-variables=yes
# List of strings which can identify a callback function by name. A callback
# name must start or end with one of those strings.
callbacks=cb_,
_cb
# A regular expression matching the name of dummy variables (i.e. expected to
# not be used).
dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_
# Argument names that match this expression will be ignored. Default to name
# with leading underscore.
ignored-argument-names=_.*|^ignored_|^unused_
# Tells whether we should check for unused import in __init__ files.
init-import=no
# List of qualified module names which can have objects that can redefine
# builtins.
redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io
[BASIC]
# Naming style matching correct argument names.
argument-naming-style=snake_case
# Regular expression matching correct argument names. Overrides argument-
# naming-style.
#argument-rgx=
# Naming style matching correct attribute names.
attr-naming-style=snake_case
# Regular expression matching correct attribute names. Overrides attr-naming-
# style.
#attr-rgx=
# Bad variable names which should always be refused, separated by a comma.
bad-names=foo,
bar,
baz,
toto,
tutu,
tata
# Bad variable names regexes, separated by a comma. If names match any regex,
# they will always be refused
bad-names-rgxs=
# Naming style matching correct class attribute names.
class-attribute-naming-style=any
# Regular expression matching correct class attribute names. Overrides class-
# attribute-naming-style.
#class-attribute-rgx=
# Naming style matching correct class names.
class-naming-style=PascalCase
# Regular expression matching correct class names. Overrides class-naming-
# style.
#class-rgx=
# Naming style matching correct constant names.
const-naming-style=UPPER_CASE
# Regular expression matching correct constant names. Overrides const-naming-
# style.
#const-rgx=
# Minimum line length for functions/classes that require docstrings, shorter
# ones are exempt.
docstring-min-length=-1
# Naming style matching correct function names.
function-naming-style=snake_case
# Regular expression matching correct function names. Overrides function-
# naming-style.
#function-rgx=
# Good variable names which should always be accepted, separated by a comma.
good-names=i,
j,
k,
ex,
Run,
_
# Good variable names regexes, separated by a comma. If names match any regex,
# they will always be accepted
good-names-rgxs=
# Include a hint for the correct naming format with invalid-name.
include-naming-hint=no
# Naming style matching correct inline iteration names.
inlinevar-naming-style=any
# Regular expression matching correct inline iteration names. Overrides
# inlinevar-naming-style.
#inlinevar-rgx=
# Naming style matching correct method names.
method-naming-style=snake_case
# Regular expression matching correct method names. Overrides method-naming-
# style.
#method-rgx=
# Naming style matching correct module names.
module-naming-style=snake_case
# Regular expression matching correct module names. Overrides module-naming-
# style.
#module-rgx=
# Colon-delimited sets of names that determine each other's naming style when
# the name regexes allow several styles.
name-group=
# Regular expression which should only match function or class names that do
# not require a docstring.
no-docstring-rgx=^_
# List of decorators that produce properties, such as abc.abstractproperty. Add
# to this list to register other decorators that produce valid properties.
# These decorators are taken in consideration only for invalid-name.
property-classes=abc.abstractproperty
# Naming style matching correct variable names.
variable-naming-style=snake_case
# Regular expression matching correct variable names. Overrides variable-
# naming-style.
#variable-rgx=
[STRING]
# This flag controls whether inconsistent-quotes generates a warning when the
# character used as a quote delimiter is used inconsistently within a module.
check-quote-consistency=no
# This flag controls whether the implicit-str-concat should generate a warning
# on implicit string concatenation in sequences defined over several lines.
check-str-concat-over-line-jumps=no
[SPELLING]
# Limits count of emitted suggestions for spelling mistakes.
max-spelling-suggestions=4
# Spelling dictionary name. Available dictionaries: none. To make it work,
# install the python-enchant package.
spelling-dict=
# List of comma separated words that should not be checked.
spelling-ignore-words=
# A path to a file that contains the private dictionary; one word per line.
spelling-private-dict-file=
# Tells whether to store unknown words to the private dictionary (see the
# --spelling-private-dict-file option) instead of raising a message.
spelling-store-unknown-words=no
[CLASSES]
# List of method names used to declare (i.e. assign) instance attributes.
defining-attr-methods=__init__,
__new__,
setUp,
__post_init__
# List of member names, which should be excluded from the protected access
# warning.
exclude-protected=_asdict,
_fields,
_replace,
_source,
_make
# List of valid names for the first argument in a class method.
valid-classmethod-first-arg=cls
# List of valid names for the first argument in a metaclass class method.
valid-metaclass-classmethod-first-arg=cls
[DESIGN]
# Maximum number of arguments for function / method.
max-args=5
# Maximum number of attributes for a class (see R0902).
max-attributes=7
# Maximum number of boolean expressions in an if statement (see R0916).
max-bool-expr=5
# Maximum number of branch for function / method body.
max-branches=12
# Maximum number of locals for function / method body.
max-locals=15
# Maximum number of parents for a class (see R0901).
max-parents=7
# Maximum number of public methods for a class (see R0904).
max-public-methods=20
# Maximum number of return / yield for function / method body.
max-returns=6
# Maximum number of statements in function / method body.
max-statements=50
# Minimum number of public methods for a class (see R0903).
min-public-methods=2
[IMPORTS]
# List of modules that can be imported at any level, not just the top level
# one.
allow-any-import-level=
# Allow wildcard imports from modules that define __all__.
allow-wildcard-with-all=no
# Analyse import fallback blocks. This can be used to support both Python 2 and
# 3 compatible code, which means that the block might have code that exists
# only in one or another interpreter, leading to false positives when analysed.
analyse-fallback-blocks=no
# Deprecated modules which should not be used, separated by a comma.
deprecated-modules=optparse,tkinter.tix
# Create a graph of external dependencies in the given file (report RP0402 must
# not be disabled).
ext-import-graph=
# Create a graph of every (i.e. internal and external) dependencies in the
# given file (report RP0402 must not be disabled).
import-graph=
# Create a graph of internal dependencies in the given file (report RP0402 must
# not be disabled).
int-import-graph=
# Force import order to recognize a module as part of the standard
# compatibility libraries.
known-standard-library=
# Force import order to recognize a module as part of a third party library.
known-third-party=enchant
# Couples of modules and preferred modules, separated by a comma.
preferred-modules=
[EXCEPTIONS]
# Exceptions that will emit a warning when being caught. Defaults to
# "BaseException, Exception".
overgeneral-exceptions=BaseException,
Exception

14
LICENCE Normal file
View File

@ -0,0 +1,14 @@
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.

View File

@ -1,7 +1,7 @@
0bin
====
0bin: a client side encrypted pastebin
===========================================
0bin is a client side encrypted pastebin that can run without a database.
0bin is a pastebin that encrypts the user content in the browsder and can run without a database.
* Try it: `0bin.net <http://0bin.net>`_
* `Report a bug <https://github.com/sametmax/0bin/issues>`_
@ -10,7 +10,7 @@
be pasted in it. The idea is that one can (probably...) not be legally entitled
to `moderate the pastebin content`_ as they have no way to decrypt it.
It's an Python implementation of the `zerobin project`_, created by sebsauvage, under the `WTF licence`_.
It's an Python implementation of the `zerobin project`_, created by sebsauvage, under the `WTFPL licence`_.
For now tested with IE9, and the last opera, safari, chrome and FF.
@ -20,7 +20,7 @@ but in short::
pip install zerobin
zerobin
0bin runs on Python 3.7.
0bin runs on Python 3.7+.
How it works
=============
@ -49,70 +49,29 @@ Key points:
Other features
======================
- automatic code coloration (no need to specify);
- automatic code coloration (no need to specify the language);
- pastebin expiration: 1 day, 1 month or never;
- burn after reading: the paste is destroyed after the first reading;
- clone paste: you can't edit a paste, but you can duplicate any of them;
- code upload: if a file is too big, you can upload it instead of using copy/paste;
- copy paste to clipboard in a click;
- get paste short URL in a click;
- own previous pastes history;
- visual hash of a paste to easily tell it apart from others in a list;
- optional command-line tool to encrypt and paste data from shell or scripts.
Technologies used
==================
- Python_
- `The Bottle Python Web microframework`_
- SJCL_ (js crypto tools)
- jQuery_
- Bootstrap_, the Twitter HTML5/CSS3 framework
- VizHash.js_ to create visual hashes from pastes
- Cherrypy_ (server only)
- `node.js`_ (for optional command-line tool only)
- reader mode;
Known issues
============
- 0bin uses several HTML5/CSS3 features that are not widely supported. In that case we handle the degradation as gracefully as we can.
- The "copy to clipboard" feature is buggy under linux. It's flash, so we won't fix it. Better wait for the HTML5 clipboard API to be implemented in major browsers.
- The pasted content size limit check is not accurate. It's just a safety net, so we think it's ok.
- Some url shorteners and other services storing URLs break the encryption key. We will sanitize the URL as much as we can, but there is a limit to what we can do.
What does 0bin not implement?
=================================
* Request throttling. It would be inefficient to do it at the app level, and web servers have robust implementations for it.
* Hash collision prevention: the ratio "probability it happens/consequence seriousness" `is not worth it`_
* Comments: it was initially planed. But comes with a lot of issues so we chose to focus on lower hanging fruits.
.. _moderate the pastebin content: http://www.zdnet.com/blog/security/pastebin-to-hunt-for-hacker-pastes-anonymous-cries-censorship/11336
.. _zerobin project: https://github.com/sebsauvage/ZeroBin/
.. _Python: https://en.wikipedia.org/wiki/Python_(programming_language)
.. _The Bottle Python Web microframework: http://bottlepy.org/
.. _SJCL: http://crypto.stanford.edu/sjcl/
.. _jQuery: http://jquery.com/
.. _Bootstrap: http://twitter.github.com/bootstrap/
.. _VizHash.js: https://github.com/sametmax/VizHash.js
.. _Cherrypy: http://www.cherrypy.org/
.. _node.js: http://nodejs.org/
.. _is not worth it: http://stackoverflow.com/questions/201705/how-many-random-elements-before-md5-produces-collisions
.. _WTF licence: http://en.wikipedia.org/wiki/WTFPL
.. _WTFPL licence: http://en.wikipedia.org/wiki/WTFPL
Contributing
=============
Please fork the project, clone your repository and add the original repo as an upstream remote to keep yours in sync.
For small fixes (typo and such), you can work on master.
For features, you should create a dedicated branch.
In any case, if you modify Javascript or CSS files, you shall run compress.sh afterward to provide the minified files. It requires your to have yui-compressor installed (apt-get install yui-compressor on the debian family).
We don't require you to rebase/merge, ordinary merging is alright.
Once it's ready, just request a PR.
We cannot accept contributions for the moment, and will ignore PR.

View File

@ -1,51 +1,57 @@
#! /bin/bash
COMMAND="yui-compressor";
command -v $COMMAND >/dev/null 2>&1 || { echo >&2 "Error: this script requires the command '$COMMAND' to be available"; exit 1; }
python -c "import scss" || {
echo >&2 "Error: this script requires the scss python module. pip install -r dev-requirements.txt"
exit 1
}
CURDIR=$(dirname $(readlink -f $0));
STATICDIR=$CURDIR'/zerobin/static/'
CSSDIR=$STATICDIR'css/'
JSDIR=$STATICDIR'js/'
command -v "uglifyjs" >/dev/null 2>&1 || {
echo >&2 "Error: this script requires the command 'uglifyjs' to be available."
exit 1
}
MAIN_JS_OUTPUT=$JSDIR"main.min.js";
ADDITIONAL_JS_OUTPUT=$JSDIR"additional.min.js";
CURDIR=$(dirname $(readlink -f $0))
CSSDIR=$CURDIR'/zerobin/static/css/'
JSDIR=$CURDIR'/zerobin/static/js/'
MAIN_JS_OUTPUT=$JSDIR"main.min.js"
CSS_OUTPUT=$CSSDIR"style.min.css"
cat /dev/null > $CSS_OUTPUT;
cat /dev/null >$CSS_OUTPUT
echo "Compressing CSS..."
echo $'\n''/* Bootstrap */' >> $CSS_OUTPUT;
$COMMAND $CSSDIR'bootstrap.min.css' >> $CSS_OUTPUT;
echo $'\n''/* Prettify */' >> $CSS_OUTPUT;
cat $CSSDIR'prettify.css' >> $CSS_OUTPUT;
echo $'\n''/* Custom */' >> $CSS_OUTPUT;
$COMMAND $CSSDIR'style.css' >> $CSS_OUTPUT;
echo $'\n''/* Prettify */' >>$CSS_OUTPUT
python -m scss $CSSDIR'prettify.css' >>$CSS_OUTPUT
rm $CSSDIR'prettify.min.css'
echo $'\n''/* Desert prettify theme */' >>$CSS_OUTPUT
python -m scss $CSSDIR'desert.css' >>$CSS_OUTPUT
rm $CSSDIR'desert.min.css'
echo $'\n''/* Bootswatch bootstrap theme */' >>$CSS_OUTPUT
python -m scss $CSSDIR'bootswatch.4.5.css' >>$CSS_OUTPUT
rm $CSSDIR'bootswatch.4.5.min.css'
echo $'\n''/* Our own CSS */' >>$CSS_OUTPUT
python -m scss $CSSDIR'style.css' >>$CSS_OUTPUT
echo "Compressing JS..."
cat /dev/null > $MAIN_JS_OUTPUT;
cat /dev/null >$MAIN_JS_OUTPUT
echo $'\n''/* Vue */' >>$MAIN_JS_OUTPUT
uglifyjs $JSDIR'vue.js' >>$MAIN_JS_OUTPUT
echo $'\n''/* jQuery */' >> $MAIN_JS_OUTPUT;
cat $JSDIR'jquery-1.7.2.min.js' >> $MAIN_JS_OUTPUT;
# strip the "use strict" statement because it will apply to all the files
# TODO: file a bug report to SJCL to invite them to use the function syntax
echo $'\n''/* SJCL */' >> $MAIN_JS_OUTPUT;
cat $JSDIR'sjcl.js' | sed 's/"use strict";//' >> $MAIN_JS_OUTPUT;
echo $'\n''/* custom */' >> $MAIN_JS_OUTPUT;
$COMMAND $JSDIR'behavior.js' >> $MAIN_JS_OUTPUT;
echo $'\n''/* SJCL */' >>$MAIN_JS_OUTPUT
uglifyjs $JSDIR'sjcl.js' | sed 's/"use strict";//' >>$MAIN_JS_OUTPUT
cat /dev/null > $ADDITIONAL_JS_OUTPUT;
echo $'\n''/* Our own JS */' >>$MAIN_JS_OUTPUT
uglifyjs $JSDIR'behavior.js' >>$MAIN_JS_OUTPUT
echo $'\n''/* jQuery Elastic */' >> $ADDITIONAL_JS_OUTPUT;
$COMMAND $JSDIR'jquery.elastic.source.js' >> $ADDITIONAL_JS_OUTPUT;
echo $'\n''/* lzw */' >> $ADDITIONAL_JS_OUTPUT;
$COMMAND $JSDIR'lzw.js' >> $ADDITIONAL_JS_OUTPUT;
echo $'\n''/* prettify */' >> $ADDITIONAL_JS_OUTPUT;
cat $JSDIR'prettify.min.js' >> $ADDITIONAL_JS_OUTPUT;
echo $'\n''/* ZeroClipboard */' >> $ADDITIONAL_JS_OUTPUT;
$COMMAND $JSDIR'ZeroClipboard.js' >> $ADDITIONAL_JS_OUTPUT;
echo $'\n''/* Prettify */' >>$MAIN_JS_OUTPUT
uglifyjs $JSDIR'prettify.min.js' >>$MAIN_JS_OUTPUT
echo "Done"

2
dev-requirements.txt Normal file
View File

@ -0,0 +1,2 @@
doit==0.32.0
pyScss==1.3.7

46
dodo.py Normal file
View File

@ -0,0 +1,46 @@
from pathlib import Path
import zerobin
ROOT = Path(__file__).absolute().parent
SOURCE_DIR = ROOT / "zerobin/"
DIST_DIR = ROOT / "dist"
def source_files(extensions=None):
extensions = extensions or [".*"]
for ext in extensions:
for file in SOURCE_DIR.rglob(f"*{ext}"):
if (
not file.suffix.endswith("pyc")
and not file.is_dir()
and not "/." in str(file)
):
yield file
def generate_manifest():
extensions = " ".join(set(f"*{f.suffix}" for f in source_files()))
(ROOT / "MANIFEST.in").write_text(f"recursive-include zerobin {extensions}")
def task_compress():
return {
"targets": [
str(SOURCE_DIR / "static/js/main.min.js"),
str(SOURCE_DIR / "static/css/style.min.css"),
],
"file_dep": list(str(f) for f in source_files([".css", ".js"])),
"actions": [str(ROOT / "compress.sh")],
}
def task_build():
return {
"targets": [DIST_DIR / f"zerobin-{zerobin.__version__}-py3-none-any.whl"],
"file_dep": list(str(f) for f in source_files() if ".min." not in str(f)),
"actions": [task_compress, generate_manifest, "python setup.py bdist_wheel",],
}

View File

@ -1,6 +0,0 @@
CherryPy==8.9.0
clize==4.1.1
lockfile==0.12.2
sigtools==2.0.2
bottle==0.12.18
Beaker==1.11.0

Binary file not shown.

Before

Width:  |  Height:  |  Size: 122 KiB

After

Width:  |  Height:  |  Size: 41 KiB

27
setup.cfg Normal file
View File

@ -0,0 +1,27 @@
[metadata]
name = zerobin
version = attr: zerobin.__version__
description = A client side encrypted pastebin
long_description = file: README.rst
author= Sam et Max
keywords = pastebin, encryption,
author_email = lesametlemax@gmail.com
url = https://0bin.net
[options]
zip_safe = False
include_package_data = True
packages = find:
install_requires =
clize==4.1.1
lockfile==0.12.2
bottle==0.12.18
Beaker==1.11.0
Paste==3.4.3
appdirs==1.4.4
bleach==3.1.5
[options.package_data]
* = static/img/*, static/css/*, static/js/*, view/*
hello = *.msg

View File

@ -1,71 +1,8 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# vim: ai ts=4 sts=4 et sw=4 nu
import os
import zerobin
from setuptools import setup, find_packages
src_directory = 'zerobin'
open('MANIFEST.in', 'w').write('\n'.join((
"include *.rst *.tx",
"recursive-include %s *.png *.jpg *.gif *.ico" % src_directory,
"recursive-include %s *.css *.js *.swf" % src_directory,
"recursive-include %s *.tpl" % src_directory
)))
setup(
name="zerobin",
version=zerobin.__version__,
packages=find_packages(exclude=["libs", "libs.*"]),
author="Sam et Max",
author_email="lesametlemax@gmail.com",
description="An client side encrypted pastebin",
long_description=open('README.rst').read(),
install_requires=[
'cherrypy',
'bottle',
'clize',
'lockfile',
],
include_package_data=True,
dependency_links=[
'http://www.subspacefield.org/security/privilege/code/privilege/'
],
classifiers=[
'Programming Language :: Python',
"Intended Audience :: Information Technology",
"License :: OSI Approved :: zlib/libpng License",
"Natural Language :: English",
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3.4",
],
entry_points = {
'console_scripts': [
'zerobin = zerobin.cmd:main',
]
}
)
from setuptools import setup
setup()

View File

@ -1,25 +0,0 @@
zerobinpaste: zerobinpaste.js commander.js ../zerobin/static/js/sjcl.js ../zerobin/static/js/lzw.js
echo '#!/usr/bin/env node' > zerobinpaste
cat commander.js ../zerobin/static/js/sjcl.js ../zerobin/static/js/lzw.js >> zerobinpaste
cat zerobinpaste.js >> zerobinpaste
chmod +x zerobinpaste
ugly: zerobinpaste
uglifyjs=$$(PATH="$$(npm bin):$$PATH" which uglifyjs 2>/dev/null) \
|| { npm install uglify-js; uglifyjs=$$(PATH="$$(npm bin):$$PATH" which uglifyjs); } \
&& sed -i 1d zerobinpaste \
&& $${uglifyjs} -o zerobinpaste.min zerobinpaste \
&& echo '#!/usr/bin/env node' > zerobinpaste \
&& cat zerobinpaste.min >> zerobinpaste \
&& chmod +x zerobinpaste
clean:
rm -f zerobinpaste{,.js,.min}
zerobinpaste.js: zerobinpaste.coffee
coffee=$$(PATH="$$(npm bin):$$PATH" which coffee 2>/dev/null) \
|| { npm install coffee-script; coffee=$$(PATH="$$(npm bin):$$PATH" which coffee); } \
&& $$coffee -c zerobinpaste.coffee
.PHONY: uglify

File diff suppressed because it is too large Load Diff

View File

@ -1,145 +0,0 @@
program
.version('0.0.1')
.usage('[options] [ file ... ]\n\n' + ' Paste contents of file(s) or stdin to 0bin site.')
.option('-u, --url [url]', 'URL of a 0bin site.')
.option('-e, --expire [period]',
'Expiration period - one of: 1_view, 1_day (default), 1_month, never.', '1_day')
.option('-k, --entropy [bytes]',
'Encryption key entropy (and hence length) to use,'\
+ ' in bytes (default: 32).\n'\
+ ' That key will be processed by 1000 pbkdf2-sha256 iterations, not used as-is.', 32)
.option('-c, --config [path]',
'Path to zerobin configuration file (default: ~/.zerobinpasterc).\n'\
+ ' Should be json-file with the same keys as can be specified on the command line.\n'\
+ ' Example contents: {"url": "http://some-0bin.com"}', '~/.zerobinpasterc')
.option('-n, --nocheck', 'do not check SSL certs.')
.parse(process.argv);
[http, https, url, qs, fs, path] = ['http', 'https', 'url', 'querystring', 'fs', 'path'].map(require)
# Parse config file, if any
config = program.config.replace(/^~\/+/, '')
config = path.resolve(process.env.HOME, config)
try
if fs.statSync(config).isFile()
config = JSON.parse(fs.readFileSync(config))
(program[k] = v) for own k, v of config
# Sanity checks and option processing
if not program.url
console.error('ERROR: URL option must be specified.')
process.exit(1)
if program.expire == '1_view'
# "burn_after_reading" is too damn long for cli
program.expire = 'burn_after_reading'
expire_opts = ['burn_after_reading', '1_day', '1_month', 'never']
if program.expire not in expire_opts
console.error(
"ERROR: --expire value (provided: '#{program.expire}')"\
+ ' must be one of: ' + expire_opts.join(', ') + "." )
process.exit(1)
program.entropy = parseInt(program.entropy)
# Generated key will use base64 (6b per char) charset
# Key is not decoded for pbkdf2, so it's generated via base64 here just for convenience
generate_key = (entropy) ->
entropy = Math.ceil(entropy / 8.0) * 8
key = sjcl.bitArray.clamp(
sjcl.random.randomWords(Math.ceil(entropy / 32), 0), entropy )
return sjcl.codec.base64.fromBits(key, 0).replace(/\=+$/, '').replace(/\//, '-')
# Paste one dump and print URL, optionally prefixed with name
paste_file = (content, name) ->
content = sjcl.codec.utf8String.toBits(content)
content = sjcl.codec.base64.fromBits(content)
# content = lzw.compress(content)
key = generate_key(program.entropy)
content = sjcl.encrypt(key, content)
content = qs.stringify
content: content
expiration: program.expire
# host.com -> http://host.com
if not program.url.match(/^https?:\/\//)
program.url = 'http://' + program.url.replace(/^\/+/, '')
proto = http
if program.url.match(/^https:\/\//)
proto = https
req_opts = url.parse(program.url)
req_opts.method = 'POST'
req_opts.headers =
'Content-Type': 'application/x-www-form-urlencoded'
'Content-Length': content.length
req_url_base = req_opts.path
.replace(/\/paste\/create\/?$/, '').replace(/\/+$/, '')
req_opts.path = req_url_base + '/paste/create'
if program.nocheck
req_opts.rejectUnauthorized = false
req = proto.request req_opts, (res) ->
req_reply = ''
res.setEncoding('utf8')
res.on 'data', (chunk) -> req_reply += chunk
res.on 'end', ->
req_reply = JSON.parse(req_reply)
if req_reply.status != 'ok'
console.error("ERROR: failure posting #{name} - " + req_reply.message)
return
req_opts.pathname = req_url_base + '/paste/' + req_reply.paste
req_opts.hash = key
paste = url.format(req_opts)
console.log(if name then "#{name} #{paste}" else paste)
req.write(content)
req.end()
req.on 'error', (e) -> console.error(e)
# Seed sjcl prng from /dev/(u)random
do (bytes=64) ->
for src in ['/dev/urandom', '/dev/random', null]
break if not src or fs.existsSync(src)
if not src
console.error( 'ERROR: Failed to seed PRNG -'\
+ ' /dev/(u)random is unavailable, relying only on sjcl entropy sources' )
return
fd = fs.openSync(src, 'r')
buff = new Buffer(bytes)
fs.readSync(fd, buff, 0, bytes)
fs.closeSync(fd)
sjcl.random.addEntropy(
(buff.readUInt32BE(n) for n in [0..bytes/4]), bytes * 8, src )
# Loop over file args or read stdin
if not program.args or not program.args.length
process.stdin.resume()
process.stdin.setEncoding('utf8')
stdin_data = ''
process.stdin.on 'data', (chunk) -> stdin_data += chunk
process.stdin.on 'end', -> paste_file(stdin_data)
else
for file in program.args
paste_file( fs.readFileSync(file, 'utf8'),
if program.args.length > 1 then path.basename(file) else null )

View File

@ -1,6 +0,0 @@
#!/usr/bin/env python
# coding: utf-8
from zerobin.cmd import main
main()

View File

@ -1,5 +1,5 @@
from pathlib import Path
__version__ = "1.0.0"
__version__ = "1.0.1"
ROOT_DIR = Path(__file__).absolute().parent

5
zerobin/__main__.py Normal file
View File

@ -0,0 +1,5 @@
#!/usr/bin/env python
from zerobin.cli import main
main()

221
zerobin/cli.py Normal file
View File

@ -0,0 +1,221 @@
#!/usr/bin/env python3
"""
Main script including runserver and delete-paste.
"""
import sys
import re
import os
from distutils.util import strtobool
import zerobin
from zerobin.utils import (
settings,
SettingsValidationError,
ensure_app_context,
hash_password,
)
from zerobin.routes import get_app
from zerobin.paste import Paste
from bottle import run
import clize
def runserver(
*,
host="",
port="",
config_dir="",
data_dir="",
debug=None,
version=False,
server="paste",
):
if version:
print("0bin V%s" % settings.VERSION)
sys.exit(0)
try:
if debug is not None:
debug = strtobool(debug)
updated_settings, app = get_app(
debug=debug, config_dir=config_dir, data_dir=data_dir,
)
except SettingsValidationError as err:
print("Configuration error: %s" % err, file=sys.stderr)
sys.exit(1)
updated_settings.HOST = host or os.environ.get(
"ZEROBIN_HOST", updated_settings.HOST
)
updated_settings.PORT = port or os.environ.get(
"ZEROBIN_PORT", updated_settings.PORT
)
if updated_settings.DEBUG:
print(
f"Admin URL for dev: http://{updated_settings.HOST}:{updated_settings.PORT}{settings.ADMIN_URL}"
)
print()
run(
app,
host=updated_settings.HOST,
port=updated_settings.PORT,
reloader=True,
server=server,
)
else:
run(app, host=settings.HOST, port=updated_settings.PORT, server=server)
# The regex parse the url and separate the paste's id from the decription key
# After the '/paste/' part, there is several caracters, identified as
# the uuid of the paste. Followed by a '#', the decryption key of the paste.
paste_url = re.compile("/paste/(?P<paste_id>.*)#(?P<key>.*)")
def unpack_paste(paste):
"""Take either the ID or the URL of a paste, and return its ID"""
try_url = paste_url.search(paste)
if try_url:
return try_url.group("paste_id")
return paste
def delete_paste(*pastes, quiet=False):
"""
Remove pastes, given its ID or its URL
quiet: Don't print anything
pastes: List of pastes, given by ID or URL
"""
for paste_uuid in map(unpack_paste, pastes):
try:
Paste.load(paste_uuid).delete()
if not quiet:
print("Paste {} is removed".format(paste_uuid))
except ValueError:
if not quiet:
print("Paste {} doesn't exist".format(paste_uuid))
def infos():
""" Print the route to the 0bin admin.
The admin route is generated by zerobin so that bots won't easily
bruteforce it. To get the full URL, simply preppend your website domain
name to it.
E.G:
If this command prints:
"/admin/f1cc3972a4b933c734b37906940cf69886161492ee4eb7c1faff5d7b5e92efb8"
Then the admin url is:
"http://yourdomain.com/admin/f1cc3972a4b933c734b37906940cf69886161492ee4eb7c1faff5d7b5e92efb8"
Adapt "http" and "yourdomain.com" to your configuration.
In debug mode, the dev server will print the url when starting.
"""
ensure_app_context()
print(f"Zerobin version: {zerobin.__version__}")
print(f"Admin URL (to moderate pastes): {settings.ADMIN_URL}")
print(f"Data dir (pastes and counter): {settings.DATA_DIR}")
print(
f"Config dir (config file, secret key, admin password and custom views): {settings.CONFIG_DIR}"
)
print(
f"Static files dir (to configure apache, nging, etc.): {settings.STATIC_FILES_ROOT}"
)
def set_admin_password(password):
""" Set the password for the admin
It will be stored as a scrypt hash in a file in the var dir.
"""
ensure_app_context()
settings.ADMIN_PASSWORD_FILE.write_bytes(hash_password(password))
def clean_expired_pastes(
*, dry_run=False, verbose=False, config_dir="", data_dir="",
):
""" Clean expired file pastes and empty paste directories
This features delete files from the data dir, make sure it's safe.
Use "dry_run" and "verbose" options to check first
"""
ensure_app_context(config_dir=config_dir, data_dir=data_dir)
print("Deleting expired pastes...")
i = 0
for p in Paste.iter_all():
if p.has_expired:
if not dry_run:
p.delete()
if verbose:
print(p.path, "has expired")
i += 1
if dry_run:
print(f"{i} pastes would have been deleted")
else:
print(f"{i} pastes deleted")
print("Deleting empty paste directories...")
i = 0
for p in settings.DATA_DIR.rglob("*"):
try:
if p.is_dir() and not next(p.iterdir(), None):
if not dry_run:
p.rmdir()
if verbose:
print(p, "is empty")
i += 1
except OSError as e:
print(f'Error while processing "{p}: {e}')
if dry_run:
print(f"{i} directories would have been deleted")
else:
print(f"{i} directories deleted")
print("Done")
def main():
subcommands = [
runserver,
delete_paste,
infos,
set_admin_password,
clean_expired_pastes,
]
subcommand_names = [
clize.util.name_py2cli(name)
for name in clize.util.dict_from_names(subcommands).keys()
]
if len(sys.argv) < 2 or sys.argv[1] not in subcommand_names:
sys.argv.insert(1, subcommand_names[0])
clize.run(runserver, delete_paste, infos, set_admin_password, clean_expired_pastes)

View File

@ -1,155 +0,0 @@
#!/usr/bin/env python3
"""
Main script including runserver and delete-paste.
"""
import sys
import re
import hashlib
import _thread as thread
from zerobin.utils import (
settings,
SettingsValidationError,
drop_privileges,
ensure_var_env,
hash_password,
)
from zerobin.routes import get_app
from zerobin.paste import Paste
from bottle import run
import clize
def runserver(
*,
host="",
port="",
debug=None,
user="",
group="",
settings_file="",
compressed_static=None,
version=False,
paste_id_length=None,
server="cherrypy",
):
if version:
print("0bin V%s" % settings.VERSION)
sys.exit(0)
settings.HOST = host or settings.HOST
settings.PORT = port or settings.PORT
settings.USER = user or settings.USER
settings.GROUP = group or settings.GROUP
settings.PASTE_ID_LENGTH = paste_id_length or settings.PASTE_ID_LENGTH
settings.DEBUG = bool(debug) if debug is not None else settings.DEBUG
ensure_var_env()
try:
_, app = get_app(debug, settings_file, compressed_static, settings=settings)
except SettingsValidationError as err:
print("Configuration error: %s" % err.message, file=sys.stderr)
sys.exit(1)
thread.start_new_thread(drop_privileges, (settings.USER, settings.GROUP))
if settings.DEBUG:
print(f"Admin URL: http://{settings.HOST}:{settings.PORT}{settings.ADMIN_URL}")
run(
app, host=settings.HOST, port=settings.PORT, reloader=True, server=server,
)
else:
run(app, host=settings.HOST, port=settings.PORT, server=server)
# The regex parse the url and separate the paste's id from the decription key
# After the '/paste/' part, there is several caracters, identified as
# the uuid of the paste. Followed by a '#', the decryption key of the paste.
paste_url = re.compile("/paste/(?P<paste_id>.*)#(?P<key>.*)")
def unpack_paste(paste):
"""Take either the ID or the URL of a paste, and return its ID"""
try_url = paste_url.search(paste)
if try_url:
return try_url.group("paste_id")
return paste
def delete_paste(*pastes, quiet=False):
"""
Remove pastes, given its ID or its URL
quiet: Don't print anything
pastes: List of pastes, given by ID or URL
"""
for paste_uuid in map(unpack_paste, pastes):
try:
Paste.load(paste_uuid).delete()
if not quiet:
print("Paste {} is removed".format(paste_uuid))
except ValueError:
if not quiet:
print("Paste {} doesn't exist".format(paste_uuid))
def print_admin_url():
""" Print the route to the 0bin admin.
The admin route is generated by zerobin so that bots won't easily
bruteforce it. To get the full URL, simply preppend your website domain
name to it.
E.G:
If this command prints:
"/admin/f1cc3972a4b933c734b37906940cf69886161492ee4eb7c1faff5d7b5e92efb8"
Then the admin url is:
"http://yourdomain.com/admin/f1cc3972a4b933c734b37906940cf69886161492ee4eb7c1faff5d7b5e92efb8"
Adapt "http" and "yourdomain.com" to your configuration.
In debug mode, the dev server will print the url when starting.
"""
ensure_var_env()
print(settings.ADMIN_URL)
def set_admin_password(password):
""" Set the password for the admin
It will be stored as a scrypt hash in a file in the var dir.
"""
ensure_var_env()
settings.ADMIN_PASSWORD_FILE.write_bytes(hash_password(password))
def main():
subcommands = [runserver, delete_paste, print_admin_url, set_admin_password]
subcommand_names = [
clize.util.name_py2cli(name)
for name in clize.util.dict_from_names(subcommands).keys()
]
if len(sys.argv) < 2 or sys.argv[1] not in subcommand_names:
sys.argv.insert(1, subcommand_names[0])
clize.run(runserver, delete_paste, print_admin_url, set_admin_password)

View File

@ -1,70 +1,33 @@
from zerobin import ROOT_DIR
# Get error messages and auto reload.
# Don't set this to True in production
DEBUG = True
# Path to the directory that will contains all variable content, such
# as pastes, the secret key, etc
VAR_DIR = ROOT_DIR.parent / "var"
# Port and host for the embedded python server
HOST = "127.0.0.1"
PORT = "3255"
# debug will get you error messages and auto reload
# don't set this to True in production
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 = ROOT_DIR / "static"
# If True, will link the compressed verion of the js and css files,
# otherwise, will use the ordinary files
COMPRESSED_STATIC_FILES = False
# 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, it needs to be it a directory at the begining of
# this tuple. By default, custom_views is meant for that purpose.
TEMPLATE_DIRS = (
VAR_DIR / "custom_views",
ROOT_DIR / "views",
# Names/links to insert in the footer.
#
MENU = (
("Create paste", "/"),
("Github", "https://github.com/Tygs/0bin"),
("Faq", "/faq/"), # You probably want to keep this
# Any link with "mailto:" will be escaped to limit spam, but displayed
# correctly to the user using JS.
("Contact", "mailto:your@email.com"),
("Zerobin Pastebin", "https://www.0bin.net/"), # Thanks the authors :)
)
# Port and host the embeded python server should be using
# You can also specify them using the --host and --port script options
# which have priority on these settings
HOST = "127.0.0.1"
PORT = "8000"
# User and group the server should run as. Set to None if it should be the
# current user. Some OS don't support it and if so, it will be ignored.
USER = None
GROUP = None
# Size limit of the paste content in bytes. Be careful, allowing a size too big can
# slow down the user's browser
MAX_SIZE = 1024 * 600
# Display a tiny counter for pastes created.
# Be carreful if your site have to many pastes this can hurt your hard drive performances.
# Refresh counter interval. Default to every minute after a paste.
DISPLAY_COUNTER = True
REFRESH_COUNTER = 60 * 1 # Fill this if you want to
ADMIN_CREDENTIALS = {
"username": None,
"password": None,
}
# Refresh counter interval.
REFRESH_COUNTER = 60 # in seconds
# Names/links to insert in the menu bar.
# Any link with "mailto:" will be escaped to prevent spam
MENU = (
("Home", "/"), # internal link. First link will be highlited
("Download 0bin", "https://github.com/sametmax/0bin"), # external link
("Faq", "/faq/"), # faq
("Contact", "mailto:your@email.com"), # email
)
# limit size of pasted text in bytes. Be careful allowing too much size can
# slow down user's browser
MAX_SIZE = 1024 * 500
# length of base64-like paste-id string in the url, int from 4 to 27 (length of sha1 digest)
# Length of the paste-id string in the url, int from 4 to 27 (length of sha1 digest)
# total number of unique pastes can be calculated as 2^(6*PASTE_ID_LENGTH)
# for PASTE_ID_LENGTH=8, for example, it's 2^(6*8) = 281 474 976 710 656
PASTE_ID_LENGTH = 8

View File

@ -1,15 +1,14 @@
# coding: utf-8
from __future__ import unicode_literals, absolute_import
import os
import hashlib
import base64
import lockfile
import json
from datetime import datetime, timedelta
from zerobin.utils import settings, to_ascii, as_unicode, safe_open as open
import bleach
from zerobin.utils import settings
class Paste(object):
@ -18,7 +17,7 @@ class Paste(object):
calculation of the expiration date.
"""
DIR_CACHE = set()
DIR_CACHE: set = set()
DURATIONS = {
"1_day": 24 * 3600,
@ -26,10 +25,20 @@ class Paste(object):
"never": 365 * 24 * 3600 * 100,
}
def __init__(self, uuid=None, uuid_length=None, content=None, expiration=None):
def __init__(
self,
uuid=None,
uuid_length=None,
content=None,
expiration=None,
title="",
btc_tip_address="",
):
self.content = content
self.expiration = self.get_expiration(expiration)
self.title = bleach.clean(title, strip=True)[:60]
self.btc_tip_address = bleach.clean(btc_tip_address, strip=True)[:50]
if not uuid:
# generate the uuid from the decoded content by hashing it
@ -97,15 +106,25 @@ class Paste(object):
uuid = os.path.basename(path)
expiration = next(paste).strip()
content = next(paste).strip()
try:
metadata = json.loads(next(paste).strip())
except (StopIteration, json.decoder.JSONDecodeError):
metadata = {}
if "burn_after_reading" not in expiration:
expiration = datetime.strptime(expiration, "%Y-%m-%d %H:%M:%S.%f")
except StopIteration:
raise TypeError(to_ascii("File %s is malformed" % path))
raise TypeError("File %s is malformed" % path)
except (IOError, OSError):
raise ValueError(to_ascii("Can not open paste from file %s" % path))
raise ValueError("Can not open paste from file %s" % path)
return Paste(uuid=uuid, expiration=expiration, content=content)
return Paste(
uuid=uuid,
expiration=expiration,
content=content,
title=" ".join(metadata.get("title", "").split()),
btc_tip_address=" ".join(metadata.get("btc_tip_address", "").split()),
)
@classmethod
def load(cls, uuid):
@ -123,9 +142,8 @@ class Paste(object):
"""
path = settings.PASTE_FILES_ROOT
counter_file = os.path.join(path, "counter")
lock = lockfile.LockFile(counter_file)
with lock:
with lockfile.LockFile(counter_file):
# Read the value from the counter
try:
with open(counter_file, "r") as fcounter:
@ -170,12 +188,18 @@ class Paste(object):
if "burn_after_reading" == self.expiration:
expiration = self.expiration + "#%s" % datetime.now() # TODO: use UTC dates
else:
expiration = as_unicode(self.expiration)
expiration = str(self.expiration)
# write the paste
with open(self.path, "w") as f:
f.write(expiration + "\n")
f.write(self.content + "\n")
metadata = {}
if self.title:
metadata["title"] = self.title
if self.btc_tip_address:
metadata["btc_tip_address"] = self.btc_tip_address
f.write(json.dumps(metadata) + "\n")
return self
@ -228,3 +252,19 @@ class Paste(object):
Delete the paste file.
"""
os.remove(self.path)
@classmethod
def iter_all(cls, on_error=lambda e: e):
for p in settings.PASTE_FILES_ROOT.rglob("*"):
if p.is_file() and "counter" not in str(p):
try:
yield Paste.load_from_file(p)
except (TypeError, ValueError) as e:
on_error(e)
@property
def has_expired(self):
try:
return self.expiration < datetime.now()
except TypeError:
return False

View File

@ -1,433 +0,0 @@
#! /usr/bin/python
"""
privilege.py
Copyright (c) 2009-2010, Travis H.
License terms: Same as those of Python itself.
This module is designed to implement the privilege-dropping API
described here:
http://www.cs.berkeley.edu/~daw/papers/setuid-usenix02.pdf
http://www.cs.berkeley.edu/~daw/papers/setuid-login08b.pdf
This topic is complex so you should read at least the second paper
before trying to understand why I'm doing this.
NOTE: This code does not (yet) attempt to work on Solaris and AIX.
That is, you must have getres[ug]id and setres[ug]id to use it.
TODO:
I have added setres[ug]id to python; unsure of when it will
appear; make a future version of this will check for its presence.
Make interface more OO, less imperative.
Implement drop_priv_temp, restore_priv.
Implement on systems without setres[ug]id
Contributors:
Kevin Gilette
"""
from ctypes import *
from ctypes.util import find_library
import os
import pwd
import grp
# Get the name of the Operating System
os_kernel = os.uname()[0]
class PrivilegeFail(Exception):
pass
clib = CDLL(find_library("c"))
# Tested on x86-64 -- all tests pass when run as root
__uid_t = c_uint
__gid_t = c_uint
_getresuid = clib.getresuid
def getresuid():
"""Get the real, effective, and saved user IDs"""
# Create some memory locations for getresuid to write to.
r = __uid_t()
e = __uid_t()
s = __uid_t()
# Call getresuid syscall, passing by reference.
res = _getresuid(byref(r), byref(e), byref(s))
if res < 0: raise pythonapi.PyErr_SetFromErrno(py_object(OSError))
# Convert to python integers.
return r.value, e.value, s.value
_getresgid = clib.getresgid
def getresgid():
"""Get the real, effective, and saved group IDs"""
r = __gid_t()
e = __gid_t()
s = __gid_t()
# Call getresgid syscall, passing by reference.
res = _getresgid(byref(r), byref(e), byref(s))
if res < 0: raise pythonapi.PyErr_SetFromErrno(py_object(OSError))
# Convert to python integers.
return r.value, e.value, s.value
# Import the setresuid system call using ctypes
_setresuid = clib.setresuid
_setresuid.argttypes = [__uid_t, __uid_t, __uid_t]
_setresuid.resttype = c_int
# Import the setresgid system call using ctypes
_setresgid = clib.setresgid
_setresgid.argttypes = [__gid_t, __gid_t, __gid_t]
_setresgid.resttype = c_int
def setresuid(ruid, euid, suid):
"""Set the real, effective, and saved user IDs"""
res = _setresuid(__uid_t(ruid), __uid_t(euid), __uid_t(suid))
if res < 0: raise pythonapi.PyErr_SetFromErrno(py_object(OSError))
def setresgid(rgid, egid, sgid):
"""Set the real, effecive, and saved group IDs"""
res = _setresgid(__gid_t(rgid), __gid_t(egid), __gid_t(sgid))
if res < 0: raise pythonapi.PyErr_SetFromErrno(py_object(OSError))
def sort_uniq(args):
"""Sort a sequence, discarding duplicates."""
return sorted(set(args))
class user_credentials:
"""
This represents the credentials associated with a user.
User ID
Group ID
Supplementary Groups
This is used as an argument to drop_permanently
"""
def __init__(self, uid, gid, sups):
# -1 has special meaning for several set*id calls (ignore)
if uid == -1: raise PrivilegeFail
if gid == -1: raise PrivilegeFail
nm = os.sysconf('SC_NGROUPS_MAX')
if nm < 0 or nm < len(sups): raise PrivilegeFail
self.uid = uid
self.gid = gid
self.sups = sort_uniq(sups)
def eql_sups(current, target):
"""
Compare two supplementary group lists, and ignore if effective GID is in current but not target.
Prerequisite: The supplementary group lists are sorted and filtered for duplicates.
"""
egid = os.getegid()
my_current = sort_uniq(current)
# Instead of tediously ignoring this value, if it's in the current list, then go ahead
# and add it to the target list
if egid in current:
my_target = target + [ egid ]
else:
my_target = target
my_target = sort_uniq(my_target)
return my_current == my_target
def get_sups():
"""This is here to give us a layer of abstraction relative to system calls"""
return os.getgroups()
def set_sups(target_sups):
"""
This is designed to give us a layer of abstraction from the system calls.
It also accommodates FreeBSD's idiosyncrasy (which is POSIX-compliant) of
keeping the egid in the supplementary groups list.
It also makes an effort to not call the setgroups routine if the target
group list is identical to the current one in force.
"""
global os_kernel
if os_kernel == 'FreeBSD':
target_sups = [ os.getegid() ] + target_sups
if os.geteuid() == 0:
# This will raise an OSError exception if it fails
os.setgroups(target_sups)
else:
cur_sups = get_sups()
# This will probably fail
if not eql_sups(cur_sups, target_sups):
# This will raise an OSError exception if it fails
os.setgroups(target_sups)
return True
def set_gids(r, e, s):
"""This is here to give us a layer of abstraction relative to system calls"""
setresgid(r, e, s)
def set_uids(r, e, s):
"""This is here to give us a layer of abstraction relative to system calls"""
setresuid(r, e, s)
class res_ids:
"""
This represents the three IDs (group or user) associated with a process.
"""
def __init__(self, real, effective, saved):
self.r = real
self.e = effective
self.s = saved
class proc_credentials:
"""
This obtains and represents the credentials associated with a process.
"""
def __init__(self):
self.uids = apply(res_ids, getresuid())
self.gids = apply(res_ids, getresgid())
self.sups = sort_uniq(os.getgroups())
def get_fs_ids():
"""Get filesystem IDs - applies only to Linux"""
uid = None
gid = None
file = open('/proc/self/status', 'r')
for line in file:
fields = line.split()
if fields[0] == 'Uid:':
uid = int(fields[4])
elif fields[0] == 'Gid:':
gid = int(fields[4])
return uid, gid
def coerce_user(user):
if hasattr(user, '__int__'):
return int(user)
return pwd.getpwnam(user).pw_uid
def coerce_group(group):
if hasattr(group, '__int__'):
return int(group)
return grp.getgrnam(group).gr_gid
def drop_privileges_permanently(uid, gid, sups):
"""
This routine is designed to permanently drop all privileges to the
user, group, and supplementary groups specified.
"""
uid = coerce_user(uid)
gid = coerce_group(gid)
sups = map(coerce_group, sups)
# This does some syntax checking
ucred = user_credentials(uid, gid, sups)
# This is for our convenience
u = uid
g = gid
# Order is important in these three calls
set_sups(ucred.sups)
set_gids(g, g, g) # real, effective, saved
set_uids(u, u, u) # real, effective, saved
# Check that we actually did what we expected or throw exception.
pc = proc_credentials()
# Portably compare the supplementary group list
if not eql_sups(pc.sups, ucred.sups): raise PrivilegeFail
# Check all the gids
if not (g == pc.gids.r and g == pc.gids.e and g == pc.gids.s):
raise PrivilegeFail
# Check all the uids
if not (u == pc.uids.r and u == pc.gids.e and u == pc.uids.s):
raise PrivilegeFail
global os_kernel
if os_kernel == 'Linux':
if get_fs_ids() != (u, g): raise PrivilegeFail
# This is all test code
# It is run if this script is invoked directly
if __name__ == '__main__':
import unittest
class test_getresXid(unittest.TestCase):
"""Test the calls to getresXid"""
def test__getresuid(self):
# TODO: is there any way to avoid redefining this here? I tried a global but it didn't
# work.
# Note: the double-leading underscore may be getting special-cased by python.
__uid_t = c_int
r = __uid_t()
e = __uid_t()
s = __uid_t()
ret = _getresuid(byref(r), byref(e), byref(s))
self.assertEqual(ret, 0)
self.assertEqual(r.value, os.getuid())
self.assertEqual(e.value, os.geteuid())
# NOTE: no other portable way to get saved UID
def test_getresuid(self):
(r, e, s) = getresuid()
self.assertEqual(r, os.getuid())
self.assertEqual(e, os.geteuid())
# NOTE: no other portable way to get saved UID
def test__getresgid(self):
__gid_t = c_int
r = __gid_t()
e = __gid_t()
s = __gid_t()
ret = _getresgid(byref(r), byref(e), byref(s))
self.assertEqual(ret, 0)
self.assertEqual(r.value, os.getgid())
self.assertEqual(e.value, os.getegid())
# NOTE: no other portable way to get saved GID
def test_getresgid(self):
(r, e, s) = getresgid()
self.assertEqual(r, os.getgid())
self.assertEqual(e, os.getegid())
# NOTE: no other portable way to get saved GID
class test_setresuid(unittest.TestCase):
"""Test the call to setresuid"""
def setUp(self):
self.uid = os.geteuid()
def test__setresuid(self):
__uid_t = c_int
r1 = __uid_t(1)
e1 = __uid_t(1)
# Must save root UID so that we can reset UIDs for other tests
s1 = __uid_t(0)
if self.uid == 0:
rv = _setresuid(r1, e1, s1)
self.assertEqual(rv, 0)
(r2, e2, s2) = getresuid()
self.assertEqual(r1.value, r2)
self.assertEqual(e1.value, e2)
self.assertEqual(s1.value, s2)
else:
rv = _setresuid(r1, e1, s1)
self.assertEqual(rv, -1)
def test_setresuid(self):
if self.uid == 0:
setresuid(1,1,0)
(r, e, s) = getresuid()
self.assertEqual(r, 1)
self.assertEqual(e, 1)
self.assertEqual(s, 0)
else:
self.assertRaises(OSError, setresuid, 1, 1, 1)
def tearDown(self):
if self.uid == 0:
# Restore UIDs for next test
_setresuid(0, 0, 0)
class test_setresgid(unittest.TestCase):
"""Test the call to setresgid"""
def setUp(self):
self.uid = os.geteuid()
def test__setresgid(self):
__gid_t = c_int
r1 = __gid_t(1)
e1 = __gid_t(1)
s1 = __gid_t(1)
if self.uid == 0:
rv = _setresgid(r1, e1, s1)
self.assertEqual(rv, 0)
(r2, e2, s2) = getresgid()
self.assertEqual(r1.value, r2)
self.assertEqual(e1.value, e2)
self.assertEqual(s1.value, s2)
else:
rv = _setresgid(r1, e1, s1)
self.assertEqual(rv, -1)
def test_setresgid(self):
if self.uid == 0:
setresgid(1,1,1)
(r, e, s) = getresgid()
self.assertEqual(r, 1)
self.assertEqual(e, 1)
self.assertEqual(s, 1)
else:
self.assertRaises(OSError, setresgid, 1, 1, 1)
class test_sort_uniq(unittest.TestCase):
def test_sort_uniq(self):
l = [ 'c', 'a', 'b', 'a' ]
self.assertEqual(sort_uniq(l), [ 'a', 'b', 'c'])
class test_user_credentials(unittest.TestCase):
def test_negatives(self):
self.assertRaises(PrivilegeFail, user_credentials, -1, 0, [])
self.assertRaises(PrivilegeFail, user_credentials, 0, -1, [])
uc = user_credentials(0, 0, [0, 1])
class test_eql_sups(unittest.TestCase):
def test_equal(self):
self.assert_(eql_sups([0, 1, 2], [0, 1, 2]))
def test_contains_egid(self):
self.assert_(eql_sups(sort_uniq([0, 1, 2, os.getegid()]), [0, 1, 2]))
self.assert_(eql_sups(sort_uniq([0, 1, os.getegid(), 9999]), [0, 1, 9999]))
class test_get_sups(unittest.TestCase):
def test(self):
self.assertEqual(os.getgroups(), get_sups())
class test_set_sups(unittest.TestCase):
def test_equal(self):
sups = os.getgroups()
set_sups(sups)
self.assertEqual(sups, os.getgroups())
def test_unequal(self):
old_sups = os.getgroups()
sups = [ 1, 2, 3 ]
if os.geteuid() == 0:
set_sups(sups)
self.assert_(eql_sups(os.getgroups(), sups))
# Clean up by resetting supplementary groups
set_sups(old_sups)
else:
self.assertRaises(OSError, set_sups, sups)
class test_res_ids(unittest.TestCase):
def test_res_ids(self):
ids = res_ids(1, 2, 3)
self.assertEqual(ids.r, 1)
self.assertEqual(ids.e, 2)
self.assertEqual(ids.s, 3)
class test_proc_credentials(unittest.TestCase):
def test_pc(self):
pc = proc_credentials()
self.assertEqual(pc.uids.r, os.getuid())
self.assertEqual(pc.uids.e, os.geteuid())
self.assertEqual(pc.uids.s, (getresuid())[2])
self.assertEqual(pc.gids.r, os.getgid())
self.assertEqual(pc.gids.e, os.getegid())
self.assertEqual(pc.gids.s, (getresgid())[2])
self.assertEqual(pc.sups, sort_uniq(os.getgroups()))
class test_get_fs_ids(unittest.TestCase):
def test_get_fs_ids(self):
uid, gid = get_fs_ids()
self.assertEqual(uid, os.getuid())
self.assertEqual(gid, os.getgid())
class test_drop_privs(unittest.TestCase):
def test_drop_privs(self):
"""This test must be run last"""
if os.geteuid() == 0:
drop_privileges_permanently(1, 1, [1])
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(test_getresXid))
suite.addTest(unittest.makeSuite(test_setresuid))
suite.addTest(unittest.makeSuite(test_setresgid))
suite.addTest(unittest.makeSuite(test_sort_uniq))
suite.addTest(unittest.makeSuite(test_user_credentials))
suite.addTest(unittest.makeSuite(test_eql_sups))
suite.addTest(unittest.makeSuite(test_set_sups))
suite.addTest(unittest.makeSuite(test_res_ids))
suite.addTest(unittest.makeSuite(test_proc_credentials))
suite.addTest(unittest.makeSuite(test_get_fs_ids))
suite.addTest(unittest.makeSuite(test_drop_privs))
unittest.TextTestRunner().run(suite)

View File

@ -3,30 +3,36 @@
"""
import os
import sys
import _thread as thread
from distutils.util import strtobool
import urllib.parse as urlparse
from urllib.parse import urlparse
from datetime import datetime, timedelta
import bottle
from bottle import Bottle, static_file, view, request, HTTPResponse, redirect, abort
from bottle import (
Bottle,
static_file,
view,
request,
HTTPResponse,
redirect,
)
from beaker.middleware import SessionMiddleware
from zerobin import __version__
from zerobin.utils import (
settings,
SettingsValidationError,
ensure_var_env,
ensure_app_context,
check_password,
settings,
)
from zerobin.paste import Paste
ensure_var_env()
ensure_app_context()
GLOBAL_CONTEXT = {
@ -37,15 +43,9 @@ GLOBAL_CONTEXT = {
}
app = SessionMiddleware(
Bottle(),
{
"session.type": "file",
"session.cookie_expires": 300,
"session.data_dir": settings.SESSIONS_DIR,
"session.auto": True,
},
)
app = Bottle()
ADMIN_LOGIN_URL = settings.ADMIN_URL + "login/"
@app.route("/")
@ -54,111 +54,111 @@ def index():
return GLOBAL_CONTEXT
@app.route("/faq/")
@app.get("/faq/")
@view("faq")
def faq():
return GLOBAL_CONTEXT
@app.route("/buy_bitcoin")
@view("buy_bitcoin")
def index():
return GLOBAL_CONTEXT
@app.route(settings.ADMIN_URL, method="GET")
@app.get(settings.ADMIN_URL)
@app.post(settings.ADMIN_URL)
@view("admin")
def admin():
session = request.environ.get("beaker.session")
if not session or not session.get("is_authenticated"):
redirect(settings.ADMIN_URL + "/login")
redirect(ADMIN_LOGIN_URL)
return {}
paste_id = request.forms.get("paste", "")
if paste_id:
try:
if "/paste/" in paste_id:
paste_id = urlparse(paste_id).path.split("/paste/")[-1]
paste = Paste.load(paste_id)
paste.delete()
except (TypeError, ValueError, FileNotFoundError):
return {
"status": "error",
"message": f"Cannot find paste '{paste_id}'",
**GLOBAL_CONTEXT,
}
return {"status": "ok", "message": "Paste deleted", **GLOBAL_CONTEXT}
return {"status": "ok", "message": "", **GLOBAL_CONTEXT}
@app.route(settings.ADMIN_URL + "/:paste_id", method="DELETE")
@view("admin")
def delete_paste_from_admin(paste_id):
session = request.environ.get("beaker.session")
if not session or not session.get("is_authenticated"):
abort(403, "Sorry, access denied.")
try:
paste = Paste.load(paste_id)
except (TypeError, ValueError):
return error404(ValueError)
paste.delete()
return {"status": "ok", "message": "Paste deleted"}
@app.route(settings.ADMIN_URL + "/login")
@app.get(ADMIN_LOGIN_URL)
@app.post(ADMIN_LOGIN_URL)
@view("login")
def login():
password = request.forms.get("password")
if password:
if not check_password(password):
return {"error": "Wrong password"}
return {"status": "error", "message": "Wrong password", **GLOBAL_CONTEXT}
session = request.environ.get("beaker.session")
session["is_authenticated"] = True
session.save()
redirect(settings.ADMIN_URL)
else:
return {}
return {"status": "ok", **GLOBAL_CONTEXT}
@app.route("/paste/create", method="POST")
@app.post(settings.ADMIN_URL + "logout/")
@view("logout")
def logout():
session = request.environ.get("beaker.session")
session["is_authenticated"] = False
session.save()
redirect("/")
@app.post("/paste/create")
def create_paste():
try:
body = urlparse.parse_qs(request.body.read(int(settings.MAX_SIZE * 1.1)))
except ValueError:
# Reject what is too small, too big, or what does not seem encrypted to
# limit a abuses
content = request.forms.get("content", "")
if '{"iv":' not in content or not (0 < len(content) < settings.MAX_SIZE):
return {"status": "error", "message": "Wrong data payload."}
try:
content = "".join(x.decode("utf8") for x in body[b"content"])
except (UnicodeDecodeError, KeyError):
return {
"status": "error",
"message": "Encoding error: the paste couldn't be saved.",
}
expiration = request.forms.get("expiration", "burn_after_reading")
title = request.forms.get("title", "")
btc_tip_address = request.forms.get("btcTipAddress", "")
if '{"iv":' not in content: # reject silently non encrypted content
return {"status": "error", "message": "Wrong data payload."}
paste = Paste(
expiration=expiration,
content=content,
uuid_length=settings.PASTE_ID_LENGTH,
title=title,
btc_tip_address=btc_tip_address,
)
paste.save()
# 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 0 < len(content) < settings.MAX_SIZE:
expiration = body.get(b"expiration", [b"burn_after_reading"])[0]
paste = Paste(
expiration=expiration.decode("utf8"),
content=content,
uuid_length=settings.PASTE_ID_LENGTH,
# If refresh time elapsed pick up, update the counter
if settings.DISPLAY_COUNTER:
paste.increment_counter()
now = datetime.now()
timeout = GLOBAL_CONTEXT["refresh_counter"] + timedelta(
seconds=settings.REFRESH_COUNTER
)
paste.save()
if timeout < now:
GLOBAL_CONTEXT["pastes_count"] = Paste.get_pastes_count()
GLOBAL_CONTEXT["refresh_counter"] = now
# display counter
if settings.DISPLAY_COUNTER:
# increment paste counter
paste.increment_counter()
# if refresh time elapsed pick up new counter value
now = datetime.now()
timeout = GLOBAL_CONTEXT["refresh_counter"] + timedelta(
seconds=settings.REFRESH_COUNTER
)
if timeout < now:
GLOBAL_CONTEXT["pastes_count"] = Paste.get_pastes_count()
GLOBAL_CONTEXT["refresh_counter"] = now
return {"status": "ok", "paste": paste.uuid, "owner_key": paste.owner_key}
return {
"status": "error",
"message": "Serveur error: the paste couldn't be saved. " "Please try later.",
}
return {"status": "ok", "paste": paste.uuid, "owner_key": paste.owner_key}
@app.route("/paste/:paste_id", method="GET")
@app.get("/paste/:paste_id")
@view("paste")
def display_paste(paste_id):
@ -191,7 +191,7 @@ def display_paste(paste_id):
return {"paste": paste, "keep_alive": keep_alive, **GLOBAL_CONTEXT}
@app.route("/paste/:paste_id", method="DELETE")
@app.delete("/paste/:paste_id")
def delete_paste(paste_id):
try:
@ -216,36 +216,52 @@ def error404(code):
return GLOBAL_CONTEXT
@app.route("/static/<filename:path>")
@app.get("/static/<filename:path>")
def server_static(filename):
return static_file(filename, root=settings.STATIC_FILES_ROOT)
def get_app(debug=None, settings_file="", compressed_static=None, settings=settings):
def get_app(debug=None, config_dir="", data_dir=""):
"""
Return a tuple (settings, app) configured using passed
parameters and/or a setting file.
"""
settings_file = settings_file or os.environ.get("ZEROBIN_SETTINGS_FILE")
data_dir = data_dir or os.environ.get("ZEROBIN_DATA_DIR")
config_dir = config_dir or os.environ.get("ZEROBIN_CONFIG_DIR")
if settings_file:
settings.update_with_file(os.path.realpath(settings_file))
ensure_app_context(config_dir=config_dir, data_dir=data_dir)
if debug is None:
settings.DEBUG = bool(
strtobool(os.environ.get("ZEROBIN_DEBUG", str(settings.DEBUG)))
)
else:
settings.DEBUG = debug
settings.DISPLAY_COUNTER = bool(
os.environ.get("ZEROBIN_DISPLAY_COUNTER", settings.DISPLAY_COUNTER)
)
settings.REFRESH_COUNTER = int(
os.environ.get("ZEROBIN_REFRESH_COUNTER", settings.REFRESH_COUNTER)
)
settings.MAX_SIZE = int(os.environ.get("ZEROBIN_MAX_SIZE", settings.MAX_SIZE))
settings.PASTE_ID_LENGTH = int(
os.environ.get("ZEROBIN_PASTE_ID_LENGTH", settings.PASTE_ID_LENGTH)
)
if settings.PASTE_ID_LENGTH < 4:
raise SettingsValidationError("PASTE_ID_LENGTH cannot be lower than 4")
if compressed_static is not None:
settings.COMPRESSED_STATIC_FILES = compressed_static
if debug is not None:
settings.DEBUG = debug
# make sure the templates can be loaded
for d in reversed(settings.TEMPLATE_DIRS):
bottle.TEMPLATE_PATH.insert(0, d)
if settings.DEBUG:
bottle.debug(True)
return settings, app
app = SessionMiddleware(
app,
{
"session.type": "file",
"session.cookie_expires": 300,
"session.data_dir": settings.SESSIONS_DIR,
"session.auto": True,
},
)

View File

@ -1,686 +0,0 @@
/*!
* Bootstrap Responsive v2.0.2
*
* Copyright 2012 Twitter, Inc
* Licensed under the Apache License v2.0
* http://www.apache.org/licenses/LICENSE-2.0
*
* Designed and built with all the love in the world @twitter by @mdo and @fat.
*/
.clearfix {
*zoom: 1;
}
.clearfix:before,
.clearfix:after {
display: table;
content: "";
}
.clearfix:after {
clear: both;
}
.hide-text {
overflow: hidden;
text-indent: 100%;
white-space: nowrap;
}
.input-block-level {
display: block;
width: 100%;
min-height: 28px;
/* Make inputs at least the height of their button counterpart */
/* Makes inputs behave like true block-level elements */
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
-ms-box-sizing: border-box;
box-sizing: border-box;
}
.hidden {
display: none;
visibility: hidden;
}
.visible-phone {
display: none;
}
.visible-tablet {
display: none;
}
.visible-desktop {
display: block;
}
.hidden-phone {
display: block;
}
.hidden-tablet {
display: block;
}
.hidden-desktop {
display: none;
}
@media (max-width: 767px) {
.visible-phone {
display: block;
}
.hidden-phone {
display: none;
}
.hidden-desktop {
display: block;
}
.visible-desktop {
display: none;
}
}
@media (min-width: 768px) and (max-width: 979px) {
.visible-tablet {
display: block;
}
.hidden-tablet {
display: none;
}
.hidden-desktop {
display: block;
}
.visible-desktop {
display: none;
}
}
@media (max-width: 480px) {
.nav-collapse {
-webkit-transform: translate3d(0, 0, 0);
}
.page-header h1 small {
display: block;
line-height: 18px;
}
input[type="checkbox"],
input[type="radio"] {
border: 1px solid #ccc;
}
.form-horizontal .control-group > label {
float: none;
width: auto;
padding-top: 0;
text-align: left;
}
.form-horizontal .controls {
margin-left: 0;
}
.form-horizontal .control-list {
padding-top: 0;
}
.form-horizontal .form-actions {
padding-left: 10px;
padding-right: 10px;
}
.modal {
position: absolute;
top: 10px;
left: 10px;
right: 10px;
width: auto;
margin: 0;
}
.modal.fade.in {
top: auto;
}
.modal-header .close {
padding: 10px;
margin: -10px;
}
.carousel-caption {
position: static;
}
}
@media (max-width: 767px) {
body {
padding-left: 20px;
padding-right: 20px;
}
.navbar-fixed-top {
margin-left: -20px;
margin-right: -20px;
}
.container {
width: auto;
}
.row-fluid {
width: 100%;
}
.row {
margin-left: 0;
}
.row > [class*="span"],
.row-fluid > [class*="span"] {
float: none;
display: block;
width: auto;
margin: 0;
}
.thumbnails [class*="span"] {
width: auto;
}
input[class*="span"],
select[class*="span"],
textarea[class*="span"],
.uneditable-input {
display: block;
width: 100%;
min-height: 28px;
/* Make inputs at least the height of their button counterpart */
/* Makes inputs behave like true block-level elements */
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
-ms-box-sizing: border-box;
box-sizing: border-box;
}
.input-prepend input[class*="span"],
.input-append input[class*="span"] {
width: auto;
}
}
@media (min-width: 768px) and (max-width: 979px) {
.row {
margin-left: -20px;
*zoom: 1;
}
.row:before,
.row:after {
display: table;
content: "";
}
.row:after {
clear: both;
}
[class*="span"] {
float: left;
margin-left: 20px;
}
.container,
.navbar-fixed-top .container,
.navbar-fixed-bottom .container {
width: 724px;
}
.span12 {
width: 724px;
}
.span11 {
width: 662px;
}
.span10 {
width: 600px;
}
.span9 {
width: 538px;
}
.span8 {
width: 476px;
}
.span7 {
width: 414px;
}
.span6 {
width: 352px;
}
.span5 {
width: 290px;
}
.span4 {
width: 228px;
}
.span3 {
width: 166px;
}
.span2 {
width: 104px;
}
.span1 {
width: 42px;
}
.offset12 {
margin-left: 764px;
}
.offset11 {
margin-left: 702px;
}
.offset10 {
margin-left: 640px;
}
.offset9 {
margin-left: 578px;
}
.offset8 {
margin-left: 516px;
}
.offset7 {
margin-left: 454px;
}
.offset6 {
margin-left: 392px;
}
.offset5 {
margin-left: 330px;
}
.offset4 {
margin-left: 268px;
}
.offset3 {
margin-left: 206px;
}
.offset2 {
margin-left: 144px;
}
.offset1 {
margin-left: 82px;
}
.row-fluid {
width: 100%;
*zoom: 1;
}
.row-fluid:before,
.row-fluid:after {
display: table;
content: "";
}
.row-fluid:after {
clear: both;
}
.row-fluid > [class*="span"] {
float: left;
margin-left: 2.762430939%;
}
.row-fluid > [class*="span"]:first-child {
margin-left: 0;
}
.row-fluid > .span12 {
width: 99.999999993%;
}
.row-fluid > .span11 {
width: 91.436464082%;
}
.row-fluid > .span10 {
width: 82.87292817100001%;
}
.row-fluid > .span9 {
width: 74.30939226%;
}
.row-fluid > .span8 {
width: 65.74585634900001%;
}
.row-fluid > .span7 {
width: 57.182320438000005%;
}
.row-fluid > .span6 {
width: 48.618784527%;
}
.row-fluid > .span5 {
width: 40.055248616%;
}
.row-fluid > .span4 {
width: 31.491712705%;
}
.row-fluid > .span3 {
width: 22.928176794%;
}
.row-fluid > .span2 {
width: 14.364640883%;
}
.row-fluid > .span1 {
width: 5.801104972%;
}
input,
textarea,
.uneditable-input {
margin-left: 0;
}
input.span12, textarea.span12, .uneditable-input.span12 {
width: 714px;
}
input.span11, textarea.span11, .uneditable-input.span11 {
width: 652px;
}
input.span10, textarea.span10, .uneditable-input.span10 {
width: 590px;
}
input.span9, textarea.span9, .uneditable-input.span9 {
width: 528px;
}
input.span8, textarea.span8, .uneditable-input.span8 {
width: 466px;
}
input.span7, textarea.span7, .uneditable-input.span7 {
width: 404px;
}
input.span6, textarea.span6, .uneditable-input.span6 {
width: 342px;
}
input.span5, textarea.span5, .uneditable-input.span5 {
width: 280px;
}
input.span4, textarea.span4, .uneditable-input.span4 {
width: 218px;
}
input.span3, textarea.span3, .uneditable-input.span3 {
width: 156px;
}
input.span2, textarea.span2, .uneditable-input.span2 {
width: 94px;
}
input.span1, textarea.span1, .uneditable-input.span1 {
width: 32px;
}
}
@media (max-width: 979px) {
body {
padding-top: 0;
}
.navbar-fixed-top {
position: static;
margin-bottom: 18px;
}
.navbar-fixed-top .navbar-inner {
padding: 5px;
}
.navbar .container {
width: auto;
padding: 0;
}
.navbar .brand {
padding-left: 10px;
padding-right: 10px;
margin: 0 0 0 -5px;
}
.navbar .nav-collapse {
clear: left;
}
.navbar .nav {
float: none;
margin: 0 0 9px;
}
.navbar .nav > li {
float: none;
}
.navbar .nav > li > a {
margin-bottom: 2px;
}
.navbar .nav > .divider-vertical {
display: none;
}
.navbar .nav .nav-header {
color: #999999;
text-shadow: none;
}
.navbar .nav > li > a,
.navbar .dropdown-menu a {
padding: 6px 15px;
font-weight: bold;
color: #999999;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;
}
.navbar .dropdown-menu li + li a {
margin-bottom: 2px;
}
.navbar .nav > li > a:hover,
.navbar .dropdown-menu a:hover {
background-color: #222222;
}
.navbar .dropdown-menu {
position: static;
top: auto;
left: auto;
float: none;
display: block;
max-width: none;
margin: 0 15px;
padding: 0;
background-color: transparent;
border: none;
-webkit-border-radius: 0;
-moz-border-radius: 0;
border-radius: 0;
-webkit-box-shadow: none;
-moz-box-shadow: none;
box-shadow: none;
}
.navbar .dropdown-menu:before,
.navbar .dropdown-menu:after {
display: none;
}
.navbar .dropdown-menu .divider {
display: none;
}
.navbar-form,
.navbar-search {
float: none;
padding: 9px 15px;
margin: 9px 0;
border-top: 1px solid #222222;
border-bottom: 1px solid #222222;
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
-moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
}
.navbar .nav.pull-right {
float: none;
margin-left: 0;
}
.navbar-static .navbar-inner {
padding-left: 10px;
padding-right: 10px;
}
.btn-navbar {
display: block;
}
.nav-collapse {
overflow: hidden;
height: 0;
}
}
@media (min-width: 980px) {
.nav-collapse.collapse {
height: auto !important;
overflow: visible !important;
}
}
@media (min-width: 1200px) {
.row {
margin-left: -30px;
*zoom: 1;
}
.row:before,
.row:after {
display: table;
content: "";
}
.row:after {
clear: both;
}
[class*="span"] {
float: left;
margin-left: 30px;
}
.container,
.navbar-fixed-top .container,
.navbar-fixed-bottom .container {
width: 1170px;
}
.span12 {
width: 1170px;
}
.span11 {
width: 1070px;
}
.span10 {
width: 970px;
}
.span9 {
width: 870px;
}
.span8 {
width: 770px;
}
.span7 {
width: 670px;
}
.span6 {
width: 570px;
}
.span5 {
width: 470px;
}
.span4 {
width: 370px;
}
.span3 {
width: 270px;
}
.span2 {
width: 170px;
}
.span1 {
width: 70px;
}
.offset12 {
margin-left: 1230px;
}
.offset11 {
margin-left: 1130px;
}
.offset10 {
margin-left: 1030px;
}
.offset9 {
margin-left: 930px;
}
.offset8 {
margin-left: 830px;
}
.offset7 {
margin-left: 730px;
}
.offset6 {
margin-left: 630px;
}
.offset5 {
margin-left: 530px;
}
.offset4 {
margin-left: 430px;
}
.offset3 {
margin-left: 330px;
}
.offset2 {
margin-left: 230px;
}
.offset1 {
margin-left: 130px;
}
.row-fluid {
width: 100%;
*zoom: 1;
}
.row-fluid:before,
.row-fluid:after {
display: table;
content: "";
}
.row-fluid:after {
clear: both;
}
.row-fluid > [class*="span"] {
float: left;
margin-left: 2.564102564%;
}
.row-fluid > [class*="span"]:first-child {
margin-left: 0;
}
.row-fluid > .span12 {
width: 100%;
}
.row-fluid > .span11 {
width: 91.45299145300001%;
}
.row-fluid > .span10 {
width: 82.905982906%;
}
.row-fluid > .span9 {
width: 74.358974359%;
}
.row-fluid > .span8 {
width: 65.81196581200001%;
}
.row-fluid > .span7 {
width: 57.264957265%;
}
.row-fluid > .span6 {
width: 48.717948718%;
}
.row-fluid > .span5 {
width: 40.170940171000005%;
}
.row-fluid > .span4 {
width: 31.623931624%;
}
.row-fluid > .span3 {
width: 23.076923077%;
}
.row-fluid > .span2 {
width: 14.529914530000001%;
}
.row-fluid > .span1 {
width: 5.982905983%;
}
input,
textarea,
.uneditable-input {
margin-left: 0;
}
input.span12, textarea.span12, .uneditable-input.span12 {
width: 1160px;
}
input.span11, textarea.span11, .uneditable-input.span11 {
width: 1060px;
}
input.span10, textarea.span10, .uneditable-input.span10 {
width: 960px;
}
input.span9, textarea.span9, .uneditable-input.span9 {
width: 860px;
}
input.span8, textarea.span8, .uneditable-input.span8 {
width: 760px;
}
input.span7, textarea.span7, .uneditable-input.span7 {
width: 660px;
}
input.span6, textarea.span6, .uneditable-input.span6 {
width: 560px;
}
input.span5, textarea.span5, .uneditable-input.span5 {
width: 460px;
}
input.span4, textarea.span4, .uneditable-input.span4 {
width: 360px;
}
input.span3, textarea.span3, .uneditable-input.span3 {
width: 260px;
}
input.span2, textarea.span2, .uneditable-input.span2 {
width: 160px;
}
input.span1, textarea.span1, .uneditable-input.span1 {
width: 60px;
}
.thumbnails {
margin-left: -30px;
}
.thumbnails > li {
margin-left: 30px;
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -1,689 +0,0 @@
article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block;}
audio,canvas,video{display:inline-block;*display:inline;*zoom:1;}
audio:not([controls]){display:none;}
html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;}
a:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;}
a:hover,a:active{outline:0;}
sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline;}
sup{top:-0.5em;}
sub{bottom:-0.25em;}
img{height:auto;border:0;-ms-interpolation-mode:bicubic;vertical-align:middle;}
button,input,select,textarea{margin:0;font-size:100%;vertical-align:middle;}
button,input{*overflow:visible;line-height:normal;}
button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0;}
button,input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button;}
input[type="search"]{-webkit-appearance:textfield;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;}
input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none;}
textarea{overflow:auto;vertical-align:top;}
.clearfix{*zoom:1;}.clearfix:before,.clearfix:after{display:table;content:"";}
.clearfix:after{clear:both;}
.hide-text{overflow:hidden;text-indent:100%;white-space:nowrap;}
.input-block-level{display:block;width:100%;min-height:28px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;}
body{margin:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;line-height:18px;color:#333333;background-color:#ffffff;}
a{color:#0088cc;text-decoration:none;}
a:hover{color:#005580;text-decoration:underline;}
.row{margin-left:-20px;*zoom:1;}.row:before,.row:after{display:table;content:"";}
.row:after{clear:both;}
[class*="span"]{float:left;margin-left:20px;}
.container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px;}
.span12{width:940px;}
.span11{width:860px;}
.span10{width:780px;}
.span9{width:700px;}
.span8{width:620px;}
.span7{width:540px;}
.span6{width:460px;}
.span5{width:380px;}
.span4{width:300px;}
.span3{width:220px;}
.span2{width:140px;}
.span1{width:60px;}
.offset12{margin-left:980px;}
.offset11{margin-left:900px;}
.offset10{margin-left:820px;}
.offset9{margin-left:740px;}
.offset8{margin-left:660px;}
.offset7{margin-left:580px;}
.offset6{margin-left:500px;}
.offset5{margin-left:420px;}
.offset4{margin-left:340px;}
.offset3{margin-left:260px;}
.offset2{margin-left:180px;}
.offset1{margin-left:100px;}
.row-fluid{width:100%;*zoom:1;}.row-fluid:before,.row-fluid:after{display:table;content:"";}
.row-fluid:after{clear:both;}
.row-fluid>[class*="span"]{float:left;margin-left:2.127659574%;}
.row-fluid>[class*="span"]:first-child{margin-left:0;}
.row-fluid > .span12{width:99.99999998999999%;}
.row-fluid > .span11{width:91.489361693%;}
.row-fluid > .span10{width:82.97872339599999%;}
.row-fluid > .span9{width:74.468085099%;}
.row-fluid > .span8{width:65.95744680199999%;}
.row-fluid > .span7{width:57.446808505%;}
.row-fluid > .span6{width:48.93617020799999%;}
.row-fluid > .span5{width:40.425531911%;}
.row-fluid > .span4{width:31.914893614%;}
.row-fluid > .span3{width:23.404255317%;}
.row-fluid > .span2{width:14.89361702%;}
.row-fluid > .span1{width:6.382978723%;}
.container{margin-left:auto;margin-right:auto;*zoom:1;}.container:before,.container:after{display:table;content:"";}
.container:after{clear:both;}
.container-fluid{padding-left:20px;padding-right:20px;*zoom:1;}.container-fluid:before,.container-fluid:after{display:table;content:"";}
.container-fluid:after{clear:both;}
p{margin:0 0 9px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;line-height:18px;}p small{font-size:11px;color:#999999;}
.lead{margin-bottom:18px;font-size:20px;font-weight:200;line-height:27px;}
h1,h2,h3,h4,h5,h6{margin:0;font-family:inherit;font-weight:bold;color:inherit;text-rendering:optimizelegibility;}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{font-weight:normal;color:#999999;}
h1{font-size:30px;line-height:36px;}h1 small{font-size:18px;}
h2{font-size:24px;line-height:36px;}h2 small{font-size:18px;}
h3{line-height:27px;font-size:18px;}h3 small{font-size:14px;}
h4,h5,h6{line-height:18px;}
h4{font-size:14px;}h4 small{font-size:12px;}
h5{font-size:12px;}
h6{font-size:11px;color:#999999;text-transform:uppercase;}
.page-header{padding-bottom:17px;margin:18px 0;border-bottom:1px solid #eeeeee;}
.page-header h1{line-height:1;}
ul,ol{padding:0;margin:0 0 9px 25px;}
ul ul,ul ol,ol ol,ol ul{margin-bottom:0;}
ul{list-style:disc;}
ol{list-style:decimal;}
li{line-height:18px;}
ul.unstyled,ol.unstyled{margin-left:0;list-style:none;}
dl{margin-bottom:18px;}
dt,dd{line-height:18px;}
dt{font-weight:bold;line-height:17px;}
dd{margin-left:9px;}
.dl-horizontal dt{float:left;clear:left;width:120px;text-align:right;}
.dl-horizontal dd{margin-left:130px;}
hr{margin:18px 0;border:0;border-top:1px solid #eeeeee;border-bottom:1px solid #ffffff;}
strong{font-weight:bold;}
em{font-style:italic;}
.muted{color:#999999;}
abbr[title]{border-bottom:1px dotted #ddd;cursor:help;}
abbr.initialism{font-size:90%;text-transform:uppercase;}
blockquote{padding:0 0 0 15px;margin:0 0 18px;border-left:5px solid #eeeeee;}blockquote p{margin-bottom:0;font-size:16px;font-weight:300;line-height:22.5px;}
blockquote small{display:block;line-height:18px;color:#999999;}blockquote small:before{content:'\2014 \00A0';}
blockquote.pull-right{float:right;padding-left:0;padding-right:15px;border-left:0;border-right:5px solid #eeeeee;}blockquote.pull-right p,blockquote.pull-right small{text-align:right;}
q:before,q:after,blockquote:before,blockquote:after{content:"";}
address{display:block;margin-bottom:18px;line-height:18px;font-style:normal;}
small{font-size:100%;}
cite{font-style:normal;}
code,pre{padding:0 3px 2px;font-family:Menlo,Monaco,"Courier New",monospace;font-size:12px;color:#333333;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;}
code{padding:2px 4px;color:#d14;background-color:#f7f7f9;border:1px solid #e1e1e8;}
pre{display:block;padding:8.5px;margin:0 0 9px;font-size:12.025px;line-height:18px;background-color:#f5f5f5;border:1px solid #ccc;border:1px solid rgba(0, 0, 0, 0.15);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;white-space:pre;white-space:pre-wrap;word-break:break-all;word-wrap:break-word;}pre.prettyprint{margin-bottom:18px;}
pre code{padding:0;color:inherit;background-color:transparent;border:0;}
.pre-scrollable{max-height:340px;overflow-y:scroll;}
form{margin:0 0 18px;}
fieldset{padding:0;margin:0;border:0;}
legend{display:block;width:100%;padding:0;margin-bottom:27px;font-size:19.5px;line-height:36px;color:#333333;border:0;border-bottom:1px solid #eee;}legend small{font-size:13.5px;color:#999999;}
label,input,button,select,textarea{font-size:13px;font-weight:normal;line-height:18px;}
input,button,select,textarea{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;}
label{display:block;margin-bottom:5px;color:#333333;}
input,textarea,select,.uneditable-input{display:inline-block;width:210px;height:18px;padding:4px;margin-bottom:9px;font-size:13px;line-height:18px;color:#555555;border:1px solid #cccccc;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;}
.uneditable-textarea{width:auto;height:auto;}
label input,label textarea,label select{display:block;}
input[type="image"],input[type="checkbox"],input[type="radio"]{width:auto;height:auto;padding:0;margin:3px 0;*margin-top:0;line-height:normal;cursor:pointer;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;border:0 \9;}
input[type="image"]{border:0;}
input[type="file"]{width:auto;padding:initial;line-height:initial;border:initial;background-color:#ffffff;background-color:initial;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;}
input[type="button"],input[type="reset"],input[type="submit"]{width:auto;height:auto;}
select,input[type="file"]{height:28px;*margin-top:4px;line-height:28px;}
input[type="file"]{line-height:18px \9;}
select{width:220px;background-color:#ffffff;}
select[multiple],select[size]{height:auto;}
input[type="image"]{-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;}
textarea{height:auto;}
input[type="hidden"]{display:none;}
.radio,.checkbox{padding-left:18px;}
.radio input[type="radio"],.checkbox input[type="checkbox"]{float:left;margin-left:-18px;}
.controls>.radio:first-child,.controls>.checkbox:first-child{padding-top:5px;}
.radio.inline,.checkbox.inline{display:inline-block;padding-top:5px;margin-bottom:0;vertical-align:middle;}
.radio.inline+.radio.inline,.checkbox.inline+.checkbox.inline{margin-left:10px;}
input,textarea{-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075);-webkit-transition:border linear 0.2s,box-shadow linear 0.2s;-moz-transition:border linear 0.2s,box-shadow linear 0.2s;-ms-transition:border linear 0.2s,box-shadow linear 0.2s;-o-transition:border linear 0.2s,box-shadow linear 0.2s;transition:border linear 0.2s,box-shadow linear 0.2s;}
input:focus,textarea:focus{border-color:rgba(82, 168, 236, 0.8);-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 8px rgba(82, 168, 236, 0.6);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 8px rgba(82, 168, 236, 0.6);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.075),0 0 8px rgba(82, 168, 236, 0.6);outline:0;outline:thin dotted \9;}
input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus,select:focus{-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;}
.input-mini{width:60px;}
.input-small{width:90px;}
.input-medium{width:150px;}
.input-large{width:210px;}
.input-xlarge{width:270px;}
.input-xxlarge{width:530px;}
input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input{float:none;margin-left:0;}
input,textarea,.uneditable-input{margin-left:0;}
input.span12, textarea.span12, .uneditable-input.span12{width:930px;}
input.span11, textarea.span11, .uneditable-input.span11{width:850px;}
input.span10, textarea.span10, .uneditable-input.span10{width:770px;}
input.span9, textarea.span9, .uneditable-input.span9{width:690px;}
input.span8, textarea.span8, .uneditable-input.span8{width:610px;}
input.span7, textarea.span7, .uneditable-input.span7{width:530px;}
input.span6, textarea.span6, .uneditable-input.span6{width:450px;}
input.span5, textarea.span5, .uneditable-input.span5{width:370px;}
input.span4, textarea.span4, .uneditable-input.span4{width:290px;}
input.span3, textarea.span3, .uneditable-input.span3{width:210px;}
input.span2, textarea.span2, .uneditable-input.span2{width:130px;}
input.span1, textarea.span1, .uneditable-input.span1{width:50px;}
input[disabled],select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{background-color:#eeeeee;border-color:#ddd;cursor:not-allowed;}
.control-group.warning>label,.control-group.warning .help-block,.control-group.warning .help-inline{color:#c09853;}
.control-group.warning input,.control-group.warning select,.control-group.warning textarea{color:#c09853;border-color:#c09853;}.control-group.warning input:focus,.control-group.warning select:focus,.control-group.warning textarea:focus{border-color:#a47e3c;-webkit-box-shadow:0 0 6px #dbc59e;-moz-box-shadow:0 0 6px #dbc59e;box-shadow:0 0 6px #dbc59e;}
.control-group.warning .input-prepend .add-on,.control-group.warning .input-append .add-on{color:#c09853;background-color:#fcf8e3;border-color:#c09853;}
.control-group.error>label,.control-group.error .help-block,.control-group.error .help-inline{color:#b94a48;}
.control-group.error input,.control-group.error select,.control-group.error textarea{color:#b94a48;border-color:#b94a48;}.control-group.error input:focus,.control-group.error select:focus,.control-group.error textarea:focus{border-color:#953b39;-webkit-box-shadow:0 0 6px #d59392;-moz-box-shadow:0 0 6px #d59392;box-shadow:0 0 6px #d59392;}
.control-group.error .input-prepend .add-on,.control-group.error .input-append .add-on{color:#b94a48;background-color:#f2dede;border-color:#b94a48;}
.control-group.success>label,.control-group.success .help-block,.control-group.success .help-inline{color:#468847;}
.control-group.success input,.control-group.success select,.control-group.success textarea{color:#468847;border-color:#468847;}.control-group.success input:focus,.control-group.success select:focus,.control-group.success textarea:focus{border-color:#356635;-webkit-box-shadow:0 0 6px #7aba7b;-moz-box-shadow:0 0 6px #7aba7b;box-shadow:0 0 6px #7aba7b;}
.control-group.success .input-prepend .add-on,.control-group.success .input-append .add-on{color:#468847;background-color:#dff0d8;border-color:#468847;}
input:focus:required:invalid,textarea:focus:required:invalid,select:focus:required:invalid{color:#b94a48;border-color:#ee5f5b;}input:focus:required:invalid:focus,textarea:focus:required:invalid:focus,select:focus:required:invalid:focus{border-color:#e9322d;-webkit-box-shadow:0 0 6px #f8b9b7;-moz-box-shadow:0 0 6px #f8b9b7;box-shadow:0 0 6px #f8b9b7;}
.form-actions{padding:17px 20px 18px;margin-top:18px;margin-bottom:18px;background-color:#eeeeee;border-top:1px solid #ddd;*zoom:1;}.form-actions:before,.form-actions:after{display:table;content:"";}
.form-actions:after{clear:both;}
.uneditable-input{display:block;background-color:#ffffff;border-color:#eee;-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);-moz-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);cursor:not-allowed;}
:-moz-placeholder{color:#999999;}
::-webkit-input-placeholder{color:#999999;}
.help-block,.help-inline{color:#555555;}
.help-block{display:block;margin-bottom:9px;}
.help-inline{display:inline-block;*display:inline;*zoom:1;vertical-align:middle;padding-left:5px;}
.input-prepend,.input-append{margin-bottom:5px;}.input-prepend input,.input-append input,.input-prepend select,.input-append select,.input-prepend .uneditable-input,.input-append .uneditable-input{*margin-left:0;-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;}.input-prepend input:focus,.input-append input:focus,.input-prepend select:focus,.input-append select:focus,.input-prepend .uneditable-input:focus,.input-append .uneditable-input:focus{position:relative;z-index:2;}
.input-prepend .uneditable-input,.input-append .uneditable-input{border-left-color:#ccc;}
.input-prepend .add-on,.input-append .add-on{display:inline-block;width:auto;min-width:16px;height:18px;padding:4px 5px;font-weight:normal;line-height:18px;text-align:center;text-shadow:0 1px 0 #ffffff;vertical-align:middle;background-color:#eeeeee;border:1px solid #ccc;}
.input-prepend .add-on,.input-append .add-on,.input-prepend .btn,.input-append .btn{-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;}
.input-prepend .active,.input-append .active{background-color:#a9dba9;border-color:#46a546;}
.input-prepend .add-on,.input-prepend .btn{margin-right:-1px;}
.input-append input,.input-append select .uneditable-input{-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;}
.input-append .uneditable-input{border-left-color:#eee;border-right-color:#ccc;}
.input-append .add-on,.input-append .btn{margin-left:-1px;-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;}
.input-prepend.input-append input,.input-prepend.input-append select,.input-prepend.input-append .uneditable-input{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
.input-prepend.input-append .add-on:first-child,.input-prepend.input-append .btn:first-child{margin-right:-1px;-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;}
.input-prepend.input-append .add-on:last-child,.input-prepend.input-append .btn:last-child{margin-left:-1px;-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;}
.search-query{padding-left:14px;padding-right:14px;margin-bottom:0;-webkit-border-radius:14px;-moz-border-radius:14px;border-radius:14px;}
.form-search input,.form-inline input,.form-horizontal input,.form-search textarea,.form-inline textarea,.form-horizontal textarea,.form-search select,.form-inline select,.form-horizontal select,.form-search .help-inline,.form-inline .help-inline,.form-horizontal .help-inline,.form-search .uneditable-input,.form-inline .uneditable-input,.form-horizontal .uneditable-input,.form-search .input-prepend,.form-inline .input-prepend,.form-horizontal .input-prepend,.form-search .input-append,.form-inline .input-append,.form-horizontal .input-append{display:inline-block;margin-bottom:0;}
.form-search .hide,.form-inline .hide,.form-horizontal .hide{display:none;}
.form-search label,.form-inline label{display:inline-block;}
.form-search .input-append,.form-inline .input-append,.form-search .input-prepend,.form-inline .input-prepend{margin-bottom:0;}
.form-search .radio,.form-search .checkbox,.form-inline .radio,.form-inline .checkbox{padding-left:0;margin-bottom:0;vertical-align:middle;}
.form-search .radio input[type="radio"],.form-search .checkbox input[type="checkbox"],.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{float:left;margin-left:0;margin-right:3px;}
.control-group{margin-bottom:9px;}
legend+.control-group{margin-top:18px;-webkit-margin-top-collapse:separate;}
.form-horizontal .control-group{margin-bottom:18px;*zoom:1;}.form-horizontal .control-group:before,.form-horizontal .control-group:after{display:table;content:"";}
.form-horizontal .control-group:after{clear:both;}
.form-horizontal .control-label{float:left;width:140px;padding-top:5px;text-align:right;}
.form-horizontal .controls{margin-left:160px;*display:inline-block;*margin-left:0;*padding-left:20px;}
.form-horizontal .help-block{margin-top:9px;margin-bottom:0;}
.form-horizontal .form-actions{padding-left:160px;}
table{max-width:100%;border-collapse:collapse;border-spacing:0;background-color:transparent;}
.table{width:100%;margin-bottom:18px;}.table th,.table td{padding:8px;line-height:18px;text-align:left;vertical-align:top;border-top:1px solid #dddddd;}
.table th{font-weight:bold;}
.table thead th{vertical-align:bottom;}
.table colgroup+thead tr:first-child th,.table colgroup+thead tr:first-child td,.table thead:first-child tr:first-child th,.table thead:first-child tr:first-child td{border-top:0;}
.table tbody+tbody{border-top:2px solid #dddddd;}
.table-condensed th,.table-condensed td{padding:4px 5px;}
.table-bordered{border:1px solid #dddddd;border-left:0;border-collapse:separate;*border-collapse:collapsed;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}.table-bordered th,.table-bordered td{border-left:1px solid #dddddd;}
.table-bordered thead:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child td{border-top:0;}
.table-bordered thead:first-child tr:first-child th:first-child,.table-bordered tbody:first-child tr:first-child td:first-child{-webkit-border-radius:4px 0 0 0;-moz-border-radius:4px 0 0 0;border-radius:4px 0 0 0;}
.table-bordered thead:first-child tr:first-child th:last-child,.table-bordered tbody:first-child tr:first-child td:last-child{-webkit-border-radius:0 4px 0 0;-moz-border-radius:0 4px 0 0;border-radius:0 4px 0 0;}
.table-bordered thead:last-child tr:last-child th:first-child,.table-bordered tbody:last-child tr:last-child td:first-child{-webkit-border-radius:0 0 0 4px;-moz-border-radius:0 0 0 4px;border-radius:0 0 0 4px;}
.table-bordered thead:last-child tr:last-child th:last-child,.table-bordered tbody:last-child tr:last-child td:last-child{-webkit-border-radius:0 0 4px 0;-moz-border-radius:0 0 4px 0;border-radius:0 0 4px 0;}
.table-striped tbody tr:nth-child(odd) td,.table-striped tbody tr:nth-child(odd) th{background-color:#f9f9f9;}
.table tbody tr:hover td,.table tbody tr:hover th{background-color:#f5f5f5;}
table .span1{float:none;width:44px;margin-left:0;}
table .span2{float:none;width:124px;margin-left:0;}
table .span3{float:none;width:204px;margin-left:0;}
table .span4{float:none;width:284px;margin-left:0;}
table .span5{float:none;width:364px;margin-left:0;}
table .span6{float:none;width:444px;margin-left:0;}
table .span7{float:none;width:524px;margin-left:0;}
table .span8{float:none;width:604px;margin-left:0;}
table .span9{float:none;width:684px;margin-left:0;}
table .span10{float:none;width:764px;margin-left:0;}
table .span11{float:none;width:844px;margin-left:0;}
table .span12{float:none;width:924px;margin-left:0;}
table .span13{float:none;width:1004px;margin-left:0;}
table .span14{float:none;width:1084px;margin-left:0;}
table .span15{float:none;width:1164px;margin-left:0;}
table .span16{float:none;width:1244px;margin-left:0;}
table .span17{float:none;width:1324px;margin-left:0;}
table .span18{float:none;width:1404px;margin-left:0;}
table .span19{float:none;width:1484px;margin-left:0;}
table .span20{float:none;width:1564px;margin-left:0;}
table .span21{float:none;width:1644px;margin-left:0;}
table .span22{float:none;width:1724px;margin-left:0;}
table .span23{float:none;width:1804px;margin-left:0;}
table .span24{float:none;width:1884px;margin-left:0;}
[class^="icon-"],[class*=" icon-"]{display:inline-block;width:14px;height:14px;line-height:14px;vertical-align:text-top;background-image:url("../img/glyphicons-halflings.png");background-position:14px 14px;background-repeat:no-repeat;*margin-right:.3em;}[class^="icon-"]:last-child,[class*=" icon-"]:last-child{*margin-left:0;}
.icon-white{background-image:url("../img/glyphicons-halflings-white.png");}
.icon-glass{background-position:0 0;}
.icon-music{background-position:-24px 0;}
.icon-search{background-position:-48px 0;}
.icon-envelope{background-position:-72px 0;}
.icon-heart{background-position:-96px 0;}
.icon-star{background-position:-120px 0;}
.icon-star-empty{background-position:-144px 0;}
.icon-user{background-position:-168px 0;}
.icon-film{background-position:-192px 0;}
.icon-th-large{background-position:-216px 0;}
.icon-th{background-position:-240px 0;}
.icon-th-list{background-position:-264px 0;}
.icon-ok{background-position:-288px 0;}
.icon-remove{background-position:-312px 0;}
.icon-zoom-in{background-position:-336px 0;}
.icon-zoom-out{background-position:-360px 0;}
.icon-off{background-position:-384px 0;}
.icon-signal{background-position:-408px 0;}
.icon-cog{background-position:-432px 0;}
.icon-trash{background-position:-456px 0;}
.icon-home{background-position:0 -24px;}
.icon-file{background-position:-24px -24px;}
.icon-time{background-position:-48px -24px;}
.icon-road{background-position:-72px -24px;}
.icon-download-alt{background-position:-96px -24px;}
.icon-download{background-position:-120px -24px;}
.icon-upload{background-position:-144px -24px;}
.icon-inbox{background-position:-168px -24px;}
.icon-play-circle{background-position:-192px -24px;}
.icon-repeat{background-position:-216px -24px;}
.icon-refresh{background-position:-240px -24px;}
.icon-list-alt{background-position:-264px -24px;}
.icon-lock{background-position:-287px -24px;}
.icon-flag{background-position:-312px -24px;}
.icon-headphones{background-position:-336px -24px;}
.icon-volume-off{background-position:-360px -24px;}
.icon-volume-down{background-position:-384px -24px;}
.icon-volume-up{background-position:-408px -24px;}
.icon-qrcode{background-position:-432px -24px;}
.icon-barcode{background-position:-456px -24px;}
.icon-tag{background-position:0 -48px;}
.icon-tags{background-position:-25px -48px;}
.icon-book{background-position:-48px -48px;}
.icon-bookmark{background-position:-72px -48px;}
.icon-print{background-position:-96px -48px;}
.icon-camera{background-position:-120px -48px;}
.icon-font{background-position:-144px -48px;}
.icon-bold{background-position:-167px -48px;}
.icon-italic{background-position:-192px -48px;}
.icon-text-height{background-position:-216px -48px;}
.icon-text-width{background-position:-240px -48px;}
.icon-align-left{background-position:-264px -48px;}
.icon-align-center{background-position:-288px -48px;}
.icon-align-right{background-position:-312px -48px;}
.icon-align-justify{background-position:-336px -48px;}
.icon-list{background-position:-360px -48px;}
.icon-indent-left{background-position:-384px -48px;}
.icon-indent-right{background-position:-408px -48px;}
.icon-facetime-video{background-position:-432px -48px;}
.icon-picture{background-position:-456px -48px;}
.icon-pencil{background-position:0 -72px;}
.icon-map-marker{background-position:-24px -72px;}
.icon-adjust{background-position:-48px -72px;}
.icon-tint{background-position:-72px -72px;}
.icon-edit{background-position:-96px -72px;}
.icon-share{background-position:-120px -72px;}
.icon-check{background-position:-144px -72px;}
.icon-move{background-position:-168px -72px;}
.icon-step-backward{background-position:-192px -72px;}
.icon-fast-backward{background-position:-216px -72px;}
.icon-backward{background-position:-240px -72px;}
.icon-play{background-position:-264px -72px;}
.icon-pause{background-position:-288px -72px;}
.icon-stop{background-position:-312px -72px;}
.icon-forward{background-position:-336px -72px;}
.icon-fast-forward{background-position:-360px -72px;}
.icon-step-forward{background-position:-384px -72px;}
.icon-eject{background-position:-408px -72px;}
.icon-chevron-left{background-position:-432px -72px;}
.icon-chevron-right{background-position:-456px -72px;}
.icon-plus-sign{background-position:0 -96px;}
.icon-minus-sign{background-position:-24px -96px;}
.icon-remove-sign{background-position:-48px -96px;}
.icon-ok-sign{background-position:-72px -96px;}
.icon-question-sign{background-position:-96px -96px;}
.icon-info-sign{background-position:-120px -96px;}
.icon-screenshot{background-position:-144px -96px;}
.icon-remove-circle{background-position:-168px -96px;}
.icon-ok-circle{background-position:-192px -96px;}
.icon-ban-circle{background-position:-216px -96px;}
.icon-arrow-left{background-position:-240px -96px;}
.icon-arrow-right{background-position:-264px -96px;}
.icon-arrow-up{background-position:-289px -96px;}
.icon-arrow-down{background-position:-312px -96px;}
.icon-share-alt{background-position:-336px -96px;}
.icon-resize-full{background-position:-360px -96px;}
.icon-resize-small{background-position:-384px -96px;}
.icon-plus{background-position:-408px -96px;}
.icon-minus{background-position:-433px -96px;}
.icon-asterisk{background-position:-456px -96px;}
.icon-exclamation-sign{background-position:0 -120px;}
.icon-gift{background-position:-24px -120px;}
.icon-leaf{background-position:-48px -120px;}
.icon-fire{background-position:-72px -120px;}
.icon-eye-open{background-position:-96px -120px;}
.icon-eye-close{background-position:-120px -120px;}
.icon-warning-sign{background-position:-144px -120px;}
.icon-plane{background-position:-168px -120px;}
.icon-calendar{background-position:-192px -120px;}
.icon-random{background-position:-216px -120px;}
.icon-comment{background-position:-240px -120px;}
.icon-magnet{background-position:-264px -120px;}
.icon-chevron-up{background-position:-288px -120px;}
.icon-chevron-down{background-position:-313px -119px;}
.icon-retweet{background-position:-336px -120px;}
.icon-shopping-cart{background-position:-360px -120px;}
.icon-folder-close{background-position:-384px -120px;}
.icon-folder-open{background-position:-408px -120px;}
.icon-resize-vertical{background-position:-432px -119px;}
.icon-resize-horizontal{background-position:-456px -118px;}
.dropdown{position:relative;}
.dropdown-toggle{*margin-bottom:-3px;}
.dropdown-toggle:active,.open .dropdown-toggle{outline:0;}
.caret{display:inline-block;width:0;height:0;vertical-align:top;border-left:4px solid transparent;border-right:4px solid transparent;border-top:4px solid #000000;opacity:0.3;filter:alpha(opacity=30);content:"";}
.dropdown .caret{margin-top:8px;margin-left:2px;}
.dropdown:hover .caret,.open.dropdown .caret{opacity:1;filter:alpha(opacity=100);}
.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;float:left;display:none;min-width:160px;padding:4px 0;margin:0;list-style:none;background-color:#ffffff;border-color:#ccc;border-color:rgba(0, 0, 0, 0.2);border-style:solid;border-width:1px;-webkit-border-radius:0 0 5px 5px;-moz-border-radius:0 0 5px 5px;border-radius:0 0 5px 5px;-webkit-box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);-moz-box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);box-shadow:0 5px 10px rgba(0, 0, 0, 0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box;*border-right-width:2px;*border-bottom-width:2px;}.dropdown-menu.pull-right{right:0;left:auto;}
.dropdown-menu .divider{height:1px;margin:8px 1px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #ffffff;*width:100%;*margin:-5px 0 5px;}
.dropdown-menu a{display:block;padding:3px 15px;clear:both;font-weight:normal;line-height:18px;color:#333333;white-space:nowrap;}
.dropdown-menu li>a:hover,.dropdown-menu .active>a,.dropdown-menu .active>a:hover{color:#ffffff;text-decoration:none;background-color:#0088cc;}
.dropdown.open{*z-index:1000;}.dropdown.open .dropdown-toggle{color:#ffffff;background:#ccc;background:rgba(0, 0, 0, 0.3);}
.dropdown.open .dropdown-menu{display:block;}
.pull-right .dropdown-menu{left:auto;right:0;}
.dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px solid #000000;content:"\2191";}
.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:1px;}
.typeahead{margin-top:2px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #eee;border:1px solid rgba(0, 0, 0, 0.05);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);}.well blockquote{border-color:#ddd;border-color:rgba(0, 0, 0, 0.15);}
.well-large{padding:24px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;}
.well-small{padding:9px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;}
.fade{-webkit-transition:opacity 0.15s linear;-moz-transition:opacity 0.15s linear;-ms-transition:opacity 0.15s linear;-o-transition:opacity 0.15s linear;transition:opacity 0.15s linear;opacity:0;}.fade.in{opacity:1;}
.collapse{-webkit-transition:height 0.35s ease;-moz-transition:height 0.35s ease;-ms-transition:height 0.35s ease;-o-transition:height 0.35s ease;transition:height 0.35s ease;position:relative;overflow:hidden;height:0;}.collapse.in{height:auto;}
.close{float:right;font-size:20px;font-weight:bold;line-height:18px;color:#000000;text-shadow:0 1px 0 #ffffff;opacity:0.2;filter:alpha(opacity=20);}.close:hover{color:#000000;text-decoration:none;opacity:0.4;filter:alpha(opacity=40);cursor:pointer;}
.btn{display:inline-block;*display:inline;*zoom:1;padding:4px 10px 4px;margin-bottom:0;font-size:13px;line-height:18px;color:#333333;text-align:center;text-shadow:0 1px 1px rgba(255, 255, 255, 0.75);vertical-align:middle;background-color:#f5f5f5;background-image:-moz-linear-gradient(top, #ffffff, #e6e6e6);background-image:-ms-linear-gradient(top, #ffffff, #e6e6e6);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#e6e6e6));background-image:-webkit-linear-gradient(top, #ffffff, #e6e6e6);background-image:-o-linear-gradient(top, #ffffff, #e6e6e6);background-image:linear-gradient(top, #ffffff, #e6e6e6);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#e6e6e6', GradientType=0);border-color:#e6e6e6 #e6e6e6 #bfbfbf;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:dximagetransform.microsoft.gradient(enabled=false);border:1px solid #cccccc;border-bottom-color:#b3b3b3;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);cursor:pointer;*margin-left:.3em;}.btn:hover,.btn:active,.btn.active,.btn.disabled,.btn[disabled]{background-color:#e6e6e6;}
.btn:active,.btn.active{background-color:#cccccc \9;}
.btn:first-child{*margin-left:0;}
.btn:hover{color:#333333;text-decoration:none;background-color:#e6e6e6;background-position:0 -15px;-webkit-transition:background-position 0.1s linear;-moz-transition:background-position 0.1s linear;-ms-transition:background-position 0.1s linear;-o-transition:background-position 0.1s linear;transition:background-position 0.1s linear;}
.btn:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px;}
.btn.active,.btn:active{background-image:none;-webkit-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);background-color:#e6e6e6;background-color:#d9d9d9 \9;outline:0;}
.btn.disabled,.btn[disabled]{cursor:default;background-image:none;background-color:#e6e6e6;opacity:0.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;}
.btn-large{padding:9px 14px;font-size:15px;line-height:normal;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;}
.btn-large [class^="icon-"]{margin-top:1px;}
.btn-small{padding:5px 9px;font-size:11px;line-height:16px;}
.btn-small [class^="icon-"]{margin-top:-1px;}
.btn-mini{padding:2px 6px;font-size:11px;line-height:14px;}
.btn-primary,.btn-primary:hover,.btn-warning,.btn-warning:hover,.btn-danger,.btn-danger:hover,.btn-success,.btn-success:hover,.btn-info,.btn-info:hover,.btn-inverse,.btn-inverse:hover{text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);color:#ffffff;}
.btn-primary.active,.btn-warning.active,.btn-danger.active,.btn-success.active,.btn-info.active,.btn-inverse.active{color:rgba(255, 255, 255, 0.75);}
.btn-primary{background-color:#0074cc;background-image:-moz-linear-gradient(top, #0088cc, #0055cc);background-image:-ms-linear-gradient(top, #0088cc, #0055cc);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0055cc));background-image:-webkit-linear-gradient(top, #0088cc, #0055cc);background-image:-o-linear-gradient(top, #0088cc, #0055cc);background-image:linear-gradient(top, #0088cc, #0055cc);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#0088cc', endColorstr='#0055cc', GradientType=0);border-color:#0055cc #0055cc #003580;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:dximagetransform.microsoft.gradient(enabled=false);}.btn-primary:hover,.btn-primary:active,.btn-primary.active,.btn-primary.disabled,.btn-primary[disabled]{background-color:#0055cc;}
.btn-primary:active,.btn-primary.active{background-color:#004099 \9;}
.btn-warning{background-color:#faa732;background-image:-moz-linear-gradient(top, #fbb450, #f89406);background-image:-ms-linear-gradient(top, #fbb450, #f89406);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406));background-image:-webkit-linear-gradient(top, #fbb450, #f89406);background-image:-o-linear-gradient(top, #fbb450, #f89406);background-image:linear-gradient(top, #fbb450, #f89406);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fbb450', endColorstr='#f89406', GradientType=0);border-color:#f89406 #f89406 #ad6704;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:dximagetransform.microsoft.gradient(enabled=false);}.btn-warning:hover,.btn-warning:active,.btn-warning.active,.btn-warning.disabled,.btn-warning[disabled]{background-color:#f89406;}
.btn-warning:active,.btn-warning.active{background-color:#c67605 \9;}
.btn-danger{background-color:#da4f49;background-image:-moz-linear-gradient(top, #ee5f5b, #bd362f);background-image:-ms-linear-gradient(top, #ee5f5b, #bd362f);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#bd362f));background-image:-webkit-linear-gradient(top, #ee5f5b, #bd362f);background-image:-o-linear-gradient(top, #ee5f5b, #bd362f);background-image:linear-gradient(top, #ee5f5b, #bd362f);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ee5f5b', endColorstr='#bd362f', GradientType=0);border-color:#bd362f #bd362f #802420;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:dximagetransform.microsoft.gradient(enabled=false);}.btn-danger:hover,.btn-danger:active,.btn-danger.active,.btn-danger.disabled,.btn-danger[disabled]{background-color:#bd362f;}
.btn-danger:active,.btn-danger.active{background-color:#942a25 \9;}
.btn-success{background-color:#5bb75b;background-image:-moz-linear-gradient(top, #62c462, #51a351);background-image:-ms-linear-gradient(top, #62c462, #51a351);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#51a351));background-image:-webkit-linear-gradient(top, #62c462, #51a351);background-image:-o-linear-gradient(top, #62c462, #51a351);background-image:linear-gradient(top, #62c462, #51a351);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462', endColorstr='#51a351', GradientType=0);border-color:#51a351 #51a351 #387038;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:dximagetransform.microsoft.gradient(enabled=false);}.btn-success:hover,.btn-success:active,.btn-success.active,.btn-success.disabled,.btn-success[disabled]{background-color:#51a351;}
.btn-success:active,.btn-success.active{background-color:#408140 \9;}
.btn-info{background-color:#49afcd;background-image:-moz-linear-gradient(top, #5bc0de, #2f96b4);background-image:-ms-linear-gradient(top, #5bc0de, #2f96b4);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#2f96b4));background-image:-webkit-linear-gradient(top, #5bc0de, #2f96b4);background-image:-o-linear-gradient(top, #5bc0de, #2f96b4);background-image:linear-gradient(top, #5bc0de, #2f96b4);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#5bc0de', endColorstr='#2f96b4', GradientType=0);border-color:#2f96b4 #2f96b4 #1f6377;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:dximagetransform.microsoft.gradient(enabled=false);}.btn-info:hover,.btn-info:active,.btn-info.active,.btn-info.disabled,.btn-info[disabled]{background-color:#2f96b4;}
.btn-info:active,.btn-info.active{background-color:#24748c \9;}
.btn-inverse{background-color:#414141;background-image:-moz-linear-gradient(top, #555555, #222222);background-image:-ms-linear-gradient(top, #555555, #222222);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#555555), to(#222222));background-image:-webkit-linear-gradient(top, #555555, #222222);background-image:-o-linear-gradient(top, #555555, #222222);background-image:linear-gradient(top, #555555, #222222);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#555555', endColorstr='#222222', GradientType=0);border-color:#222222 #222222 #000000;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:dximagetransform.microsoft.gradient(enabled=false);}.btn-inverse:hover,.btn-inverse:active,.btn-inverse.active,.btn-inverse.disabled,.btn-inverse[disabled]{background-color:#222222;}
.btn-inverse:active,.btn-inverse.active{background-color:#080808 \9;}
button.btn,input[type="submit"].btn{*padding-top:2px;*padding-bottom:2px;}button.btn::-moz-focus-inner,input[type="submit"].btn::-moz-focus-inner{padding:0;border:0;}
button.btn.btn-large,input[type="submit"].btn.btn-large{*padding-top:7px;*padding-bottom:7px;}
button.btn.btn-small,input[type="submit"].btn.btn-small{*padding-top:3px;*padding-bottom:3px;}
button.btn.btn-mini,input[type="submit"].btn.btn-mini{*padding-top:1px;*padding-bottom:1px;}
.btn-group{position:relative;*zoom:1;*margin-left:.3em;}.btn-group:before,.btn-group:after{display:table;content:"";}
.btn-group:after{clear:both;}
.btn-group:first-child{*margin-left:0;}
.btn-group+.btn-group{margin-left:5px;}
.btn-toolbar{margin-top:9px;margin-bottom:9px;}.btn-toolbar .btn-group{display:inline-block;*display:inline;*zoom:1;}
.btn-group .btn{position:relative;float:left;margin-left:-1px;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
.btn-group .btn:first-child{margin-left:0;-webkit-border-top-left-radius:4px;-moz-border-radius-topleft:4px;border-top-left-radius:4px;-webkit-border-bottom-left-radius:4px;-moz-border-radius-bottomleft:4px;border-bottom-left-radius:4px;}
.btn-group .btn:last-child,.btn-group .dropdown-toggle{-webkit-border-top-right-radius:4px;-moz-border-radius-topright:4px;border-top-right-radius:4px;-webkit-border-bottom-right-radius:4px;-moz-border-radius-bottomright:4px;border-bottom-right-radius:4px;}
.btn-group .btn.large:first-child{margin-left:0;-webkit-border-top-left-radius:6px;-moz-border-radius-topleft:6px;border-top-left-radius:6px;-webkit-border-bottom-left-radius:6px;-moz-border-radius-bottomleft:6px;border-bottom-left-radius:6px;}
.btn-group .btn.large:last-child,.btn-group .large.dropdown-toggle{-webkit-border-top-right-radius:6px;-moz-border-radius-topright:6px;border-top-right-radius:6px;-webkit-border-bottom-right-radius:6px;-moz-border-radius-bottomright:6px;border-bottom-right-radius:6px;}
.btn-group .btn:hover,.btn-group .btn:focus,.btn-group .btn:active,.btn-group .btn.active{z-index:2;}
.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0;}
.btn-group .dropdown-toggle{padding-left:8px;padding-right:8px;-webkit-box-shadow:inset 1px 0 0 rgba(255, 255, 255, 0.125),inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 1px 0 0 rgba(255, 255, 255, 0.125),inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 1px 0 0 rgba(255, 255, 255, 0.125),inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);*padding-top:3px;*padding-bottom:3px;}
.btn-group .btn-mini.dropdown-toggle{padding-left:5px;padding-right:5px;*padding-top:1px;*padding-bottom:1px;}
.btn-group .btn-small.dropdown-toggle{*padding-top:4px;*padding-bottom:4px;}
.btn-group .btn-large.dropdown-toggle{padding-left:12px;padding-right:12px;}
.btn-group.open{*z-index:1000;}.btn-group.open .dropdown-menu{display:block;margin-top:1px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;}
.btn-group.open .dropdown-toggle{background-image:none;-webkit-box-shadow:inset 0 1px 6px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 1px 6px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 1px 6px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);}
.btn .caret{margin-top:7px;margin-left:0;}
.btn:hover .caret,.open.btn-group .caret{opacity:1;filter:alpha(opacity=100);}
.btn-mini .caret{margin-top:5px;}
.btn-small .caret{margin-top:6px;}
.btn-large .caret{margin-top:6px;border-left:5px solid transparent;border-right:5px solid transparent;border-top:5px solid #000000;}
.btn-primary .caret,.btn-warning .caret,.btn-danger .caret,.btn-info .caret,.btn-success .caret,.btn-inverse .caret{border-top-color:#ffffff;border-bottom-color:#ffffff;opacity:0.75;filter:alpha(opacity=75);}
.alert{padding:8px 35px 8px 14px;margin-bottom:18px;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);background-color:#fcf8e3;border:1px solid #fbeed5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;color:#c09853;}
.alert-heading{color:inherit;}
.alert .close{position:relative;top:-2px;right:-21px;line-height:18px;}
.alert-success{background-color:#dff0d8;border-color:#d6e9c6;color:#468847;}
.alert-danger,.alert-error{background-color:#f2dede;border-color:#eed3d7;color:#b94a48;}
.alert-info{background-color:#d9edf7;border-color:#bce8f1;color:#3a87ad;}
.alert-block{padding-top:14px;padding-bottom:14px;}
.alert-block>p,.alert-block>ul{margin-bottom:0;}
.alert-block p+p{margin-top:5px;}
.nav{margin-left:0;margin-bottom:18px;list-style:none;}
.nav>li>a{display:block;}
.nav>li>a:hover{text-decoration:none;background-color:#eeeeee;}
.nav .nav-header{display:block;padding:3px 15px;font-size:11px;font-weight:bold;line-height:18px;color:#999999;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);text-transform:uppercase;}
.nav li+.nav-header{margin-top:9px;}
.nav-list{padding-left:15px;padding-right:15px;margin-bottom:0;}
.nav-list>li>a,.nav-list .nav-header{margin-left:-15px;margin-right:-15px;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);}
.nav-list>li>a{padding:3px 15px;}
.nav-list>.active>a,.nav-list>.active>a:hover{color:#ffffff;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.2);background-color:#0088cc;}
.nav-list [class^="icon-"]{margin-right:2px;}
.nav-list .divider{height:1px;margin:8px 1px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #ffffff;*width:100%;*margin:-5px 0 5px;}
.nav-tabs,.nav-pills{*zoom:1;}.nav-tabs:before,.nav-pills:before,.nav-tabs:after,.nav-pills:after{display:table;content:"";}
.nav-tabs:after,.nav-pills:after{clear:both;}
.nav-tabs>li,.nav-pills>li{float:left;}
.nav-tabs>li>a,.nav-pills>li>a{padding-right:12px;padding-left:12px;margin-right:2px;line-height:14px;}
.nav-tabs{border-bottom:1px solid #ddd;}
.nav-tabs>li{margin-bottom:-1px;}
.nav-tabs>li>a{padding-top:8px;padding-bottom:8px;line-height:18px;border:1px solid transparent;-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0;}.nav-tabs>li>a:hover{border-color:#eeeeee #eeeeee #dddddd;}
.nav-tabs>.active>a,.nav-tabs>.active>a:hover{color:#555555;background-color:#ffffff;border:1px solid #ddd;border-bottom-color:transparent;cursor:default;}
.nav-pills>li>a{padding-top:8px;padding-bottom:8px;margin-top:2px;margin-bottom:2px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;}
.nav-pills>.active>a,.nav-pills>.active>a:hover{color:#ffffff;background-color:#0088cc;}
.nav-stacked>li{float:none;}
.nav-stacked>li>a{margin-right:0;}
.nav-tabs.nav-stacked{border-bottom:0;}
.nav-tabs.nav-stacked>li>a{border:1px solid #ddd;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
.nav-tabs.nav-stacked>li:first-child>a{-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0;}
.nav-tabs.nav-stacked>li:last-child>a{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px;}
.nav-tabs.nav-stacked>li>a:hover{border-color:#ddd;z-index:2;}
.nav-pills.nav-stacked>li>a{margin-bottom:3px;}
.nav-pills.nav-stacked>li:last-child>a{margin-bottom:1px;}
.nav-tabs .dropdown-menu,.nav-pills .dropdown-menu{margin-top:1px;border-width:1px;}
.nav-pills .dropdown-menu{-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
.nav-tabs .dropdown-toggle .caret,.nav-pills .dropdown-toggle .caret{border-top-color:#0088cc;border-bottom-color:#0088cc;margin-top:6px;}
.nav-tabs .dropdown-toggle:hover .caret,.nav-pills .dropdown-toggle:hover .caret{border-top-color:#005580;border-bottom-color:#005580;}
.nav-tabs .active .dropdown-toggle .caret,.nav-pills .active .dropdown-toggle .caret{border-top-color:#333333;border-bottom-color:#333333;}
.nav>.dropdown.active>a:hover{color:#000000;cursor:pointer;}
.nav-tabs .open .dropdown-toggle,.nav-pills .open .dropdown-toggle,.nav>.open.active>a:hover{color:#ffffff;background-color:#999999;border-color:#999999;}
.nav .open .caret,.nav .open.active .caret,.nav .open a:hover .caret{border-top-color:#ffffff;border-bottom-color:#ffffff;opacity:1;filter:alpha(opacity=100);}
.tabs-stacked .open>a:hover{border-color:#999999;}
.tabbable{*zoom:1;}.tabbable:before,.tabbable:after{display:table;content:"";}
.tabbable:after{clear:both;}
.tab-content{display:table;width:100%;}
.tabs-below .nav-tabs,.tabs-right .nav-tabs,.tabs-left .nav-tabs{border-bottom:0;}
.tab-content>.tab-pane,.pill-content>.pill-pane{display:none;}
.tab-content>.active,.pill-content>.active{display:block;}
.tabs-below .nav-tabs{border-top:1px solid #ddd;}
.tabs-below .nav-tabs>li{margin-top:-1px;margin-bottom:0;}
.tabs-below .nav-tabs>li>a{-webkit-border-radius:0 0 4px 4px;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px;}.tabs-below .nav-tabs>li>a:hover{border-bottom-color:transparent;border-top-color:#ddd;}
.tabs-below .nav-tabs .active>a,.tabs-below .nav-tabs .active>a:hover{border-color:transparent #ddd #ddd #ddd;}
.tabs-left .nav-tabs>li,.tabs-right .nav-tabs>li{float:none;}
.tabs-left .nav-tabs>li>a,.tabs-right .nav-tabs>li>a{min-width:74px;margin-right:0;margin-bottom:3px;}
.tabs-left .nav-tabs{float:left;margin-right:19px;border-right:1px solid #ddd;}
.tabs-left .nav-tabs>li>a{margin-right:-1px;-webkit-border-radius:4px 0 0 4px;-moz-border-radius:4px 0 0 4px;border-radius:4px 0 0 4px;}
.tabs-left .nav-tabs>li>a:hover{border-color:#eeeeee #dddddd #eeeeee #eeeeee;}
.tabs-left .nav-tabs .active>a,.tabs-left .nav-tabs .active>a:hover{border-color:#ddd transparent #ddd #ddd;*border-right-color:#ffffff;}
.tabs-right .nav-tabs{float:right;margin-left:19px;border-left:1px solid #ddd;}
.tabs-right .nav-tabs>li>a{margin-left:-1px;-webkit-border-radius:0 4px 4px 0;-moz-border-radius:0 4px 4px 0;border-radius:0 4px 4px 0;}
.tabs-right .nav-tabs>li>a:hover{border-color:#eeeeee #eeeeee #eeeeee #dddddd;}
.tabs-right .nav-tabs .active>a,.tabs-right .nav-tabs .active>a:hover{border-color:#ddd #ddd #ddd transparent;*border-left-color:#ffffff;}
.navbar{*position:relative;*z-index:2;overflow:visible;margin-bottom:18px;}
.navbar-inner{padding-left:20px;padding-right:20px;background-color:#2c2c2c;background-image:-moz-linear-gradient(top, #333333, #222222);background-image:-ms-linear-gradient(top, #333333, #222222);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#333333), to(#222222));background-image:-webkit-linear-gradient(top, #333333, #222222);background-image:-o-linear-gradient(top, #333333, #222222);background-image:linear-gradient(top, #333333, #222222);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#333333', endColorstr='#222222', GradientType=0);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 1px 3px rgba(0, 0, 0, 0.25),inset 0 -1px 0 rgba(0, 0, 0, 0.1);-moz-box-shadow:0 1px 3px rgba(0, 0, 0, 0.25),inset 0 -1px 0 rgba(0, 0, 0, 0.1);box-shadow:0 1px 3px rgba(0, 0, 0, 0.25),inset 0 -1px 0 rgba(0, 0, 0, 0.1);}
.navbar .container{width:auto;}
.btn-navbar{display:none;float:right;padding:7px 10px;margin-left:5px;margin-right:5px;background-color:#2c2c2c;background-image:-moz-linear-gradient(top, #333333, #222222);background-image:-ms-linear-gradient(top, #333333, #222222);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#333333), to(#222222));background-image:-webkit-linear-gradient(top, #333333, #222222);background-image:-o-linear-gradient(top, #333333, #222222);background-image:linear-gradient(top, #333333, #222222);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#333333', endColorstr='#222222', GradientType=0);border-color:#222222 #222222 #000000;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);filter:progid:dximagetransform.microsoft.gradient(enabled=false);-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.075);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.075);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.075);}.btn-navbar:hover,.btn-navbar:active,.btn-navbar.active,.btn-navbar.disabled,.btn-navbar[disabled]{background-color:#222222;}
.btn-navbar:active,.btn-navbar.active{background-color:#080808 \9;}
.btn-navbar .icon-bar{display:block;width:18px;height:2px;background-color:#f5f5f5;-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px;-webkit-box-shadow:0 1px 0 rgba(0, 0, 0, 0.25);-moz-box-shadow:0 1px 0 rgba(0, 0, 0, 0.25);box-shadow:0 1px 0 rgba(0, 0, 0, 0.25);}
.btn-navbar .icon-bar+.icon-bar{margin-top:3px;}
.nav-collapse.collapse{height:auto;}
.navbar{color:#999999;}.navbar .brand:hover{text-decoration:none;}
.navbar .brand{float:left;display:block;padding:8px 20px 12px;margin-left:-20px;font-size:20px;font-weight:200;line-height:1;color:#ffffff;}
.navbar .navbar-text{margin-bottom:0;line-height:40px;}
.navbar .btn,.navbar .btn-group{margin-top:5px;}
.navbar .btn-group .btn{margin-top:0;}
.navbar-form{margin-bottom:0;*zoom:1;}.navbar-form:before,.navbar-form:after{display:table;content:"";}
.navbar-form:after{clear:both;}
.navbar-form input,.navbar-form select,.navbar-form .radio,.navbar-form .checkbox{margin-top:5px;}
.navbar-form input,.navbar-form select{display:inline-block;margin-bottom:0;}
.navbar-form input[type="image"],.navbar-form input[type="checkbox"],.navbar-form input[type="radio"]{margin-top:3px;}
.navbar-form .input-append,.navbar-form .input-prepend{margin-top:6px;white-space:nowrap;}.navbar-form .input-append input,.navbar-form .input-prepend input{margin-top:0;}
.navbar-search{position:relative;float:left;margin-top:6px;margin-bottom:0;}.navbar-search .search-query{padding:4px 9px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;font-weight:normal;line-height:1;color:#ffffff;background-color:#626262;border:1px solid #151515;-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1),0 1px 0px rgba(255, 255, 255, 0.15);-moz-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1),0 1px 0px rgba(255, 255, 255, 0.15);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1),0 1px 0px rgba(255, 255, 255, 0.15);-webkit-transition:none;-moz-transition:none;-ms-transition:none;-o-transition:none;transition:none;}.navbar-search .search-query:-moz-placeholder{color:#cccccc;}
.navbar-search .search-query::-webkit-input-placeholder{color:#cccccc;}
.navbar-search .search-query:focus,.navbar-search .search-query.focused{padding:5px 10px;color:#333333;text-shadow:0 1px 0 #ffffff;background-color:#ffffff;border:0;-webkit-box-shadow:0 0 3px rgba(0, 0, 0, 0.15);-moz-box-shadow:0 0 3px rgba(0, 0, 0, 0.15);box-shadow:0 0 3px rgba(0, 0, 0, 0.15);outline:0;}
.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030;margin-bottom:0;}
.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding-left:0;padding-right:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;}
.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:940px;}
.navbar-fixed-top{top:0;}
.navbar-fixed-bottom{bottom:0;}
.navbar .nav{position:relative;left:0;display:block;float:left;margin:0 10px 0 0;}
.navbar .nav.pull-right{float:right;}
.navbar .nav>li{display:block;float:left;}
.navbar .nav>li>a{float:none;padding:10px 10px 11px;line-height:19px;color:#999999;text-decoration:none;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);}
.navbar .nav>li>a:hover{background-color:transparent;color:#ffffff;text-decoration:none;}
.navbar .nav .active>a,.navbar .nav .active>a:hover{color:#ffffff;text-decoration:none;background-color:#222222;}
.navbar .divider-vertical{height:40px;width:1px;margin:0 9px;overflow:hidden;background-color:#222222;border-right:1px solid #333333;}
.navbar .nav.pull-right{margin-left:10px;margin-right:0;}
.navbar .dropdown-menu{margin-top:1px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}.navbar .dropdown-menu:before{content:'';display:inline-block;border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-bottom-color:rgba(0, 0, 0, 0.2);position:absolute;top:-7px;left:9px;}
.navbar .dropdown-menu:after{content:'';display:inline-block;border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #ffffff;position:absolute;top:-6px;left:10px;}
.navbar-fixed-bottom .dropdown-menu:before{border-top:7px solid #ccc;border-top-color:rgba(0, 0, 0, 0.2);border-bottom:0;bottom:-7px;top:auto;}
.navbar-fixed-bottom .dropdown-menu:after{border-top:6px solid #ffffff;border-bottom:0;bottom:-6px;top:auto;}
.navbar .nav .dropdown-toggle .caret,.navbar .nav .open.dropdown .caret{border-top-color:#ffffff;border-bottom-color:#ffffff;}
.navbar .nav .active .caret{opacity:1;filter:alpha(opacity=100);}
.navbar .nav .open>.dropdown-toggle,.navbar .nav .active>.dropdown-toggle,.navbar .nav .open.active>.dropdown-toggle{background-color:transparent;}
.navbar .nav .active>.dropdown-toggle:hover{color:#ffffff;}
.navbar .nav.pull-right .dropdown-menu,.navbar .nav .dropdown-menu.pull-right{left:auto;right:0;}.navbar .nav.pull-right .dropdown-menu:before,.navbar .nav .dropdown-menu.pull-right:before{left:auto;right:12px;}
.navbar .nav.pull-right .dropdown-menu:after,.navbar .nav .dropdown-menu.pull-right:after{left:auto;right:13px;}
.breadcrumb{padding:7px 14px;margin:0 0 18px;list-style:none;background-color:#fbfbfb;background-image:-moz-linear-gradient(top, #ffffff, #f5f5f5);background-image:-ms-linear-gradient(top, #ffffff, #f5f5f5);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#f5f5f5));background-image:-webkit-linear-gradient(top, #ffffff, #f5f5f5);background-image:-o-linear-gradient(top, #ffffff, #f5f5f5);background-image:linear-gradient(top, #ffffff, #f5f5f5);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#f5f5f5', GradientType=0);border:1px solid #ddd;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:inset 0 1px 0 #ffffff;-moz-box-shadow:inset 0 1px 0 #ffffff;box-shadow:inset 0 1px 0 #ffffff;}.breadcrumb li{display:inline-block;*display:inline;*zoom:1;text-shadow:0 1px 0 #ffffff;}
.breadcrumb .divider{padding:0 5px;color:#999999;}
.breadcrumb .active a{color:#333333;}
.pagination{height:36px;margin:18px 0;}
.pagination ul{display:inline-block;*display:inline;*zoom:1;margin-left:0;margin-bottom:0;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);}
.pagination li{display:inline;}
.pagination a{float:left;padding:0 14px;line-height:34px;text-decoration:none;border:1px solid #ddd;border-left-width:0;}
.pagination a:hover,.pagination .active a{background-color:#f5f5f5;}
.pagination .active a{color:#999999;cursor:default;}
.pagination .disabled span,.pagination .disabled a,.pagination .disabled a:hover{color:#999999;background-color:transparent;cursor:default;}
.pagination li:first-child a{border-left-width:1px;-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;}
.pagination li:last-child a{-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;}
.pagination-centered{text-align:center;}
.pagination-right{text-align:right;}
.pager{margin-left:0;margin-bottom:18px;list-style:none;text-align:center;*zoom:1;}.pager:before,.pager:after{display:table;content:"";}
.pager:after{clear:both;}
.pager li{display:inline;}
.pager a{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px;}
.pager a:hover{text-decoration:none;background-color:#f5f5f5;}
.pager .next a{float:right;}
.pager .previous a{float:left;}
.pager .disabled a,.pager .disabled a:hover{color:#999999;background-color:#fff;cursor:default;}
.modal-open .dropdown-menu{z-index:2050;}
.modal-open .dropdown.open{*z-index:2050;}
.modal-open .popover{z-index:2060;}
.modal-open .tooltip{z-index:2070;}
.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000000;}.modal-backdrop.fade{opacity:0;}
.modal-backdrop,.modal-backdrop.fade.in{opacity:0.8;filter:alpha(opacity=80);}
.modal{position:fixed;top:50%;left:50%;z-index:1050;overflow:auto;width:560px;margin:-250px 0 0 -280px;background-color:#ffffff;border:1px solid #999;border:1px solid rgba(0, 0, 0, 0.3);*border:1px solid #999;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-moz-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box;}.modal.fade{-webkit-transition:opacity .3s linear, top .3s ease-out;-moz-transition:opacity .3s linear, top .3s ease-out;-ms-transition:opacity .3s linear, top .3s ease-out;-o-transition:opacity .3s linear, top .3s ease-out;transition:opacity .3s linear, top .3s ease-out;top:-25%;}
.modal.fade.in{top:50%;}
.modal-header{padding:9px 15px;border-bottom:1px solid #eee;}.modal-header .close{margin-top:2px;}
.modal-body{overflow-y:auto;max-height:400px;padding:15px;}
.modal-form{margin-bottom:0;}
.modal-footer{padding:14px 15px 15px;margin-bottom:0;text-align:right;background-color:#f5f5f5;border-top:1px solid #ddd;-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;-webkit-box-shadow:inset 0 1px 0 #ffffff;-moz-box-shadow:inset 0 1px 0 #ffffff;box-shadow:inset 0 1px 0 #ffffff;*zoom:1;}.modal-footer:before,.modal-footer:after{display:table;content:"";}
.modal-footer:after{clear:both;}
.modal-footer .btn+.btn{margin-left:5px;margin-bottom:0;}
.modal-footer .btn-group .btn+.btn{margin-left:-1px;}
.tooltip{position:absolute;z-index:1020;display:block;visibility:visible;padding:5px;font-size:11px;opacity:0;filter:alpha(opacity=0);}.tooltip.in{opacity:0.8;filter:alpha(opacity=80);}
.tooltip.top{margin-top:-2px;}
.tooltip.right{margin-left:2px;}
.tooltip.bottom{margin-top:2px;}
.tooltip.left{margin-left:-2px;}
.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-top:5px solid #000000;}
.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-left:5px solid #000000;}
.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-bottom:5px solid #000000;}
.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-right:5px solid #000000;}
.tooltip-inner{max-width:200px;padding:3px 8px;color:#ffffff;text-align:center;text-decoration:none;background-color:#000000;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
.tooltip-arrow{position:absolute;width:0;height:0;}
.popover{position:absolute;top:0;left:0;z-index:1010;display:none;padding:5px;}.popover.top{margin-top:-5px;}
.popover.right{margin-left:5px;}
.popover.bottom{margin-top:5px;}
.popover.left{margin-left:-5px;}
.popover.top .arrow{bottom:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-top:5px solid #000000;}
.popover.right .arrow{top:50%;left:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-right:5px solid #000000;}
.popover.bottom .arrow{top:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-bottom:5px solid #000000;}
.popover.left .arrow{top:50%;right:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-left:5px solid #000000;}
.popover .arrow{position:absolute;width:0;height:0;}
.popover-inner{padding:3px;width:280px;overflow:hidden;background:#000000;background:rgba(0, 0, 0, 0.8);-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-moz-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);}
.popover-title{padding:9px 15px;line-height:1;background-color:#f5f5f5;border-bottom:1px solid #eee;-webkit-border-radius:3px 3px 0 0;-moz-border-radius:3px 3px 0 0;border-radius:3px 3px 0 0;}
.popover-content{padding:14px;background-color:#ffffff;-webkit-border-radius:0 0 3px 3px;-moz-border-radius:0 0 3px 3px;border-radius:0 0 3px 3px;-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box;}.popover-content p,.popover-content ul,.popover-content ol{margin-bottom:0;}
.thumbnails{margin-left:-20px;list-style:none;*zoom:1;}.thumbnails:before,.thumbnails:after{display:table;content:"";}
.thumbnails:after{clear:both;}
.thumbnails>li{float:left;margin:0 0 18px 20px;}
.thumbnail{display:block;padding:4px;line-height:1;border:1px solid #ddd;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0, 0, 0, 0.075);-moz-box-shadow:0 1px 1px rgba(0, 0, 0, 0.075);box-shadow:0 1px 1px rgba(0, 0, 0, 0.075);}
a.thumbnail:hover{border-color:#0088cc;-webkit-box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);-moz-box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);}
.thumbnail>img{display:block;max-width:100%;margin-left:auto;margin-right:auto;}
.thumbnail .caption{padding:9px;}
.label{padding:1px 4px 2px;font-size:10.998px;font-weight:bold;line-height:13px;color:#ffffff;vertical-align:middle;white-space:nowrap;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#999999;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;}
.label:hover{color:#ffffff;text-decoration:none;}
.label-important{background-color:#b94a48;}
.label-important:hover{background-color:#953b39;}
.label-warning{background-color:#f89406;}
.label-warning:hover{background-color:#c67605;}
.label-success{background-color:#468847;}
.label-success:hover{background-color:#356635;}
.label-info{background-color:#3a87ad;}
.label-info:hover{background-color:#2d6987;}
.label-inverse{background-color:#333333;}
.label-inverse:hover{background-color:#1a1a1a;}
.badge{padding:1px 9px 2px;font-size:12.025px;font-weight:bold;white-space:nowrap;color:#ffffff;background-color:#999999;-webkit-border-radius:9px;-moz-border-radius:9px;border-radius:9px;}
.badge:hover{color:#ffffff;text-decoration:none;cursor:pointer;}
.badge-error{background-color:#b94a48;}
.badge-error:hover{background-color:#953b39;}
.badge-warning{background-color:#f89406;}
.badge-warning:hover{background-color:#c67605;}
.badge-success{background-color:#468847;}
.badge-success:hover{background-color:#356635;}
.badge-info{background-color:#3a87ad;}
.badge-info:hover{background-color:#2d6987;}
.badge-inverse{background-color:#333333;}
.badge-inverse:hover{background-color:#1a1a1a;}
@-webkit-keyframes progress-bar-stripes{from{background-position:0 0;} to{background-position:40px 0;}}@-moz-keyframes progress-bar-stripes{from{background-position:0 0;} to{background-position:40px 0;}}@-ms-keyframes progress-bar-stripes{from{background-position:0 0;} to{background-position:40px 0;}}@keyframes progress-bar-stripes{from{background-position:0 0;} to{background-position:40px 0;}}.progress{overflow:hidden;height:18px;margin-bottom:18px;background-color:#f7f7f7;background-image:-moz-linear-gradient(top, #f5f5f5, #f9f9f9);background-image:-ms-linear-gradient(top, #f5f5f5, #f9f9f9);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#f5f5f5), to(#f9f9f9));background-image:-webkit-linear-gradient(top, #f5f5f5, #f9f9f9);background-image:-o-linear-gradient(top, #f5f5f5, #f9f9f9);background-image:linear-gradient(top, #f5f5f5, #f9f9f9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#f5f5f5', endColorstr='#f9f9f9', GradientType=0);-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1);-moz-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
.progress .bar{width:0%;height:18px;color:#ffffff;font-size:12px;text-align:center;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);background-color:#0e90d2;background-image:-moz-linear-gradient(top, #149bdf, #0480be);background-image:-ms-linear-gradient(top, #149bdf, #0480be);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#149bdf), to(#0480be));background-image:-webkit-linear-gradient(top, #149bdf, #0480be);background-image:-o-linear-gradient(top, #149bdf, #0480be);background-image:linear-gradient(top, #149bdf, #0480be);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#149bdf', endColorstr='#0480be', GradientType=0);-webkit-box-shadow:inset 0 -1px 0 rgba(0, 0, 0, 0.15);-moz-box-shadow:inset 0 -1px 0 rgba(0, 0, 0, 0.15);box-shadow:inset 0 -1px 0 rgba(0, 0, 0, 0.15);-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;-webkit-transition:width 0.6s ease;-moz-transition:width 0.6s ease;-ms-transition:width 0.6s ease;-o-transition:width 0.6s ease;transition:width 0.6s ease;}
.progress-striped .bar{background-color:#149bdf;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);-webkit-background-size:40px 40px;-moz-background-size:40px 40px;-o-background-size:40px 40px;background-size:40px 40px;}
.progress.active .bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-moz-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite;}
.progress-danger .bar{background-color:#dd514c;background-image:-moz-linear-gradient(top, #ee5f5b, #c43c35);background-image:-ms-linear-gradient(top, #ee5f5b, #c43c35);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#c43c35));background-image:-webkit-linear-gradient(top, #ee5f5b, #c43c35);background-image:-o-linear-gradient(top, #ee5f5b, #c43c35);background-image:linear-gradient(top, #ee5f5b, #c43c35);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ee5f5b', endColorstr='#c43c35', GradientType=0);}
.progress-danger.progress-striped .bar{background-color:#ee5f5b;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);}
.progress-success .bar{background-color:#5eb95e;background-image:-moz-linear-gradient(top, #62c462, #57a957);background-image:-ms-linear-gradient(top, #62c462, #57a957);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#57a957));background-image:-webkit-linear-gradient(top, #62c462, #57a957);background-image:-o-linear-gradient(top, #62c462, #57a957);background-image:linear-gradient(top, #62c462, #57a957);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462', endColorstr='#57a957', GradientType=0);}
.progress-success.progress-striped .bar{background-color:#62c462;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);}
.progress-info .bar{background-color:#4bb1cf;background-image:-moz-linear-gradient(top, #5bc0de, #339bb9);background-image:-ms-linear-gradient(top, #5bc0de, #339bb9);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#339bb9));background-image:-webkit-linear-gradient(top, #5bc0de, #339bb9);background-image:-o-linear-gradient(top, #5bc0de, #339bb9);background-image:linear-gradient(top, #5bc0de, #339bb9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#5bc0de', endColorstr='#339bb9', GradientType=0);}
.progress-info.progress-striped .bar{background-color:#5bc0de;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);}
.progress-warning .bar{background-color:#faa732;background-image:-moz-linear-gradient(top, #fbb450, #f89406);background-image:-ms-linear-gradient(top, #fbb450, #f89406);background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406));background-image:-webkit-linear-gradient(top, #fbb450, #f89406);background-image:-o-linear-gradient(top, #fbb450, #f89406);background-image:linear-gradient(top, #fbb450, #f89406);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fbb450', endColorstr='#f89406', GradientType=0);}
.progress-warning.progress-striped .bar{background-color:#fbb450;background-image:-webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));background-image:-webkit-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-moz-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-ms-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);}
.accordion{margin-bottom:18px;}
.accordion-group{margin-bottom:2px;border:1px solid #e5e5e5;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
.accordion-heading{border-bottom:0;}
.accordion-heading .accordion-toggle{display:block;padding:8px 15px;}
.accordion-inner{padding:9px 15px;border-top:1px solid #e5e5e5;}
.carousel{position:relative;margin-bottom:18px;line-height:1;}
.carousel-inner{overflow:hidden;width:100%;position:relative;}
.carousel .item{display:none;position:relative;-webkit-transition:0.6s ease-in-out left;-moz-transition:0.6s ease-in-out left;-ms-transition:0.6s ease-in-out left;-o-transition:0.6s ease-in-out left;transition:0.6s ease-in-out left;}
.carousel .item>img{display:block;line-height:1;}
.carousel .active,.carousel .next,.carousel .prev{display:block;}
.carousel .active{left:0;}
.carousel .next,.carousel .prev{position:absolute;top:0;width:100%;}
.carousel .next{left:100%;}
.carousel .prev{left:-100%;}
.carousel .next.left,.carousel .prev.right{left:0;}
.carousel .active.left{left:-100%;}
.carousel .active.right{left:100%;}
.carousel-control{position:absolute;top:40%;left:15px;width:40px;height:40px;margin-top:-20px;font-size:60px;font-weight:100;line-height:30px;color:#ffffff;text-align:center;background:#222222;border:3px solid #ffffff;-webkit-border-radius:23px;-moz-border-radius:23px;border-radius:23px;opacity:0.5;filter:alpha(opacity=50);}.carousel-control.right{left:auto;right:15px;}
.carousel-control:hover{color:#ffffff;text-decoration:none;opacity:0.9;filter:alpha(opacity=90);}
.carousel-caption{position:absolute;left:0;right:0;bottom:0;padding:10px 15px 5px;background:#333333;background:rgba(0, 0, 0, 0.75);}
.carousel-caption h4,.carousel-caption p{color:#ffffff;}
.hero-unit{padding:60px;margin-bottom:30px;background-color:#eeeeee;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;}.hero-unit h1{margin-bottom:0;font-size:60px;line-height:1;color:inherit;letter-spacing:-1px;}
.hero-unit p{font-size:18px;font-weight:200;line-height:27px;color:inherit;}
.pull-right{float:right;}
.pull-left{float:left;}
.hide{display:none;}
.show{display:block;}
.invisible{visibility:hidden;}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,34 @@
/* desert scheme ported from vim to google prettify */
pre.prettyprint { display: block; background-color: #333 }
pre .nocode { background-color: none; color: #000 }
pre .str { color: #ffa0a0 } /* string - pink */
pre .kwd { color: #f0e68c; font-weight: bold }
pre .com { color: #87ceeb } /* comment - skyblue */
pre .typ { color: #98fb98 } /* type - lightgreen */
pre .lit { color: #cd5c5c } /* literal - darkred */
pre .pun { color: #fff } /* punctuation */
pre .pln { color: #fff } /* plaintext */
pre .tag { color: #f0e68c; font-weight: bold } /* html/xml tag - lightyellow */
pre .atn { color: #bdb76b; font-weight: bold } /* attribute name - khaki */
pre .atv { color: #ffa0a0 } /* attribute value - pink */
pre .dec { color: #98fb98 } /* decimal - lightgreen */
/* Specify class=linenums on a pre to get line numbering */
ol.linenums { margin-top: 0; margin-bottom: 0; color: #AEAEAE } /* IE indents via margin-left */
li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8 { list-style-type: none }
/* Alternate shading for lines */
li.L1,li.L3,li.L5,li.L7,li.L9 { }
@media print {
pre.prettyprint { background-color: none }
pre .str, code .str { color: #060 }
pre .kwd, code .kwd { color: #006; font-weight: bold }
pre .com, code .com { color: #600; font-style: italic }
pre .typ, code .typ { color: #404; font-weight: bold }
pre .lit, code .lit { color: #044 }
pre .pun, code .pun { color: #440 }
pre .pln, code .pln { color: #000 }
pre .tag, code .tag { color: #006; font-weight: bold }
pre .atn, code .atn { color: #404 }
pre .atv, code .atv { color: #060 }
}

View File

@ -8,18 +8,17 @@
border: 25px solid red;
}
body {
color: #eee;
}
/* logo */
.brand {
font-size: 38px !important;
padding: 0 55px 3px !important;
padding: 0 !important;
margin-left: 10%;
text-shadow: 0 1px 0 rgba(255, 255, 255, .1), 0 0 30px rgba(255, 255, 255, .125);
-webkit-transition: all .2s linear;
-moz-transition: all .2s linear;
-o-transition: all .2s linear;
-ms-transition: all .2s linear;
transition: all .2s linear;
}
.brand span {
@ -51,9 +50,9 @@
/* body & other stuff */
body {
padding-top: 60px;
padding-bottom: 40px;
.blk-space {
height: 20px;
display: block;
}
.sidebar-nav {
@ -64,22 +63,18 @@ select {
width: 135px;
}
label {
display: inline;
margin-left: 18px;
font-style: italic;
font-size: 11px;
color: #888;
}
ul,
ol {
padding: 0;
margin: 0;
}
li {
margin-left: -9px;
a {
color: #2ea1d7;
}
a:hover {
color: #1888bc;
}
p {
@ -100,48 +95,56 @@ blockquote {
float: left;
}
h4 p {
float: left;
font-size: 80px;
text-shadow: 1px 3px 1px #DDD, 0 0 4px #333;
-webkit-transition: all 0.2s linear;
-o-transition: all .2s linear;
-ms-transition: all .2s linear;
-moz-transition: all .2s linear;
transition: all .2s linear;
margin-right: 7px;
margin-top: 3px;
}
h4#pixels-total {
position: relative;
width: 166px;
float: right;
margin: 8px 0 0 0;
padding: 0 0 0 54px;
font-size: 1.1em;
line-height: 1.4;
font-weight: normal;
color: #777;
-webkit-border-top-right-radius: 6px;
-webkit-border-top-left-radius: 20px;
-moz-border-top-right-radius: 6px;
-moz-border-top-left-radius: 20px;
border-top-right-radius: 6px;
border-top-left-radius: 20px;
}
.greetings {
clear: both;
margin: 0 auto;
text-align: center;
margin-top: 40px;
}
.alert .title {
display: block;
}
/* Footer */
/* See:
https://philipwalton.github.io/solved-by-flexbox/demos/sticky-footer/
*/
#app {
display: flex;
min-height: 100vh;
flex-direction: column;
}
#wrap-content {
flex: 1;
}
.footer {
text-align: center;
height: 60px;
width: 100%;
position: relative;
display: block;
margin: 0px 0px 0px 0px;
padding: 8px 0 0 0;
background-color: #424141;
}
.footer li {
list-style-type: none;
display: inline;
}
.footer li span {
color: #2ea1d7;
}
.footer li span:hover {
color: #1888bc;
text-decoration: underline;
cursor: pointer;
}
.footer li:not(:last-child):after {
content: ' -';
}
/* Home */
.btn-group {
@ -172,23 +175,112 @@ input.btn-upload {
input.hide-upload {
position: relative;
left: -110px;
left: -80px;
-moz-opacity: 0;
filter: alpha(opacity=0);
opacity: 0;
z-index: 2;
width: 100px;
width: 0;
margin-top: -20px;
cursor: pointer;
cursor: hand;
height: 49px;
height: 24px;
}
/* Paste Page */
#paste-content {
background-color: white;
padding: 1em;
h1 {
font-size: 2em;
}
.reader-mode-title {
text-align: center;
margin-bottom: 30px;
}
.paste-options-res {
text-align: center;
margin-bottom: 10px;
opacity: 0.9 !important;
}
.reader-mode-tools {
display: none !important;
}
.paste-options-res .btn-group {
float: none;
border: 0px;
}
.paste-options-res span {
border: 0px;
}
.paste-form {
position: relative;
}
#expiration-tag {
position: absolute;
right: 10px;
margin: 1em;
background: grey;
color: white;
font-size: 0.8em;
padding: 0 1ex;
opacity: 0.5;
filter: alpha(opacity=50);
font-weight: bold;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
-moz-background-clip: padding;
-webkit-background-clip: padding-box;
background-clip: padding-box;
}
.paste-options,
.paste-options-res {
background-color: #333333;
padding: 10px;
opacity: 0.5;
}
.paste-options-res svg {
margin-left: 8px;
}
.paste-options span {
background-color: #3c4a59;
color: #f9fafc;
width: 70px;
}
.paste-options:hover{
opacity: 1;
}
.paste-options input{
background-color: #375A7F;
color: #eee;
}
::placeholder {
color: #f9fafc !important;
opacity: 1;
}
.paste-wrapper {
width: 100%;
text-align: center;
background-color: #333333;
}
.paste-wrapper img {
max-width: 100%;
}
.pre-wrapper {
margin: 8px 0 8px 0;
}
#paste-content.linenums {
@ -199,8 +291,8 @@ input.hide-upload {
display: none;
}
.paste-option {
float: right;
.tip svg{
margin-left: 6px;
}
a#clip-button.hover {
@ -222,10 +314,15 @@ li.L9 {
background: inherit;
}
.prettyprint.linenums {
-webkit-box-shadow: inset 40px 0 0 #fbfbfc, inset 41px 0 0 #ececf0;
-moz-box-shadow: inset 40px 0 0 #fbfbfc, inset 41px 0 0 #ececf0;
box-shadow: inset 40px 0 0 #fbfbfc, inset 41px 0 0 #ececf0;
pre.prettyprint {
width: 100%;
min-height: 100px;
padding: 5px;
word-break: break-word;
margin-bottom: 0px;
/* background-repeat: no-repeat;
background-position: 0 calc(100% + 6px);
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1440 330'%3E%3Cpath fill='%23273036' fill-opacity='0.1' d='M0,128L40,154.7C80,181,160,235,240,266.7C320,299,400,309,480,304C560,299,640,277,720,240C800,203,880,149,960,160C1040,171,1120,245,1200,261.3C1280,277,1360,235,1400,213.3L1440,192L1440,320L1400,320C1360,320,1280,320,1200,320C1120,320,1040,320,960,320C880,320,800,320,720,320C640,320,560,320,480,320C400,320,320,320,240,320C160,320,80,320,40,320L0,320Z'%3E%3C/path%3E%3C/svg%3E"); */
}
/* Specify class=linenums on a pre to get line numbering */
@ -234,48 +331,87 @@ ol.linenums {
/* IE indents via margin-left */
}
ol.linenums li {
color: #bebec5;
line-height: 18px;
text-shadow: 0 1px 0 #fff;
}
.prettyprint {
padding: 8px;
background-color: #f7f7f9;
border: 1px solid #e1e1e8;
}
pre {
font-family: Consolas, Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, Bitstream Vera Sans Mono, monospace, serif;
line-height: 21px;
font-size: 12px;
white-space: pre-wrap;
overflow: visible;
}
.kwd {
color: #66F;
}
.pun,
.opn,
.clo {
color: #0A0;
}
.lit {
color: #933;
}
.com {
color: #C0C;
}
/* Common css */
form {
padding-bottom: 3em !important;
padding-right: 17px;
#faq {
max-width: 80%;
margin: auto;
}
#buy_bitcoin {
max-width: 80%;
margin: auto;
}
#buy_bitcoin img{
width: 85%;
display: block;
margin: 20px auto;
}
.caret {
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-top: 8px solid #f9fafc;
width: 1px;
margin: 8px 0px 0px 4px;
display: inline-block;
}
.form-control,
.form-control:focus,
.form-control:disabled,
.form-control[readonly],
#paste-content {
background-color: #375a7f;
color: #f9fafc;
border-radius: 3px;
}
.upload-file {
min-width: 100px;
margin-right: 4px;
}
.form-group {
margin-bottom: 0;
}
.download-link {
border-top-left-radius: 3px !important;
border-bottom-left-radius: 3px !important;
}
.download-link a,
.buy-btc a {
color: #eee;
}
.download-link a:hover,
.buy-btc a:hover {
text-decoration: none;
}
.select-date {
width: 320px;
float: right;
}
.select-date-clone {
width: 60%;
max-width: 320px;
}
.container-md {
margin-top: 20px;
padding-bottom: 130px;
}
form textarea {
@ -283,16 +419,6 @@ form textarea {
min-height: 250px;
}
button.btn,
input[type="submit"].btn {
margin-left: 5px;
}
.well {
padding-bottom: 40px;
padding-right: 17px;
}
.legal {
margin: 0 auto;
width: 300px;
@ -300,51 +426,28 @@ input[type="submit"].btn {
margin-top: 30px;
}
.btn {
margin-left: 5px;
}
.btn-primary,
.btn-danger {
position: relative;
top: -4px;
#alert-template {
display: none;
}
/** Progress bar */
.progress {
width: 100%;
margin: 8px 0 8px 0;
height: 1.5rem;
}
.progress .bar {
width: 25%;
text-indent: 10px;
padding: 12px;
text-align: left;
}
.lnk-option canvas {
vertical-align: middle;
margin-right: 10px;
}
/* Previous paste list */
.previous-pastes .item {
margin-top: 5px;
vertical-align: middle;
line-height: 24px;
padding-left: 1em;
}
li.item {
margin-left: -13px;
margin-right: -5px;
}
.previous-pastes canvas {
display: block;
float: left;
margin-right: 5px;
.progress-container {
margin: 8px 0 8px 0;
clear: both;
}
html.local-storage .no-local-storage {
@ -377,24 +480,254 @@ canvas {
text-decoration: underline;
}
#expiration-tag {
float: right;
margin: 1em;
background: grey;
color: white;
font-size: 0.8em;
padding: 0 1ex;
opacity: 0.5;
filter: alpha(opacity=50);
font-weight: bold;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
-moz-background-clip: padding;
-webkit-background-clip: padding-box;
background-clip: padding-box;
}
#content.hover {
background-color: #eee;
}
/* top Menu */
.topnav {
height: 60px;
background-color: #333;
}
.topnav .brand {
float: left;
display: block;
color: #f2f2f2;
text-align: center;
padding: 14px 16px;
text-decoration: none;
font-size: 17px;
width: calc(22% - 30px);
}
.topnav .tagline {
font-size: 0.9em;
padding: 8px;
float: left;
margin-left: 20px;
}
.topnav .tagline span {
font-style: italic;
}
.topnav a.active {
background-color: #4CAF50;
color: #f9fafc;
}
.topnav .icon {
display: none;
}
/* Styling the sub menu */
nav {
float: right;
margin: 18px;
}
nav ul li {
float: left;
list-style: none;
}
nav ul li a {
color: #f9fafc;
background: #333333;
padding: 2px 10px 0 10px;
text-decoration: none;
height: 30px;
display: inline-block;
}
nav ul li a:hover {
background: #375A7F;
border-radius: 3px;
color: #FFF;
text-decoration: none;
}
.submenu {
position: relative;
}
.submenu ul {
position: absolute;
margin: 6px 0 0px 0px;
width: fill-available;
z-index: 999;
border: 1px solid #666;
border-radius: 3px;
padding: 0;
width: 133px;
border-top: 0px;
}
.submenu li {
display: block;
background: #375A7F;
color: white;
float: none;
text-align: center;
}
/* Styling drop down links */
.submenu li a {
background: transparent;
padding: 10px;
display: block;
display: inline-table;
width: -moz-available; /* WebKit-based browsers will ignore this. */
width: -webkit-fill-available; /* Mozilla-based browsers will ignore this. */
width: fill-available;
}
.submenu li a:hover,
.submenu li.active {
background: gray;
}
/* Showing Submenu on hover*/
.submenu:hover ul {
display: block;
}
/* Responsive */
@media screen and (max-width: 1060px) {
.topnav a:not(:first-child) {
display: none;
}
.topnav .tagline {
display: none;
}
.brand {
margin-left: 1%;
}
/* Fonts */
.btn,
.btn-group span {
font-size: 0.77em;
}
.reader-tools {
bottom: 30px;
}
}
.responsive-icons svg{
display: none;
}
@media screen and (max-width: 660px) {
.reader-mode .submenu {
display: none;
}
.reader-mode .reader-book {
display: block;
}
.paste-wrapper img {
width: 94%;
max-width: 50%;
}
.paste-options-res a {
max-width: 120px;
width: 100%;
overflow: hidden;
text-overflow: ellipsis;
}
}
@media screen and (max-width: 460px) {
/* Responsive icons */
.responsive-icons {
font-size: 0;
}
.responsive-icons svg{
display: block;
}
}
/* reader mode */
#readable-paste-content {
color: #f2f2f2;
max-width: 800px;
width: 90%;
text-align: justify;
margin: auto;
margin-top: 20px;
white-space: pre-wrap;
overflow: visible;
}
.reader-mode-bg {
background-color: #333;
}
.reader-book svg {
font-size: 26px;
}
.reader-tools {
color: #eee;
}
.reader-tools.min a{
font-size: 14px;
}
.reader-tools.max a{
font-size: 18px;
}
/* Admin */
.login-form {
width: 80%;
max-width: 500px;
margin: auto;
}
#password-field {
width: calc(100% - 65px);
}
.admin-header {
text-align: center;
font-size: 1.5em;
color: #f2f2f2;
margin: 20px 0px 20px 0px;
clear: both;
}
.admin-header svg {
font-size: 4em;
opacity: 0.3;
}
form {
clear: both;
}
.logout svg {
width: 16px;
height: 16px;
margin: 0px 10px 2px 0px;
}

File diff suppressed because one or more lines are too long

View File

@ -1,118 +0,0 @@
/*
* Derived from einaros's Sons of Obsidian theme at
* http://studiostyl.es/schemes/son-of-obsidian by
* Alex Ford of CodeTunnel:
* http://CodeTunnel.com/blog/post/71/google-code-prettify-obsidian-theme
*/
.str
{
color: #EC7600;
}
.kwd
{
color: #93C763;
}
.com
{
color: #66747B;
}
.typ
{
color: #678CB1;
}
.lit
{
color: #FACD22;
}
.pun
{
color: #F1F2F3;
}
.pln
{
color: #F1F2F3;
}
.tag
{
color: #8AC763;
}
.atn
{
color: #E0E2E4;
}
.atv
{
color: #EC7600;
}
.dec
{
color: purple;
}
pre.prettyprint
{
border: 0px solid #888;
}
ol.linenums
{
margin-top: 0;
margin-bottom: 0;
}
.prettyprint {
background: #000;
}
li.L0, li.L1, li.L2, li.L3, li.L4, li.L5, li.L6, li.L7, li.L8, li.L9
{
color: #555;
list-style-type: decimal;
}
li.L1, li.L3, li.L5, li.L7, li.L9 {
background: #111;
}
@media print
{
.str
{
color: #060;
}
.kwd
{
color: #006;
font-weight: bold;
}
.com
{
color: #600;
font-style: italic;
}
.typ
{
color: #404;
font-weight: bold;
}
.lit
{
color: #044;
}
.pun
{
color: #440;
}
.pln
{
color: #000;
}
.tag
{
color: #006;
font-weight: bold;
}
.atn
{
color: #404;
}
.atv
{
color: #060;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 261 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 580 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 522 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
zerobin/static/img/favicon.ico Normal file → Executable file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 15 KiB

File diff suppressed because one or more lines are too long

View File

@ -15,45 +15,42 @@ Vue.options.delimiters = ['{%', '%}'];
// Force focus for textarea (firefox hack)
setTimeout(function () {
document.querySelector('textarea').focus()
document.getElementById('content').focus();
}, 100)
// Parse obfuscaded emails and make them usable
const menu = new Vue({
el: "#menu-top",
methods: {
formatEmail: (email) => {
return "mailto:" + email.replace('__AT__', '@');
},
}
})
var app = new Vue({
const app = new Vue({
el: '#wrap-content',
el: '#app',
data: {
previousPastes: [],
downloadLink: {},
displayBottomToolBar: false,
openPreviousPastesMenu: false,
readerMode: false,
isUploading: false,
btcCopied: false,
currentPaste: {
ownerKey: '',
id: ''
id: '',
type: '',
content: '',
downloadLink: {},
title: '',
btcTipAddress: ''
},
newPaste: {
expiration: '1_day',
content: '',
title: '',
btcTipAddress: ''
},
messages: [],
/** Check for browser support of the named featured. Store the result
and add a class to the html tag with the result */
support: {
clipboard: (function () {
var val = !!(navigator.clipboard);
document.querySelector('html').classList.add((val ? '' : 'no-') + 'clipboard');
return val;
})(),
clipboard: !!(isSecureContext && navigator.clipboard && navigator.clipboard.writeText),
URLSearchParams: !!window.URLSearchParams,
localStorage: (function () {
var val = !!(localStorage);
@ -77,48 +74,92 @@ const app = new Vue({
isLoading: false
},
methods: {
forceLoadPaste: (link) => {
toggleReaderMode: function () {
if (!this.readerMode) {
this.messages = [];
if (this.support.URLSearchParams) {
var searchParams = new URLSearchParams(window.location.search);
searchParams.set('readerMode', 1);
window.location.search = searchParams.toString();
}
} else {
if (this.support.URLSearchParams) {
var searchParams = new URLSearchParams(window.location.search);
searchParams.delete('readerMode');
window.location.search = searchParams.toString();
}
}
this.readerMode = !this.readerMode;
},
increaseFontSize: function (amount) {
var readableModeContent = document.getElementById('readable-paste-content');
var fontSize = window.getComputedStyle(readableModeContent, null).getPropertyValue('font-size');
amount = amount || 5;
readableModeContent.style.fontSize = (parseFloat(fontSize) + amount) + "px";
},
decreaseFontSize: function () {
this.increaseFontSize(-5);
},
formatEmail: function (email) {
return "mailto:" + email.replace('__AT__', '@');
},
forceLoad: function (link) {
window.location = link;
window.location.reload();
},
handleClone: () => {
handleClone: function () {
document.querySelector('.submit-form').style.display = "inherit";
document.querySelector('.paste-form').style.display = "none";
let content = document.getElementById('content');
var title = document.querySelector('h1');
if (title) {
title.style.display = "none";
}
var content = document.getElementById('content');
content.value = zerobin.getPasteContent();
content.dispatchEvent(new Event('change'));
this.newPaste.title = this.currentPaste.title;
this.newPaste.btcTipAddress = this.currentPaste.btcTipAddress;
},
handleCancelClone: () => {
handleCancelClone: function () {
document.querySelector('.submit-form').style.display = "none";
document.querySelector('.paste-form').style.display = "inherit";
document.querySelector('h1').style.display = "inherit";
},
handleUpload: (files) => {
handleUpload: function (files) {
try {
app.isUploading = true;
zerobin.upload(files);
} catch (e) {
zerobin.message('error', 'Could no upload the file', 'Error');
zerobin.message('danger', 'Could no upload the file', 'Error');
}
app.isUploading = false;
},
handleForceColoration: (e) => {
let content = document.getElementById('paste-content');
handleForceColoration: function (e) {
var content = document.getElementById('paste-content');
content.classList.add('linenums');
e.target.innerHTML = 'Applying coloration';
prettyPrint();
e.target.parentNode.remove()
},
handleSendByEmail: (e) => {
e.target.href = 'mailto:friend@example.com?body=' + window.location.toString();
handleSendByEmail: function (e) {
window.location = 'mailto:friend@example.com?body=' + window.location.toString();
},
handleDeletePaste: () => {
handleDeletePaste: function () {
if (window.confirm("Delete this paste?")) {
app.isLoading = true;
bar.set('Deleting paste...', '50%');
@ -131,19 +172,20 @@ const app = new Vue({
}).then(function (response) {
if (response.ok) {
window.location = "/";
window.reload()
} else {
form.forEach((node) => node.disabled = false);
form.forEach(function (node) {
node.disabled = false;
});
app.isLoading = false
zerobin.message(
'error',
'danger',
'Paste could not be deleted. Please try again later.',
'Error');
}
app.isLoading = false;
}).catch(function (error) {
zerobin.message(
'error',
'danger',
'Paste could not be delete. Please try again later.',
'Error');
app.isLoading = false;
@ -151,35 +193,78 @@ const app = new Vue({
}
},
copyToClipboard: () => {
copyToClipboard: function () {
var pasteContent = zerobin.getPasteContent();
let promise;
if (pasteContent.indexOf("data:image") === 0) {
promise = fetch(pasteContent).then((res) => {
return res.blob().then(blob => {
return navigator.clipboard.write([
new ClipboardItem({
[blob.type]: blob
})
])
})
})
} else {
promise = navigator.clipboard.writeText(pasteContent);
}
var promise = navigator.clipboard.writeText(pasteContent);
promise.then(function () {
zerobin.message('info', 'The paste is now in your clipboard', '', true);
zerobin.message('primary', 'The paste is now in your clipboard', '', true);
}, function (err) {
zerobin.message('error', 'The paste could not be copied', '', true);
zerobin.message('danger', 'The paste could not be copied', '', true);
});
},
copyBTCAdressToClipboard: function () {
var promise = navigator.clipboard.writeText(this.currentPaste.btcTipAddress);
app.btcCopied = true;
promise.then(function () {
if (app.btcCopied) {
clearTimeout(app.btcCopied);
}
app.btcCopied = setTimeout(function () {
app.btcCopied = false;
}, 3000)
}, function (err) {
zerobin.message('danger', 'The BTC addresse could not be copied', '', true);
});
},
compressImage: function (base64) {
var canvas = document.createElement('canvas')
var img = document.createElement('img')
return new Promise(function (resolve, reject) {
img.onload = function () {
var width = img.width;
var height = img.height;
var biggest = width > height ? width : height;
var maxHeight = height;
var maxWidth = width;
if (width > height) {
if (width > maxWidth) {
height = Math.round((height *= maxWidth / width));
width = maxWidth;
}
} else {
if (height > maxHeight) {
width = Math.round((width *= maxHeight / height));
height = maxHeight;
}
}
canvas.width = width;
canvas.height = height;
var ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0, width, height);
resolve(canvas.toDataURL('image/jpeg', 0.7));
}
img.onerror = function (err) {
reject(err);
}
img.src = base64;
})
},
/**
On the create paste page:
On click on the send button, compress and encrypt data before
@ -187,7 +272,7 @@ const app = new Vue({
newly created paste, adding the key in the hash.
*/
encryptAndSendPaste: (e) => {
encryptAndSendPaste: function () {
var paste = document.querySelector('textarea').value;
@ -195,7 +280,9 @@ const app = new Vue({
var form = document.querySelectorAll('input, textarea, select, button');
form.forEach((node) => node.disabled = true);
form.forEach(function (node) {
node.disabled = true;
});
// set up progress bar
var bar = zerobin.progressBar('form.well .progress');
@ -210,76 +297,114 @@ const app = new Vue({
var key = zerobin.makeKey(256);
zerobin.encrypt(key, paste,
var promise = new Promise(function (resolve, reject) {
resolve(paste);
}); // noop to avoid branching
if (paste.indexOf('data:image') == 0) {
promise = app.compressImage(paste);
}
() => bar.set('Encoding to base64...', '45%'),
() => bar.set('Compressing...', '65%'),
() => bar.set('Encrypting...', '85%'),
promise.then(function (base64) {
zerobin.encrypt(key, base64,
/* This block deals with sending the data, redirection or error handling */
function (content) {
function () {
bar.set('Encoding to base64...', '45%')
},
function () {
bar.set('Compressing...', '65%')
},
function () {
bar.set('Encrypting...', '85%')
},
bar.set('Sending...', '95%');
var data = {
content: content,
expiration: app.newPaste.expiration
};
var sizebytes = zerobin.count(JSON.stringify(data));
var oversized = sizebytes > zerobin.max_size; // 100kb - the others header information
var readableFsize = Math.round(sizebytes / 1024);
var readableMaxsize = Math.round(zerobin.max_size / 1024);
/* This block deals with sending the data, redirection or error handling */
function (content) {
if (oversized) {
app.isLoading = false
form.forEach((node) => node.disabled = false);
zerobin.message('error', ('The encrypted file was <strong class="file-size">' + readableFsize +
'</strong>KB. You have reached the maximum size limit of ' + readableMaxsize + 'KB.'),
'Warning!', true);
return;
}
bar.set('Sending...', '95%');
var data = {
content: content,
expiration: app.newPaste.expiration,
title: app.newPaste.title,
btcTipAddress: app.newPaste.btcTipAddress
};
var sizebytes = zerobin.count(JSON.stringify(data));
var oversized = sizebytes > zerobin.max_size; // 100kb - the others header information
var readableFsize = Math.round(sizebytes / 1024);
var readableMaxsize = Math.round(zerobin.max_size / 1024);
fetch('/paste/create', {
method: "POST",
body: new URLSearchParams(data)
}).then(function (response) {
if (response.ok) {
bar.set('Redirecting to new paste...', '100%');
if (oversized) {
app.isLoading = false
form.forEach(function (node) {
node.disabled = false;
});
zerobin.message('danger', ('The file was <strong class="file-size">' + readableFsize +
'</strong>KB after encryption. You have reached the maximum size limit of ' + readableMaxsize + 'KB.'),
'Warning!', true);
return;
}
fetch('/paste/create', {
method: "POST",
body: new URLSearchParams(data)
}).then(function (response) {
if (response.ok) {
bar.set('Redirecting to new paste...', '100%');
response.json().then(function (data) {
if (data.status === 'error') {
zerobin.message('danger', data.message, 'Error');
form.forEach(function (node) {
node.disabled = false;
});
app.isLoading = false;
} else {
if (app.support.localStorage) {
zerobin.storePaste('/paste/' + data.paste + "?owner_key=" + data.owner_key + '#' + key);
}
window.location = ('/paste/' + data.paste + '#' + key);
}
})
response.json().then((data) => {
if (data.status === 'error') {
zerobin.message('error', data.message, 'Error');
form.forEach((node) => node.disabled = false);
app.isLoading = false
} else {
if (app.support.localStorage) {
zerobin.storePaste('/paste/' + data.paste + "?owner_key=" + data.owner_key + '#' + key);
}
window.location = ('/paste/' + data.paste + '#' + key);
form.forEach(function (node) {
node.disabled = false
});
app.isLoading = false;
zerobin.message(
'danger',
'Paste could not be saved. Please try again later.',
'Error');
}
})
}).catch(function (error) {
form.forEach(function (node) {
node.disabled = false;
});
app.isLoading = false;
zerobin.message(
'danger',
'Paste could not be saved. Please try again later.',
'Error');
});
} else {
form.forEach((node) => node.disabled = false);
app.isLoading = false
zerobin.message(
'error',
'Paste could not be saved. Please try again later.',
'Error');
}
}).catch(function (error) {
form.forEach((node) => node.disabled = false);
app.isLoading = false
zerobin.message(
'error',
'Paste could not be saved. Please try again later.',
'Error');
})
}),
function (err) {
debugger;
form.forEach(function (node) {
node.disabled = false;
});
app.isLoading = false;
zerobin.message('danger', 'Paste could not be encrypted. Aborting.',
'Error');
};
});
} catch (err) {
form.forEach((node) => node.disabled = false);
app.isLoading = false
zerobin.message('error', 'Paste could not be encrypted. Aborting.',
form.forEach(function (node) {
node.disabled = false;
});
app.isLoading = false;
zerobin.message('danger', 'Paste could not be encrypted. Aborting.',
'Error');
}
}
@ -326,11 +451,13 @@ window.zerobin = {
content = sjcl.encrypt(key, content);
} catch (e) {
document.querySelectorAll('input, textarea, select, button').forEach((node) => node.disabled = true);
document.querySelectorAll('input, textarea, select, button').forEach(function (node) {
node.disabled = true
});
app.isLoading = false;
zerobin.message('error', 'Paste could not be encrypted. Aborting.',
zerobin.message('danger', 'Paste could not be encrypted. Aborting.',
'Error');
}
if (doneCallback) {
@ -367,7 +494,7 @@ window.zerobin = {
/* Decompress */
setTimeout(function () {
try {
content = lzw.decompress(content);
if (fromBase64Callback) {
fromBase64Callback();
}
@ -388,6 +515,7 @@ window.zerobin = {
doneCallback(content);
}
} catch (err) {
debugger;
errorCallback(err);
}
@ -497,7 +625,7 @@ window.zerobin = {
displayDate = pasteDateTime.split(' ')[1];
prefix = 'at ';
}
let link = localStorage.getItem(key);
var link = localStorage.getItem(key);
return {
displayDate: displayDate,
@ -548,10 +676,10 @@ window.zerobin = {
/** Return the paste content stripted from any code coloration */
getPasteContent: function () {
var copy = '';
document.querySelectorAll("#paste-content li").forEach((node) => {
document.querySelectorAll("#paste-content li").forEach(function (node) {
copy = copy + node.textContent.replace(/[\u00a0]+/g, ' ') + '\n';
})
});
if (copy === '') {
copy = document.querySelector("#paste-content").textContent;
}
@ -576,7 +704,7 @@ window.zerobin = {
message: function (type, message, title, flush, callback, action) {
window.scrollTo(0, 0);
if (flush) {
app.messages = app.messages.filter((msg) => {
app.messages = app.messages.filter(function (msg) {
msg.type !== type
});
}
@ -586,7 +714,7 @@ window.zerobin = {
type: type,
action: action || {},
});
callback && callback()
callback && callback();
},
/** Return a progress bar object */
@ -632,19 +760,28 @@ window.zerobin = {
return total * 1000 / size;
},
// prevent defaults
ignoreDrag: function (e) {
e.stopPropagation();
e.preventDefault();
},
// Handle Drop
handleDrop: function (e) {
e.preventDefault();
zerobin.upload(e.dataTransfer.files);
document.getElementById("content").classList.remove("hover");
},
handlePaste: function (e) {
var items = (event.clipboardData || event.originalEvent.clipboardData).items;
for (var i = 0; i < items.length; i++) {
if (items[i].type.indexOf("image") === 0) {
e.preventDefault()
return zerobin.upload([items[i].getAsFile()]);
}
}
},
handleDragOver: function (e) {
zerobin.ignoreDrag(e);
document.getElementById("content").classList.add('hover');
@ -655,59 +792,36 @@ window.zerobin = {
},
upload: function (files) {
let content = document.getElementById('content');
var current_file = files[0];
var content = document.getElementById('content');
var currentFile = files[0];
var reader = new FileReader();
if (current_file.type.indexOf('image') == 0) {
if (currentFile.type.indexOf('image') == 0) {
reader.onload = function (event) {
var image = new Image();
image.src = event.target.result;
content.value = event.target.result
image.onload = function () {
var maxWidth = 1024,
maxHeight = 1024,
imageWidth = image.width,
imageHeight = image.height;
if (imageWidth > imageHeight) {
if (imageWidth > maxWidth) {
imageHeight *= maxWidth / imageWidth;
imageWidth = maxWidth;
}
} else {
if (imageHeight > maxHeight) {
imageWidth *= maxHeight / imageHeight;
imageHeight = maxHeight;
}
app.messages = []
var previousImage = document.querySelector('.paste-wrapper');
if (previousImage) {
previousImage.remove();
}
var canvas = document.createElement('canvas');
canvas.width = imageWidth;
canvas.height = imageHeight;
image.width = imageWidth;
image.height = imageHeight;
var ctx = canvas.getContext("2d");
ctx.drawImage(this, 0, 0, imageWidth, imageHeight);
var paste = canvas.toDataURL(current_file.type);
content.value = paste;
content.dispatchEvent(new Event('change'));
image.style.maxWidth = '742px';
var imgWrapper = document.createElement('div');
imgWrapper.classList.add('paste-wrapper');
imgWrapper.appendChild(image)
content.style.display = "none";
content.after(image);
content.after(imgWrapper);
}
}
reader.readAsDataURL(current_file);
reader.readAsDataURL(currentFile);
} else {
reader.onload = function (event) {
content.value = event.target.result
content.dispatchEvent(new Event('change'));
};
reader.readAsText(current_file);
reader.readAsText(currentFile);
}
}
};
@ -719,8 +833,8 @@ window.zerobin = {
Also calculate and set the paste visual hash.
*/
let pasteContent = document.querySelector('#paste-content');
let content = '';
var pasteContent = document.querySelector('#paste-content');
var content = '';
if (pasteContent) {
content = pasteContent.textContent.trim();
@ -733,7 +847,9 @@ var error = false;
if (content && key) {
var form = document.querySelectorAll('input, textarea, select, button');
form.forEach((node) => node.disabled = true);
form.forEach(function (node) {
node.disabled = true;
});
var bar = zerobin.progressBar('.well form .progress');
app.isLoading = true;
@ -744,58 +860,84 @@ if (content && key) {
/* On error*/
function () {
app.isLoading = false;
zerobin.message('error', 'Could not decrypt data (Wrong key ?)', 'Error');
zerobin.message('danger', 'Could not decrypt data (Wrong key ?)', 'Error');
},
/* Update progress bar */
() => bar.set('Decompressing...', '45%'),
() => bar.set('Base64 decoding...', '65%'),
() => bar.set('From bits to string...', '85%'),
function () {
bar.set('Decompressing...', '45%');
},
function () {
bar.set('Base64 decoding...', '65%');
},
function () {
bar.set('From bits to string...', '85%');
},
/* When done */
function (content) {
/* Decrypted content goes back to initial container*/
document.querySelector('#paste-content').innerHTML = content;
var readerMode = false;
if (content.indexOf('data:image') == 0) {
// Display Image
let pasteContent = document.querySelector('#paste-content');
app.currentPaste.type = "image";
var pasteContent = document.querySelector('#paste-content');
pasteContent.style.display = "none";
var img = document.createElement('img')
var imgWrapper = document.createElement('div');
imgWrapper.classList.add('paste-wrapper');
var img = document.createElement('img');
img.src = content;
img.style.maxWidth = '742px';
pasteContent.after(img);
pasteContent.after(imgWrapper);
imgWrapper.appendChild(img);
// Display Download button
document.querySelectorAll('.btn-clone').forEach((node) => node.style.display = "none")
document.querySelectorAll('.btn-clone').forEach(function (node) {
node.style.display = "none";
});
app.downloadLink = {
name: '0bin_' + document.location.pathname.split('/').pop(),
var extension = /data:image\/([^;]+);base64/.exec(content)[1];
app.currentPaste.downloadLink = {
name: '0bin_' + document.location.pathname.split('/').pop() + '.' + extension,
url: content
}
} else {
app.currentPaste.type = "text";
/* Decrypted content goes back to initial container*/
document.querySelector('#paste-content').innerText = content;
app.currentPaste.content = content;
app.currentPaste.downloadLink = {
name: '0bin_' + document.location.pathname.split('/').pop() + ".txt",
url: "data:text/html;charset=UTF-8," + content
}
if (app.support.URLSearchParams) {
readerMode = (new URLSearchParams(window.location.search)).get('readerMode');
}
}
bar.set('Code coloration...', '95%');
/* Add a continuation to let the UI redraw */
/* Add a continuation to var the UI redraw */
setTimeout(function () {
/** Syntaxic coloration */
if (zerobin.isCode(content) > 100) {
if (zerobin.isCode(content) > 100 && !readerMode) {
document.getElementById('paste-content').classList.add('linenums');
prettyPrint();
} else {
if (content.indexOf('data:image') != 0) {
zerobin.message('info',
zerobin.message('primary',
"The paste did not seem to be code, so it " +
"was not colorized. ",
'', false, undefined, {
message: "Force coloration",
message: "Click here to force coloration",
callback: app.handleForceColoration
});
}
@ -808,8 +950,13 @@ if (content && key) {
bar.set('Done', '100%');
app.isLoading = false;
form.forEach((node) => node.disabled = false);
form.forEach(function (node) {
node.disabled = false;
});
content = '';
if (readerMode) {
app.toggleReaderMode();
}
}, 100);
@ -817,16 +964,40 @@ if (content && key) {
} /* End of "DECRYPTION" */
/* Display bottom paste option buttons when needed */
window.onload = function () {
["keyup", "change"].forEach((event) => {
let content = document.getElementById("content");
content.addEventListener(event, () => {
let height = parseFloat(getComputedStyle(content, null).height.replace("px", ""))
/* Display bottom paste option buttons when needed */
["keyup", "change"].forEach(function (event) {
var content = document.getElementById("content");
content.addEventListener(event, function () {
var height = parseFloat(getComputedStyle(content, null).height.replace("px", ""))
app.displayBottomToolBar = height > 400;
})
})
/* Remove expired pasted from history **/
if (app.support.history && app.currentPaste.type === 'not_found') {
var paste_id = zerobin.getPasteId();
var keys = zerobin.getLocalStorageURLKeys();
keys.forEach(function (key, i) {
if (localStorage[key].indexOf(paste_id) !== -1) {
localStorage.removeItem(key);
return false;
}
})
}
var title = document.querySelector('h1');
if (title) {
app.currentPaste.title = title.innerText;
}
var btcTipAddress = document.querySelector('.btc-tip-address');
if (btcTipAddress) {
app.currentPaste.btcTipAddress = btcTipAddress.innerText;
}
}
/* Display previous pastes */
@ -839,21 +1010,22 @@ if (app.support.localStorage) {
if (app.support.fileUpload) {
// Implements drag & drop upload
let content = document.getElementById('content');
var content = document.getElementById('content');
content.addEventListener('drop', zerobin.handleDrop);
content.addEventListener('paste', zerobin.handlePaste);
content.addEventListener('dragover', zerobin.handleDragOver);
content.addEventListener('dragleave', zerobin.handleDragLeave);
}
/* Remove expired pasted from history */
if (app.support.history && zerobin.paste_not_found) {
var paste_id = zerobin.getPasteId();
var keys = zerobin.getLocalStorageURLKeys();
keys.forEach((key, i) => {
if (localStorage[key].indexOf(paste_id) !== -1) {
localStorage.removeItem(key);
return false;
}
})
/* Autofit text area height */
var tx = document.getElementsByTagName('textarea');
for (var i = 0; i < tx.length; i++) {
tx[i].setAttribute('style', 'height:' + (tx[i].scrollHeight) + 'px;overflow-y:hidden;');
tx[i].addEventListener("input", OnInput, false);
}
function OnInput() {
this.style.height = 'auto';
this.style.height = (this.scrollHeight) + 'px';
}

View File

@ -1,110 +0,0 @@
// Author: Anthony McKale
//
// Note: modifed to javascript from original as2 found below
// basically identical actual to as2
//
// http://www.razorberry.com/blog/archives/2004/08/22/lzw-compression-methods-in-as2/
//
// A class for LZW compression modified from code posted at the following URL's
// http://www.shoe-box.org/blog/index.php/2004/05/05/13-CompressionLzw
// http://www.lalex.com/blog/comments/200405/164-compression-lzw-actionscript-2.html
//
var lzw = {
// Change this variable to output an xml safe string
xmlsafe : false,
compress : function(str){
var dico = new Array();
var skipnum = lzw.xmlsafe?5:0;
for (var i = 0; i < 256; i++)
{
dico[String.fromCharCode(i)] = i;
}
if (lzw.xmlsafe)
{
dico["<"] = 256;
dico[">"] = 257;
dico["&"] = 258;
dico["\""] = 259;
dico["'"] = 260;
}
var res = "";
var txt2encode = str;
var splitStr = txt2encode.split("");
var len = splitStr.length;
var nbChar = 256+skipnum;
var buffer = "";
for (var i = 0; i <= len; i++)
{
var current = splitStr[i];
if (dico[buffer + current] !== undefined)
{
buffer += current;
}
else
{
res += String.fromCharCode(dico[buffer]);
dico[buffer + current] = nbChar;
nbChar++;
buffer = current;
}
}
return res;
},
decompress : function (str)
{
var dico = new Array();
var skipnum = lzw.xmlsafe?5:0;
for (var i = 0; i < 256; i++)
{
var c = String.fromCharCode(i);
dico[i] = c;
}
if (lzw.xmlsafe)
{
dico[256] = "<";
dico[257] = ">";
dico[258] = "&";
dico[259] = "\"";
dico[260] = "'";
}
var txt2encode = str;
var splitStr = txt2encode.split("");
var length = splitStr.length;
var nbChar = 256+skipnum;
var buffer = "";
var chaine = "";
var result = "";
for (var i = 0; i < length; i++)
{
var code = txt2encode.charCodeAt(i);
var current = dico[code];
if (buffer == "")
{
buffer = current;
result += current;
}
else
{
if (code <= 255+skipnum)
{
result += current;
chaine = buffer + current;
dico[nbChar] = chaine;
nbChar++;
buffer = current;
}
else
{
chaine = dico[code];
if (chaine == undefined) chaine = buffer + buffer.slice(0,1);
result += chaine;
dico[nbChar] = buffer + chaine.slice(0, 1);
nbChar++;
buffer = chaine;
}
}
}
return result;
}
};

File diff suppressed because one or more lines are too long

View File

@ -1,47 +1,19 @@
import time
import os
import glob
import tempfile
import codecs
import unicodedata
import hashlib
import secrets
from functools import partial
from pathlib import Path
import bottle
from appdirs import AppDirs
import zerobin
from zerobin import default_settings
try:
from zerobin.privilege import drop_privileges_permanently, coerce_user, coerce_group
except (AttributeError):
pass # privilege does't work on several plateforms
from runpy import run_path
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.")
class SettingsValidationError(Exception):
pass
@ -60,12 +32,12 @@ class SettingsContainer(object):
cls._instance.update_with_module(default_settings)
return cls._instance
def update_with_dict(self, dict):
def update_with_dict(self, mapping):
"""
Update settings with values from the given mapping object.
(Taking only variable with uppercased name)
"""
for name, value in dict.items():
for name, value in mapping.items():
if name.isupper():
setattr(self, name, value)
return self
@ -83,44 +55,23 @@ class SettingsContainer(object):
Create an instance of SettingsContainer with values based
on the one in the passed module.
"""
settings = cls()
settings.update_with_module(module)
return settings
params = cls()
params.update_with_module(module)
return params
def update_with_file(self, filepath):
"""
Update settings with values from the given module file.
Uses update_with_dict() behind the scenes.
"""
settings = run_path(filepath)
return self.update_with_dict(settings)
params = run_path(filepath)
return self.update_with_dict(params)
settings = SettingsContainer()
def to_ascii(utext):
""" Take a unicode string and return ascii bytes.
Try to replace non ASCII char by similar ASCII char. If it can't,
replace it with "?".
"""
return unicodedata.normalize("NFKD", utext).encode("ascii", "replace")
# Make sure to always specify encoding when using open in Python 2 or 3
safe_open = partial(codecs.open, encoding="utf8")
def as_unicode(obj):
""" Return the unicode representation of an object """
try:
return unicode(obj)
except NameError:
return str(obj)
def ensure_var_env():
def ensure_app_context(data_dir=None, config_dir=None):
""" Ensure all the variable things we generate are available.
This will make sure we have:
@ -129,20 +80,41 @@ def ensure_var_env():
- a content dir
- a secret key
- an admin URL
This function is idempotent if nothing touch the files it created.
"""
settings.VAR_DIR.mkdir(exist_ok=True, parents=True)
settings.PASTE_FILES_ROOT = VAR_DIR / "content"
app_dirs = AppDirs("0bin", "tygs")
settings.DATA_DIR = Path(data_dir or app_dirs.user_data_dir).expanduser()
settings.DATA_DIR.mkdir(exist_ok=True, parents=True)
settings.CONFIG_DIR = Path(config_dir or app_dirs.user_config_dir).expanduser()
settings.CONFIG_DIR.mkdir(exist_ok=True, parents=True)
settings.STATIC_FILES_ROOT = zerobin.ROOT_DIR / "static"
settings.PASTE_FILES_ROOT = settings.DATA_DIR / "pastes"
settings.PASTE_FILES_ROOT.mkdir(exist_ok=True)
settings.SESSIONS_DIR = VAR_DIR / "sessions"
settings.SESSIONS_DIR = settings.DATA_DIR / "sessions"
settings.SESSIONS_DIR.mkdir(exist_ok=True)
secret_key_file = settings.VAR_DIR / "secret_key"
bottle.TEMPLATE_PATH.insert(0, zerobin.ROOT_DIR / "views")
CUSTOM_VIEWS_DIR = settings.CONFIG_DIR / "custom_views"
CUSTOM_VIEWS_DIR.mkdir(exist_ok=True)
bottle.TEMPLATE_PATH.insert(0, CUSTOM_VIEWS_DIR)
bottle.BaseRequest.MEMFILE_MAX = settings.MAX_SIZE + (1024 * 100)
secret_key_file = settings.CONFIG_DIR / "secret_key"
if not secret_key_file.is_file():
secret_key_file.write_text(secrets.token_urlsafe(64))
settings.SECRET_KEY = secret_key_file.read_text()
admin_password_file = settings.VAR_DIR / "admin_password"
admin_password_file = settings.CONFIG_DIR / "admin_password"
if not secret_key_file.is_file():
admin_password_file.write_text(
"No password set. Use the set_admin_passord command. Don't write this file by hand."
@ -150,7 +122,14 @@ def ensure_var_env():
settings.ADMIN_PASSWORD_FILE = admin_password_file
payload = ("admin" + settings.SECRET_KEY).encode("ascii")
settings.ADMIN_URL = "/admin/" + hashlib.sha256(payload).hexdigest()
settings.ADMIN_URL = "/admin/" + hashlib.sha256(payload).hexdigest() + "/"
settings_file = settings.CONFIG_DIR / "settings.py"
if not settings_file.is_file():
default_config = (zerobin.ROOT_DIR / "default_settings.py").read_text()
settings_file.write_text(default_config)
settings.update_with_file(settings_file)
def hash_password(password):
@ -166,7 +145,7 @@ def hash_password(password):
def check_password(password):
try:
return settings.ADMIN_PASSWORD_FILE.read_bytes() != hash_password(password)
return settings.ADMIN_PASSWORD_FILE.read_bytes() == hash_password(password)
except (FileNotFoundError, AttributeError):
return False

View File

@ -1,8 +1,4 @@
<script type="text/javascript">
zerobin.paste_not_found = true;
</script>
<p class="alert alert-error">
<p class="alert alert-warning alert-dismissible" :dummy="currentPaste.type = 'not_found'">
<a class="close" data-dismiss="alert" href="#" @click.prevent="$event.target.parentNode.remove()">×</a>
<strong class="title">404 Error!</strong>
<span class="message">
@ -10,41 +6,58 @@
</span>
</p>
<p class="file-upload">
<input type="button" class="btn btn-upload" :value="isUploading ? 'Uploading...': 'Upload file'"
:disabled="isUploading">
<input type="file" class="hide-upload" id="file-upload" @change="handleUpload($event.target.files)">
</p>
<form class="well" method="post" action="/paste/create" @submit.prevent="encryptAndSendPaste()">
<p class=" paste-option">
<label for="expiration">Expiration:</label>
<select id="expiration" name="expiration" v-model="newPaste.expiration">
<option value="burn_after_reading">Burn after reading</option>
<option selected value="1_day">1 day</option>
<option value="1_month">1 month</option>
<option value="never">Never</option>
</select>
<button type="submit" class="btn btn-primary" ">Submit</button>
</p>
<p>
<div class=" progress progress-striped active" v-show="isLoading">
<div class="bar"></div>
<div class="d-flex justify-content-between">
<div>
<div class="file-upload" v-if="support.fileUpload">
<input type="button" class="btn btn-primary" :value="isUploading ? 'Uploading...': 'Upload file'"
:disabled="isUploading">
<input type="file" class="hide-upload" id="file-upload" @change="handleUpload($event.target.files)">
</div>
<textarea rows="10" style="width:100%;" class="input-xlarge" id="content" name="content" autofocus
v-on:keydown.prevent.ctrl.enter="encryptAndSendPaste()"></textarea>
</p>
<p class="paste-option down" v-if="displayBottomToolBar">
<label for="expiration">Expiration:</label>
<select id="expiration" name="expiration" v-model="newPaste.expiration">
<option value="burn_after_reading">Burn after reading</option>
<option selected value="1_day">1 day</option>
<option value="1_month">1 month</option>
<option value="never">Never</option>
</select>
<button type="submit" class="btn btn-primary">Submit</button>
</p>
</div>
<div class="form-group select-date paste-option">
<div class="input-group">
<select id="expiration" name="expiration" class="custom-select" v-model="newPaste.expiration">
<option value="burn_after_reading">Burn after reading</option>
<option selected value="1_day">Expire in 1 day</option>
<option value="1_month">Expire in 1 month</option>
<option value="never">Never expire</option>
</select>
<div class="input-group-append">
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</div>
</div>
</div>
<div class="pre-wrapper">
<div class="progress" v-show="isLoading">
<div class="progress-bar progress-bar-striped" role="progressbar"></div>
</div>
<textarea rows="10" style="width:100%;" class="form-control" id="content" name="content" autofocus
@keydown.ctrl.enter="encryptAndSendPaste()"></textarea>
</div>
<div class="form-group select-date paste-option down" v-if="displayBottomToolBar">
<div class="input-group">
<select id="expiration" name="expiration" class="custom-select" v-model="newPaste.expiration">
<option value="burn_after_reading">Burn after reading</option>
<option selected value="1_day">Expire in 1 day</option>
<option value="1_month">Expire in 1 month</option>
<option value="never">Never expire</option>
</select>
<div class="input-group-append">
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</div>
</div>
</form>
% rebase('base', settings=settings, pastes_count=pastes_count)

View File

@ -1,32 +1,45 @@
%if is_authenticated:
<form action="" method="delete">
<form action="./logout/" method="post">
<div class="float-right logout">
<button type="submit" class="btn btn-secondary">
<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-power" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" d="M5.578 4.437a5 5 0 1 0 4.922.044l.5-.866a6 6 0 1 1-5.908-.053l.486.875z"/>
<path fill-rule="evenodd" d="M7.5 8V1h1v7h-1z"/>
</svg>
Logout</button>
</div>
</form>
<div>
<form>
<div class="form-group">
<label>Paste to delete</label>
<input type="text" class="form-control" placeholder="Paste URL or ID">
</div>
<button type="submit" class="btn btn-black">Delete</button>
</form>
<div class="admin-header">
<h3>Admin Panel</h3>
<br>
<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-hammer" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<path d="M9.812 1.952a.5.5 0 0 1-.312.89c-1.671 0-2.852.596-3.616 1.185L4.857 5.073V6.21a.5.5 0 0 1-.146.354L3.425 7.853a.5.5 0 0 1-.708 0L.146 5.274a.5.5 0 0 1 0-.706l1.286-1.29a.5.5 0 0 1 .354-.146H2.84C4.505 1.228 6.216.862 7.557 1.04a5.009 5.009 0 0 1 2.077.782l.178.129z"/>
<path fill-rule="evenodd" d="M6.012 3.5a.5.5 0 0 1 .359.165l9.146 8.646A.5.5 0 0 1 15.5 13L14 14.5a.5.5 0 0 1-.756-.056L4.598 5.297a.5.5 0 0 1 .048-.65l1-1a.5.5 0 0 1 .366-.147z"/>
</svg>
</div>
%if status == "error":
<div class="alert alert-danger" role="alert alert-danger">
{{message}}
</div>
%end
%if status == "ok" and message:
<div class="alert alert-success" role="alert">
{{message}}
</div>
%end
<form action="." method="post">
<label>Paste to delete:</label>
<div class="input-group">
<input name="paste" type="text" class="form-control" placeholder="Paste URL or ID here">
<div class="input-group-append">
<button type="submit" class="btn btn-danger">Delete</button>
</div>
</div>
</form>
%else:
<form action="/login" method="post">
<div class="login-form">
<form>
<label>Password</label>
<input type="password" class="form-control" placeholder="Password">
<button type="submit" class="btn btn-black">Login</button>
</form>
</div>
</form>
%end
% rebase('base', settings=settings, pastes_count=pastes_count)
% rebase('base', settings=settings, pastes_count=pastes_count)

View File

@ -4,176 +4,144 @@
<head>
<meta charset="utf-8">
<title>0bin - encrypted pastebin</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1">
<meta name="description" content="0bin is a client-side-encrypted
pastebin featuring burn after reading, history, and
a clipboard">
<link rel="shortcut icon" href="/favicon.ico">
<link rel="icon" href="/static/img/favicon.ico" />
<link rel="apple-touch-icon" href="/static/img/apple-touch-icon.png" />
%if settings.COMPRESSED_STATIC_FILES:
%if not settings.DEBUG:
<link href="/static/css/style.min.css?{{ VERSION }}" rel="stylesheet" />
%else:
<link href="/static/css/prettify.css" rel="stylesheet" />
<link href="/static/css/bootstrap.css" rel="stylesheet">
<link href="/static/css/desert.css" rel="stylesheet" />
<link href="/static/css/bootswatch.4.5.css" rel="stylesheet">
<link href="/static/css/style.css?{{ VERSION }}" rel="stylesheet">
%end
<!-- Le HTML5 shim, for IE7-8 support of HTML5 elements -->
<!--[if lt IE 9]>
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
</head>
<body>
<div class="navbar navbar-fixed-top" id="menu-top">
<div class="navbar-inner">
<div class="container">
<a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</a>
<a class="brand" href="/"><span>ø</span>bin<em>.net</em></a>
<div class="nav-collapse">
<ul class="nav">
<div id="app" :class="{ 'reader-mode-bg': readerMode}">
%for i, entry in enumerate(settings.MENU):
<li>
%if "mailto:" in entry[1]:
<a :href="formatEmail('{{ entry[1].replace('mailto:', '').replace('@', '__AT__') }}')" class="email-link">
{{ entry[0] }}
</a>
%else:
<a href="{{ entry[1] }}">{{ entry[0] }}</a>
%end
<div :class="{'topnav': true, 'reader-mode': readerMode}" @mouseleave="openPreviousPastesMenu =false">
<a class="brand" href="/"><span>ø</span>bin<em>.net</em></a>
<span class="tagline">"A client side encrypted PasteBin"<br><span>All pastes are AES256 encrypted, we cannot know
what you paste...</span>
</span>
</li>
%end
<nav>
<ul>
<li class="reader-tools min" v-if="readerMode">
<a href="#" @click.prevent="decreaseFontSize()">
<svg height="30" width="30">
<text x="10" y="20" fill="#eee">A</text>
</svg>
</a>
</li>
<li class="reader-tools max" v-if="readerMode">
<a href="#" @click.prevent="increaseFontSize()">
<svg height="30" width="30">
<text x="10" y="20" fill="#eee">A</text>
</svg>
</a>
</li>
<li>
<a class="reader-book" href="#" v-if="currentPaste.type === 'text'" @click.prevent="toggleReaderMode()">
<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-book" fill="currentColor"
xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd"
d="M3.214 1.072C4.813.752 6.916.71 8.354 2.146A.5.5 0 0 1 8.5 2.5v11a.5.5 0 0 1-.854.354c-.843-.844-2.115-1.059-3.47-.92-1.344.14-2.66.617-3.452 1.013A.5.5 0 0 1 0 13.5v-11a.5.5 0 0 1 .276-.447L.5 2.5l-.224-.447.002-.001.004-.002.013-.006a5.017 5.017 0 0 1 .22-.103 12.958 12.958 0 0 1 2.7-.869zM1 2.82v9.908c.846-.343 1.944-.672 3.074-.788 1.143-.118 2.387-.023 3.426.56V2.718c-1.063-.929-2.631-.956-4.09-.664A11.958 11.958 0 0 0 1 2.82z" />
<path fill-rule="evenodd"
d="M12.786 1.072C11.188.752 9.084.71 7.646 2.146A.5.5 0 0 0 7.5 2.5v11a.5.5 0 0 0 .854.354c.843-.844 2.115-1.059 3.47-.92 1.344.14 2.66.617 3.452 1.013A.5.5 0 0 0 16 13.5v-11a.5.5 0 0 0-.276-.447L15.5 2.5l.224-.447-.002-.001-.004-.002-.013-.006-.047-.023a12.582 12.582 0 0 0-.799-.34 12.96 12.96 0 0 0-2.073-.609zM15 2.82v9.908c-.846-.343-1.944-.672-3.074-.788-1.143-.118-2.387-.023-3.426.56V2.718c1.063-.929 2.631-.956 4.09-.664A11.956 11.956 0 0 1 15 2.82z" />
</svg>
</a>
</li>
<li class="submenu"><a href="#" @click.prevent="openPreviousPastesMenu = !openPreviousPastesMenu">Previous
pastes <span class="caret"></span></a>
<ul class="previous-pastes" id="topmenu" v-if="openPreviousPastesMenu">
<li class="item" v-if="previousPastes.length === 0">
<a href="#">No paste yet</a>
</li>
<li :class="{item: true, active: paste.isCurrent}" v-for="paste in previousPastes">
<a :href="paste.link" @click="forceLoad(paste.link)">{% paste.displayDate %}</a>
</li>
</ul>
</li>
</ul>
</nav>
</ul>
<p class="about pull-right">
"A client side encrypted PasteBin"<br>
<span>All pastes are AES256 encrypted, we cannot know what you paste...</span>
</p>
</div>
<!--/.nav-collapse -->
</div>
</div>
</div>
<noscript class="noscript">
<div class="container jumbotron">
<h1 class="display-4">This site requires Javascript</h1>
<p class="lead">This pastebin uses client-side encryption, and therefore, it needs JavaScript to work.</p>
<p>It seems like your browser doesn't have JavaScript enabled.</p>
<noscript class="container noscript">
<p>This pastebin uses client-side encryption. Therefore, it needs JavaScript enabled.</p>
<p>It seems like your browser doesn't have JavaScript enable.</p>
<p>Please enable JavaScript for this website or use a JavaScript-capable web browser.</p>
</div>
</noscript>
</noscript>
<div class="container-md" id="wrap-content">
<div class="container app" id="wrap-content" v-cloak>
<div class="row">
<div class="span2">
<div class="well sidebar-nav">
<ul class="nav nav-list previous-pastes">
<li class="nav-header">Previous pastes</li>
<li class="item local-storage" v-if="previousPastes.length === 0">
<em class="grey">
Your previous pastes will be saved in your browser using
<a href="http://www.w3.org/TR/webstorage/">localStorage</a>.
</em>
</li>
<li class="item no-local-storage">
<em class="grey">
Sorry your browser does not support
<a href="http://www.w3.org/TR/webstorage/">LocalStorage</a>,
We cannot display your previous pastes.
</em>
</li>
<li :class="{item: true, active: paste.isCurrent}" v-for="paste in previousPastes">
<a :href="paste.link" @click="forceLoadPaste(paste.link)">
{% paste.prefix %}{% paste.displayDate %}
</a>
</li>
</ul>
</div>
<!--/.well -->
</div>
<!--/span-->
<div id='main' class="span10">
<p :class="'alert alert-' + msg.type" v-for="msg in messages">
<a class="close" data-dismiss="alert" href="#" @click.prevent="$event.target.parentNode.remove()">×</a>
<strong class="title" v-if="msg.title" v-html="msg.title"></strong>
<span class="message" v-html="msg.content"></span>
<a v-if="msg.action.message" href="#"
@click.once.prevent="msg.action.callback($event)">{% msg.action.message %}</a>
</p>
{{!base}}
</div>
<!--/span-->
</div>
<!--/row-->
<hr>
<footer>
<blockquote>
<p>“Few persons can be made to believe that it is not quite an easy thing to invent a method of secret writing
which shall baffle investigation. Yet it may be roundly asserted that human ingenuity cannot concoct a cipher
which human ingenuity cannot resolve...”</p>
<small>Edgar Allan Poe</small>
</blockquote>
%if settings.DISPLAY_COUNTER:
<h4 id="pixels-total">
<p>ø</p>
<strong>{{ pastes_count }}</strong> <br />pastes øbinned
</h4>
%if defined('paste') and paste.title:
<h1 :class="{ 'reader-mode-title': readerMode}">{{ paste.title }}</h1>
%end
<br>
<p class="greetings span12">
Based on an original idea from
<a href="http://sebsauvage.net/paste/">sebsauvage.net</a><br>
<a href="http://sametmax.com">Sam &amp; Max</a>
<p :class="'alert alert-' + msg.type" v-for="msg in messages">
<a class="close" data-dismiss="alert" href="#" @click.prevent="$event.target.parentNode.remove()">×</a>
<strong class="title" v-if="msg.title" v-html="msg.title"></strong>
<span class="message" v-html="msg.content"></span>
<a v-if="msg.action.message" href="#"
@click.once.prevent="msg.action.callback($event)">{% msg.action.message %}</a>
</p>
<div id='main'>{{!base}}</div>
</div>
<footer class="footer">
<ul>
%for i, entry in enumerate(settings.MENU):
<li>
%if "mailto:" in entry[1]:
<span :title='formatEmail(`{{ entry[1].replace("mailto:", "").replace("@", "__AT__") }}`)' class="email-link">
{{ entry[0] }}
</span>
%else:
<a href="{{ entry[1] }}">{{ entry[0] }}</a>
%end
</li>
%end
</ul>
%if settings.DISPLAY_COUNTER:
<strong>{{ pastes_count }}</strong> pastes øbinned
%end
</footer>
</div>
<!--/wrap-content-->
<script src="/static/js/vue.js"></script>
%if settings.COMPRESSED_STATIC_FILES:
%if not settings.DEBUG:
<script src="/static/js/main.min.js?{{ VERSION }}"></script>
%else:
<script src="/static/js/vue.js"></script>
<script src="/static/js/sjcl.js"></script>
<script src="/static/js/behavior.js?{{ VERSION }}"></script>
<script src="/static/js/prettify.min.js"></script>
%end
<script type="text/javascript">
zerobin.max_size = {{ settings.MAX_SIZE }};
</script>
%if settings.COMPRESSED_STATIC_FILES:
<script src="/static/js/additional.min.js?{{ VERSION }}"></script>
%else:
<p id="alert-template" class="alert-primary">
<a class="close" data-dismiss="alert" href="#">×</a>
<strong class="title"></strong>
<span class="message"></span>
</p>
<script src="/static/js/lzw.js"></script>
<script src="/static/js/prettify.min.js"></script>
%end
</body>

View File

@ -0,0 +1,85 @@
<div class="well" id="buy_bitcoin">
<h1>How To Buy Bitcoin?</h1>
<p></p>
<h4>What is Bitcoin?</h4>
<p>Bitcoin was created by Satoshi Nakamoto, a pseudonymous person or team who outlined the technology in a 2008 white paper. Its an appealingly simple concept: <strong>bitcoin is digital money</strong> that allows for secure peer-to-peer transactions on the internet.
</p>
<p>Unlike services like Venmo and PayPal, which rely on the traditional financial system for permission to transfer money and on existing debit/credit accounts, bitcoin is decentralized: any two people, anywhere in the world, can send bitcoin to each other without the involvement of a bank, government, or other institution.
<p>
Every transaction involving Bitcoin is tracked on the blockchain, which is similar to a banks ledger, or log of customers funds going in and out of the bank. In simple terms, its a record of every transaction ever made using bitcoin.
</p>
<p>
There will only ever be 21 million bitcoin. This is digital money that cannot be inflated or manipulated in any way.
</p>
<p>
It isnt necessary to buy an entire bitcoin: you can buy just a fraction of one if thats all you want or need.
</p>
<h4>Why would I buy Bitcoin?</h4>
<p>Bitcoin is in the news today more than ever. Thanks to skyrocketing prices and rollercoaster dips, everyone and their dogs are interested in learning how to buy and sell Bitcoin. As the most popular form of cryptocurrency, Bitcoin is now widely accepted around the world and has a growing number of applications. If you want to take advantage of that though, you first need to know how to buy Bitcoin and what to do with it when you have.
</p>
<h4>How to buy Bitcoin ?</h4>
<p>The best place to make your first Bitcoin purchase is on an exchange. There are a whole lot of exchanges out there, with varying performance. Some are less trustworthy than others, and some can be quite limited, so its crucial to pick the right exchange in the first place. We recommend using <a href="https://www.binance.com/en/register?ref=CAWS9NNE" title="Buy Bitcoin">Binance</a>, follow the simple guide bellow to buy your first Bitcoins.
</p>
<h6>1. Buy Bitcoin on Binance</h6>
<p>We recommend buying Bitcoins on <a href="https://www.binance.com/en/register?ref=CAWS9NNE" title="Buy Bitcoin">Binance</a> because in terms of volume, it is considered to be the world's leading platform.
In 2019, Binance reports an average trading volume of <strong>$2.8 billion per day</strong>. This platform, launched on July 14, 2017 in Hong Kong, has the advantage of operating with technical assistance 24 hours a day, 7 days a week. Its <strong>CEO Zhao Changpeng</strong>, who has become a billionaire, was featured on the cover of the prestigious Forbes magazine in February 2018.
</p>
<h6>2. How do I register with <a href="https://www.binance.com/en/register?ref=CAWS9NNE" title="Buy Bitcoin">Binance</a> to buy Bitcoin?</h6>
<p>The first step before buying Bitcoin on <a href="https://www.binance.com/en/register?ref=CAWS9NNE" title="Buy Bitcoin">Binance</a> is to register. The user must first select the language in which they want to access the site, then enter a valid email address and finally choose a password.
</p>
<h3 class="text-center"><a href="https://www.binance.com/en/register?ref=CAWS9NNE" title="Buy Bitcoin">Click here to register an account with Binance now!<a></h3>
<img src="/static/img/binance1.png" alt="Register Binance">
<p>For your security, the platform offers you to proceed to the 2FA, also called "Two Step Verification", which consists in logging in with your password and your phone using <a href="https://support.google.com/accounts/answer/1066447?co=GENIE.Platform%3DAndroid">Google Authenticator</a>. Both we and the platform recommend that you delay the purchase of your Bitcoins by opting for this extra layer of security. Binance Academy 2fa available <a href="https://academy.binance.com/tutorials/binance-2fa-guide">here</a>.
</p>
<img src="/static/img/binance2.png" alt="2FA Binance">
<p>The second step is to verify your identity. Indeed, <a href="https://www.binance.com/en/register?ref=CAWS9NNE" title="Buy Bitcoin">Binance</a> is subject to KYC regulations as part of the fight against money laundering and terrorist financing. The platform will ask you whether or not you reside in China, before moving on to other personal information (surname, first name, gender, country of residence).
</p>
<img src="/static/img/binance3.png" alt="Verify identity Binance">
<p>Authentication is done by sending the number and a photocopy of both sides of your ID and a photo of you. On this photo, you must show the same ID and a newspaper (or other document) with the current date.
</p>
<img src="/static/img/binance4.png" alt="Verify identity Binance">
<p>The easiest way to get Bitcoin at Binance is to buy it by credit card. To do so, head over to the “Buy Crypto” section on the <a href="https://www.binance.com/en/register?ref=CAWS9NNE" title="Buy Bitcoin">Binance</a> toolbar, and choose the “Buy with Credit Card” option.
</p>
<img src="/static/img/binance6.png" alt="Verify identity Binance">
<p>Select your currency and BTC coin you wish to purchase and enter the amount before clicking "Buy BTC". All you will need to do is enter your bank details and the purchase will be completed.</p>
<img src="/static/img/binance5.png" alt="Verify identity Binance">
<p>Although it is mainly focused on the exchange of cryptomoney, <a href="https://www.binance.com/en/register?ref=CAWS9NNE" title="Buy Bitcoin">Binance</a> is now establishing itself as a very competitive platform for the purchase of cryptomoney by credit card. It is also reassuring because of its reputation, its activity and its ecosystem.</p>
<h4>You're now a part of the future! Congrats!</h4>
<p></p>
<p>Read more about Bitcoins
<br><a href="https://bitcoin.org/en/">Bitcoin.org</a>
</p>
</div>
% rebase("base", settings=settings, pastes_count=pastes_count)

View File

@ -1,70 +1,73 @@
<div class="well" id="faq">
<h1>FAQ</h1>
<h1>FAQ</h1>
<hr width="90%">
<p></p>
<dl>
<dt>How does 0bin work?</dt>
<dd>
<p>A random key is generated and used to encrypt the paste, thanks to
the <a href="http://crypto.stanford.edu/sjcl/">sjcl</a>
JavaScript library.</p>
<p>The encrypted content is then sent to the server, which returns the
address of the newly created paste.</p>
<p>The JavaScript code redirects to this address, but it adds the
encryption key in the URL hash (#).</p>
<p>When somebody wants to read the paste, they will usually click on a link
with this URL. If the hash containing the key is a part of it, 0bin's
JavaScript will use it to decrypt the content sent by the server.</p>
<p>The browser never sends the hash to the server, so the latter does not
receives the key at any time.</p>
</dd>
<h4>How does 0bin work?</h4>
<dt>But JavaScript encryption is not secure!</dt>
<dd>
<p>No, it isn't.</p>
<p>The goal of 0bin is <strong>not</strong> to protect the user and their data
(including, obviously, their secrets).</p>
<p>Instead, it aims to protect the host from being sued for the
content users pasted on the pastebin. The idea is that you cannot
require somebody to moderate something they cannot read - as such,
the host is granted plausible deniability.</p>
<p>A random key is generated and used to encrypt the paste, thanks to
the <a href="http://crypto.stanford.edu/sjcl/">sjcl</a>
JavaScript library.</p>
<p>The encrypted content is then sent to the server, which returns the
address of the newly created paste.</p>
<p>The JavaScript code redirects to this address, but it adds the
encryption key in the URL hash (#).</p>
<p>When somebody wants to read the paste, they will usually click on a link
with this URL. If the hash containing the key is a part of it, 0bin's
JavaScript will use it to decrypt the content sent by the server.</p>
<p>The browser never sends the hash to the server, so the latter does not
receives the key at any time.</p>
<p>Remember that as an user, you should use 0bin in the same way as unencrypted and
insecure pastebins - that is, with caution. The only difference with those is that if
you decide to host a 0bin server, the encryption feature hopefully be used as a defense.
This is not proven, though! :-)
</dd>
<dt>What if the server changes the JavaScript code? And what happens in the case of a <a href="https://en.wikipedia.org/wiki/Man-in-the-middle_attack">MITM attack</a>?</dt>
<dd>
<p>Read above.</p>
<p>0bin is not built, and does not aim, to protect user data - but rather the host.
If any user data is compromised, 0bin still provides the host with
plausible deniability (as they ignore the content of the pastes).</p>
<p>It would make no sense if the host was to compromise the encryption process
to read the data; in that case, they wouldn't have
installed 0bin in the first place, as 0bin is here to protect them.</p>
<p><strong>However, if you want to ensure your data is not read in anyway, you should
not use 0bin</strong>. Use <a href="http://www.cypherpunks.ca/otr/">OTR</a> for chatting,
<a href="https://gnupg.org/">GnuPG</a> for encrypted & verified data sharing, with <a href="https://www.enigmail.net/">EnigMail</a>
for emails.</p>
<p>It would be unlikely for those softwares to fail you. Errors will nearly always come from your side - you ought to have a perfect <a href="https://en.wikipedia.org/wiki/Operations_security">operations security</a>
if you do not want your data to be leaked. Remember to use your common sense.</p>
</dd>
<dt>How did the idea of 0bin emerge?</dt>
<dd>
<p>0bin is based on <a href="http://sebsauvage.net/wiki/doku.php?id=php:zerobin">sebsauvage's work</a>.
The project sprang as a reaction to <a href="https://www.zdnet.com/blog/security/pastebin-to-hunt-for-hacker-pastes-anonymous-cries-censorship/11336">the implementation of a moderation system on Pastebin</a>,
due to the significant amount of illegal content pasted on it, or that it linked to.</p>
</dd>
<dt>How can I get 0bin?</dt>
<dd>
<p>0bin is an open-source project, and the code is hosted on <a href="https://github.com/sametmax/0bin">GitHub</a>.
You can either download a tarball or clone the repository.</p>
</dd>
</dl>
<h4>But JavaScript encryption is not secure!</h4>
<p>No, it isn't.</p>
<p>The goal of 0bin is <strong>not</strong> to protect the user and their data
(including, obviously, their secrets).</p>
<p>Instead, it aims to protect the host from being sued for the
content users pasted on the pastebin. The idea is that you cannot
require somebody to moderate something they cannot read - as such,
the host is granted plausible deniability.</p>
<p>Remember that as an user, you should use 0bin in the same way as unencrypted and
insecure pastebins - that is, with caution. The only difference with those is that if
you decide to host a 0bin server, the encryption feature hopefully be used as a defense.
This is not proven, though! :-)</p>
<h4>What if the server changes the JavaScript code? And what happens in the case of a <a
href="https://en.wikipedia.org/wiki/Man-in-the-middle_attack">MITM attack</a>?</h4>
<p>Read above.</p>
<p>0bin is not built, and does not aim, to protect user data - but rather the host.
If any user data is compromised, 0bin still provides the host with
plausible deniability (as they ignore the content of the pastes).</p>
<p>It would make no sense if the host was to compromise the encryption process
to read the data; in that case, they wouldn't have
installed 0bin in the first place, as 0bin is here to protect them.</p>
<p><strong>However, if you want to ensure your data is not read in anyway, you should
not use 0bin</strong>. Use <a href="http://www.cypherpunks.ca/otr/">OTR</a> for chatting,
<a href="https://gnupg.org/">GnuPG</a> for encrypted & verified data sharing, with <a
href="https://www.enigmail.net/">EnigMail</a>
for emails.</p>
<p>It would be unlikely for those softwares to fail you. Errors will nearly always come from your side - you
ought to have a perfect <a href="https://en.wikipedia.org/wiki/Operations_security">operations security</a>
if you do not want your data to be leaked. Remember to use your common sense.</p>
<h4>How did the idea of 0bin emerge?</h4>
<p>0bin is based on <a href="http://sebsauvage.net/wiki/doku.php?id=php:zerobin">sebsauvage's work</a>.
The project sprang as a reaction to <a
href="https://www.zdnet.com/blog/security/pastebin-to-hunt-for-hacker-pastes-anonymous-cries-censorship/11336">the
implementation of a moderation system on Pastebin</a>,
due to the significant amount of illegal content pasted on it, or that it linked to.</p>
<h4>How can I get 0bin?</h4>
<p>0bin is an open-source project, and the code is hosted on <a
href="https://github.com/sametmax/0bin">GitHub</a>.
You can either download a tarball or clone the repository.</p>
</div>

View File

@ -1,38 +1,83 @@
<p class="file-upload" v-if="support.fileUpload">
<input type="button" class="btn btn-upload" value="Upload File" :value="isUploading ? 'Uploading...': 'Upload file'"
:disabled="isUploading">
<input type="file" class="hide-upload" id="file-upload" @change="handleUpload($event.target.files)">
</p>
<form class="well" method="post" action="/paste/create" @submit.prevent="encryptAndSendPaste()">
<p class="paste-option">
<label for="expiration">Expiration:</label>
<select id="expiration" name="expiration" v-model="newPaste.expiration">
<option value="burn_after_reading">Burn after reading</option>
<option selected value="1_day">1 day</option>
<option value="1_month">1 month</option>
<option value="never">Never</option>
</select>
<button type="submit" class="btn btn-primary">Submit</button>
</p>
<p>
<div class="progress progress-striped active" v-show="isLoading">
<div class="bar"></div>
</div>
<textarea rows="10" style="width:100%" class="input-xlarge" id="content" name="content" autofocus
v-on:keydown.prevent.ctrl.enter="encryptAndSendPaste()"></textarea>
</p>
<div class="d-flex justify-content-between">
<div>
<div class="file-upload" v-if="support.fileUpload">
<label type="button" class="btn btn-primary upload-file"
:disabled="isUploading">{% isUploading ? 'Uploading...': 'Upload file' %}
<input type="file" class="hide-upload" id="file-upload" @change="handleUpload($event.target.files)">
</label>
</div>
</div>
<div class="form-group select-date paste-option">
<div class="input-group">
<select id="expiration" name="expiration" class="custom-select" v-model="newPaste.expiration">
<option value="burn_after_reading">Burn after reading</option>
<option selected value="1_day">Expire in 1 day</option>
<option value="1_month">Expire in 1 month</option>
<option value="never">Never expire</option>
</select>
<div class="input-group-append">
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</div>
</div>
</div>
<div class="pre-wrapper">
<div class="progress" v-show="isLoading">
<div class="progress-bar progress-bar-striped" role="progressbar"></div>
</div>
<textarea rows="10" style="width:100%;" class="form-control" id="content" name="content" autofocus
@keydown.ctrl.enter="encryptAndSendPaste()"></textarea>
<div class="paste-options">
<h6>Optional fields (those are <em>not</em> encrypted):</h6>
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text">Title</span>
</div>
<input type="text" class="form-control paste-excerpt" name="paste-excerpt"
placeholder="Anything you type here will be visible by anyone, including search engines."
v-model="newPaste.title" maxlength="60">
</div>
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text tip" id="basic-addon1">Tip <svg xmlns="http://www.w3.org/2000/svg" width="18"
height="18" viewBox="0 0 24 24">
<path
d="M12 2c5.514 0 10 4.486 10 10s-4.486 10-10 10-10-4.486-10-10 4.486-10 10-10zm0-2c-6.627 0-12 5.373-12 12s5.373 12 12 12 12-5.373 12-12-5.373-12-12-12zm0 18v-1.511h-.5v1.511h-1v-1.511h-2.484l.25-1.489h.539c.442 0 .695-.425.695-.854v-4.444c0-.416-.242-.702-.683-.702h-.817v-1.5h2.5v-1.5h1v1.5h.5v-1.5h1v1.526c2.158.073 3.012.891 3.257 1.812.29 1.09-.429 2.005-1.046 2.228.75.192 1.789.746 1.789 2.026 0 1.742-1.344 2.908-4 2.908v1.5h-1zm-.5-5.503v2.503c1.984 0 3.344-.188 3.344-1.258 0-1.148-1.469-1.245-3.344-1.245zm0-.997c1.105 0 2.789-.078 2.789-1.25 0-1-1.039-1.25-2.789-1.25v2.5z"
fill="#eee" /></svg></span>
</div>
<input type="text" class="form-control paste-btc-tip-address" name="paste-btc-tip-address"
placeholder="Put a BTC address to ask for a tip. Leave it empty to let us use our."
v-model="newPaste.btcTipAddress" maxlength="50">
</div>
</div>
</div>
<div class="form-group select-date paste-option down" v-if="displayBottomToolBar">
<div class="input-group">
<select id="expiration" name="expiration" class="custom-select" v-model="newPaste.expiration">
<option value="burn_after_reading">Burn after reading</option>
<option selected value="1_day">Expire in 1 day</option>
<option value="1_month">Expire in 1 month</option>
<option value="never">Never expire</option>
</select>
<div class="input-group-append">
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</div>
</div>
<p class="paste-option down" v-if="displayBottomToolBar">
<label for="expiration">Expiration:</label>
<select id="expiration" name="expiration" v-model="newPaste.expiration">
<option value="burn_after_reading">Burn after reading</option>
<option selected value="1_day">1 day</option>
<option value="1_month">1 month</option>
<option value="never">Never</option>
</select>
<button type="submit" class="btn btn-primary">Submit</button>
</p>
</form>

31
zerobin/views/login.tpl Normal file
View File

@ -0,0 +1,31 @@
<form class="form-group" action="." method="post">
<div class="login-form">
<div class="admin-header">
<h3>Admin Panel</h3>
<br>
<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-hammer" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<path d="M9.812 1.952a.5.5 0 0 1-.312.89c-1.671 0-2.852.596-3.616 1.185L4.857 5.073V6.21a.5.5 0 0 1-.146.354L3.425 7.853a.5.5 0 0 1-.708 0L.146 5.274a.5.5 0 0 1 0-.706l1.286-1.29a.5.5 0 0 1 .354-.146H2.84C4.505 1.228 6.216.862 7.557 1.04a5.009 5.009 0 0 1 2.077.782l.178.129z"/>
<path fill-rule="evenodd" d="M6.012 3.5a.5.5 0 0 1 .359.165l9.146 8.646A.5.5 0 0 1 15.5 13L14 14.5a.5.5 0 0 1-.756-.056L4.598 5.297a.5.5 0 0 1 .048-.65l1-1a.5.5 0 0 1 .366-.147z"/>
</svg>
</div>
<form>
<label>Password:</label>
%if status == "error":
<div class="alert alert-danger" role="alert alert-danger">
{{message}}
</div>
%end
<div class="input-group">
<input type="password" id="password-field" placeholder="Enter your admin password here" name="password">
<div class="input-group-append">
<button type="submit" class="btn btn-secondary">Login</button>
</div>
</div>
</form>
</div>
</form>
% rebase('base', settings=settings, pastes_count=pastes_count)

View File

@ -1,6 +1,6 @@
%if "burn_after_reading" in str(paste.expiration):
%if keep_alive:
<p class="alert alert-info">
<p class="alert alert-dismissible alert-primary">
<a class="close" data-dismiss="alert" href="#" @click.prevent="$event.target.parentNode.remove()">×</a>
<strong class="title">Ok!</strong>
<span class="message">
@ -8,7 +8,7 @@
</span>
</p>
%else:
<p class="alert">
<p class="alert alert-warning alert-dismissible">
<a class="close" data-dismiss="alert" href="#" @click.prevent="$event.target.parentNode.remove()">×</a>
<strong class="title">Warning!</strong>
<span class="message">
@ -18,55 +18,129 @@
</p>
%end
%end
<div class="well paste-form">
<form action="/" method="get" accept-charset="utf-8">
<p class="lnk-option">
<a id="clip-button" v-if="support.clipboard" href="#" @click.prevent="copyToClipboard()">Copy To Clipboard</a> |
<a id="email-link" href="#" @click="handleSendByEmail($event)">Email this</a>
<div :class="{'d-flex': true, 'justify-content-between': true , 'reader-mode-tools': readerMode}">
<span class="paste-option btn-group top">
<button class="btn btn-clone" @click.prevent="handleClone()"><i class="icon-camera"></i>&nbsp;Clone</button>
<button class="btn" v-if="downloadLink.url">
<a :href="downloadLink.url" :download="downloadLink.name"><i class="icon-download"></i> Download</a>
<div class="btn-group" role="group">
<button v-if="support.clipboard && currentPaste.type === 'text'" @click.prevent="copyToClipboard()"
type="button" id="clip-button" class="btn btn-secondary responsive-icons">Copy to clipboard
<svg width="24" height="24" viewBox="0 0 20 20" class="bi bi-clipboard-plus" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" fill="#eee" d="M4 1.5H3a2 2 0 0 0-2 2V14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3.5a2 2 0 0 0-2-2h-1v1h1a1 1 0 0 1 1 1V14a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V3.5a1 1 0 0 1 1-1h1v-1z"/>
<path fill-rule="evenodd" fill="#eee" d="M9.5 1h-3a.5.5 0 0 0-.5.5v1a.5.5 0 0 0 .5.5h3a.5.5 0 0 0 .5-.5v-1a.5.5 0 0 0-.5-.5zm-3-1A1.5 1.5 0 0 0 5 1.5v1A1.5 1.5 0 0 0 6.5 4h3A1.5 1.5 0 0 0 11 2.5v-1A1.5 1.5 0 0 0 9.5 0h-3zM8 7a.5.5 0 0 1 .5.5V9H10a.5.5 0 0 1 0 1H8.5v1.5a.5.5 0 0 1-1 0V10H6a.5.5 0 0 1 0-1h1.5V7.5A.5.5 0 0 1 8 7z"/>
</svg>
</button>
<button type="button" id="email-link" class="btn btn-secondary responsive-icons" @click="handleSendByEmail($event)">
<svg width="24" height="24" viewBox="0 0 20 20" class="bi bi-envelope" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" fill="#eee" d="M0 4a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V4zm2-1a1 1 0 0 0-1 1v.217l7 4.2 7-4.2V4a1 1 0 0 0-1-1H2zm13 2.383l-4.758 2.855L15 11.114v-5.73zm-.034 6.878L9.271 8.82 8 9.583 6.728 8.82l-5.694 3.44A1 1 0 0 0 2 13h12a1 1 0 0 0 .966-.739zM1 11.114l4.758-2.876L1 5.383v5.73z"/>
</svg>
Email
this</button>
</div>
<button class="btn">New Paste</button>
</span>
</p>
<div>
<div class="progress progress-striped active" v-show="isLoading">
<div class="bar"></div>
<span class="paste-option btn-group responsive-icons">
<button class="btn btn-clone btn-secondary" @click.prevent="handleClone()">Clone
<svg width="24" height="24" viewBox="0 0 20 20" class="bi bi-files" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<path fill="#eee" fill-rule="evenodd" d="M3 2h8a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2zm0 1a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V4a1 1 0 0 0-1-1H3z"/>
<path d="M5 0h8a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2v-1a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1H5a1 1 0 0 0-1 1H3a2 2 0 0 1 2-2z"/>
</svg>
</button>
<a class="btn btn-secondary download-link" :href="currentPaste.downloadLink.url"
:download="currentPaste.downloadLink.name">Download
<svg width="24" height="24" viewBox="0 0 20 20" class="bi bi-cloud-download-fill" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<path fill="#eee" fill-rule="evenodd" d="M8 0a5.53 5.53 0 0 0-3.594 1.342c-.766.66-1.321 1.52-1.464 2.383C1.266 4.095 0 5.555 0 7.318 0 9.366 1.708 11 3.781 11H7.5V5.5a.5.5 0 0 1 1 0V11h4.188C14.502 11 16 9.57 16 7.773c0-1.636-1.242-2.969-2.834-3.194C12.923 1.999 10.69 0 8 0zm-.354 15.854a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 14.293V11h-1v3.293l-2.146-2.147a.5.5 0 0 0-.708.708l3 3z"/>
</svg>
</a>
<button class="btn btn-secondary">New Paste
<svg width="24" height="24" viewBox="0 0 20 20" class="bi bi-pencil-square" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<path fill="#eee" d="M15.502 1.94a.5.5 0 0 1 0 .706L14.459 3.69l-2-2L13.502.646a.5.5 0 0 1 .707 0l1.293 1.293zm-1.75 2.456l-2-2L4.939 9.21a.5.5 0 0 0-.121.196l-.805 2.414a.25.25 0 0 0 .316.316l2.414-.805a.5.5 0 0 0 .196-.12l6.813-6.814z"/>
<path fill-rule="evenodd" d="M1 13.5A1.5 1.5 0 0 0 2.5 15h11a1.5 1.5 0 0 0 1.5-1.5v-6a.5.5 0 0 0-1 0v6a.5.5 0 0 1-.5.5h-11a.5.5 0 0 1-.5-.5v-11a.5.5 0 0 1 .5-.5H9a.5.5 0 0 0 0-1H2.5A1.5 1.5 0 0 0 1 2.5v11z"/>
</svg>
</button>
</span>
</div>
</div>
<div class="progress-container">
<div class="progress active" v-show="isLoading">
<div class="progress-bar progress-bar-striped" role="progressbar"></div>
</div>
</div>
%expiration = paste.humanized_expiration
%if expiration:
<p id="expiration-tag">Expire {{ expiration }}</p>
<span id="expiration-tag">Expire {{ expiration }}</span>
%end
<p>
<pre id="paste-content" class="prettyprint">
<code>
{{ paste.content }}
</code>
</pre>
</p>
<pre id="paste-content" class="prettyprint" v-if="!readerMode">
<code>
{{ paste.content }}
</code>
</pre>
<p v-if="currentPaste.ownerKey">
<button type="button" class="btn btn-danger" @click="handleDeletePaste()">Delete this paste</button>
</p>
<div>
<div id="readable-paste-content" v-if="readerMode">
{% currentPaste.content %}
</div>
</div>
<p class="paste-option btn-group bottom">
<button class="btn btn-clone" @click.prevent="handleClone()"><i class="icon-camera"></i>&nbsp;Clone</button>
<div class="paste-options-res">
<div class="btn-group">
<span class="input-group-text">Tip it<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18"
viewBox="0 0 24 24">
<path
d="M12 2c5.514 0 10 4.486 10 10s-4.486 10-10 10-10-4.486-10-10 4.486-10 10-10zm0-2c-6.627 0-12 5.373-12 12s5.373 12 12 12 12-5.373 12-12-5.373-12-12-12zm0 18v-1.511h-.5v1.511h-1v-1.511h-2.484l.25-1.489h.539c.442 0 .695-.425.695-.854v-4.444c0-.416-.242-.702-.683-.702h-.817v-1.5h2.5v-1.5h1v1.5h.5v-1.5h1v1.526c2.158.073 3.012.891 3.257 1.812.29 1.09-.429 2.005-1.046 2.228.75.192 1.789.746 1.789 2.026 0 1.742-1.344 2.908-4 2.908v1.5h-1zm-.5-5.503v2.503c1.984 0 3.344-.188 3.344-1.258 0-1.148-1.469-1.245-3.344-1.245zm0-.997c1.105 0 2.789-.078 2.789-1.25 0-1-1.039-1.25-2.789-1.25v2.5z"
fill="#eee" /></svg></span>
<a class="btn btn-primary btc-tip-address"
href="bitcoin:{{ paste.btc_tip_address or 'bc1q4x6nwp56s9enmwtsa8um0gywpxdzeyrdluga04' }}">
{{ paste.btc_tip_address or 'bc1q4x6nwp56s9enmwtsa8um0gywpxdzeyrdluga04'}}
</a>
<button v-if="support.clipboard" class="btn btn-secondary" @click.prevent="copyBTCAdressToClipboard()">
{% this.btcCopied ? "Copied :)" : "Copy" %}
</button>
<a href="https://www.binance.com/en/register?ref=CAWS9NNE" class="btn btn-secondary buy-btc" target="_blank" title="Be cool, Buy Bitcoins!">Buy Bitcoins!</a>
</div>
</div>
<button class="btn" v-if="downloadLink.url">
<a :href="downloadLink.url" :download="downloadLink.name"><i class="icon-download"></i> Download</a>
</button>
<button class="btn">New Paste</button>
</p>
<div class="d-flex justify-content-between down">
<div>
<div v-if="currentPaste.ownerKey">
<button class="btn btn-secondary" @click.prevent="handleDeletePaste()">Delete Paste</button>
</div>
</div>
<div>
<span class="paste-option btn-group responsive-icons">
<button class="btn btn-clone btn-secondary" @click.prevent="handleClone()">Clone
<svg width="24" height="24" viewBox="0 0 20 20" class="bi bi-files" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<path fill="#eee" fill-rule="evenodd" d="M3 2h8a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2zm0 1a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V4a1 1 0 0 0-1-1H3z"/>
<path d="M5 0h8a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2v-1a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1H5a1 1 0 0 0-1 1H3a2 2 0 0 1 2-2z"/>
</svg>
</button>
<a class="btn btn-secondary download-link" :href="currentPaste.downloadLink.url"
:download="currentPaste.downloadLink.name">Download
<svg width="24" height="24" viewBox="0 0 20 20" class="bi bi-cloud-download-fill" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<path fill="#eee" fill-rule="evenodd" d="M8 0a5.53 5.53 0 0 0-3.594 1.342c-.766.66-1.321 1.52-1.464 2.383C1.266 4.095 0 5.555 0 7.318 0 9.366 1.708 11 3.781 11H7.5V5.5a.5.5 0 0 1 1 0V11h4.188C14.502 11 16 9.57 16 7.773c0-1.636-1.242-2.969-2.834-3.194C12.923 1.999 10.69 0 8 0zm-.354 15.854a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 14.293V11h-1v3.293l-2.146-2.147a.5.5 0 0 0-.708.708l3 3z"/>
</svg>
</a>
<button class="btn btn-secondary">New Paste
<svg width="24" height="24" viewBox="0 0 20 20" class="bi bi-pencil-square" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<path fill="#eee" d="M15.502 1.94a.5.5 0 0 1 0 .706L14.459 3.69l-2-2L13.502.646a.5.5 0 0 1 .707 0l1.293 1.293zm-1.75 2.456l-2-2L4.939 9.21a.5.5 0 0 0-.121.196l-.805 2.414a.25.25 0 0 0 .316.316l2.414-.805a.5.5 0 0 0 .196-.12l6.813-6.814z"/>
<path fill-rule="evenodd" d="M1 13.5A1.5 1.5 0 0 0 2.5 15h11a1.5 1.5 0 0 0 1.5-1.5v-6a.5.5 0 0 0-1 0v6a.5.5 0 0 1-.5.5h-11a.5.5 0 0 1-.5-.5v-11a.5.5 0 0 1 .5-.5H9a.5.5 0 0 0 0-1H2.5A1.5 1.5 0 0 0 1 2.5v11z"/>
</svg>
</button>
</span>
</div>
</div>
</form>
</div>
@ -74,39 +148,93 @@
<!-- For cloning -->
<div class="submit-form clone">
<form class="well" method="post" action="/paste/create" @submit.prevent="encryptAndSendPaste()">
<p class="paste-option">
<label for="expiration">Expiration:</label>
<select id="expiration" name="expiration" v-model="newPaste.expiration">
<option value="burn_after_reading">Burn after reading</option>
<option selected value="1_day">1 day</option>
<option value="1_month">1 month</option>
<option value="never">Never</option>
</select>
<button type="submit" class="btn btn-primary">Submit</button>
<button class="btn btn-danger" @click.prevent="handleCancelClone()">Cancel clone</button>
</p>
<div>
<div class="progress progress-striped active" v-show="isLoading">
<div class="bar"></div>
<div class="d-flex justify-content-between">
<div>
<div class="file-upload">
<button type="button" class="btn btn-info" @click.prevent="handleCancelClone()">Cancel clone</button>
</div>
</div>
<textarea rows="10" style="width:100%;" class="input-xlarge" id="content" name="content" autofocus
v-on:keydown.prevent.ctrl.enter="encryptAndSendPaste()"></textarea>
<div class="form-group select-date-clone paste-option">
<div class="input-group">
<select id="expiration" name="expiration" class="custom-select" v-model="newPaste.expiration">
<option value="burn_after_reading">Burn after reading</option>
<option selected value="1_day">Expire in 1 day</option>
<option value="1_month">Expire in 1 month</option>
<option value="never">Never expire</option>
</select>
<div class="input-group-append">
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</div>
</div>
</div>
<p class="paste-option down" v-if="displayBottomToolBar">
<label for="expiration">Expiration:</label>
<select id="expiration" name="expiration" v-model="newPaste.expiration">
<option value="burn_after_reading">Burn after reading</option>
<option selected value="1_day">1 day</option>
<option value="1_month">1 month</option>
<option value="never">Never</option>
</select>
<button type="submit" class="btn btn-primary">Submit</button>
</p>
<div class="progress-container progress-clone">
<div class="progress active" v-show="isLoading">
<div class="progress-bar progress-bar-striped" role="progressbar"></div>
</div>
</div>
<div>
<textarea rows="10" style="width:100%;" class=" form-control" @keydown.ctrl.enter="encryptAndSendPaste()"
id="content" name="content"></textarea>
<div class="paste-options">
<h6>Optional fields (those are <em>not</em> encrypted):</h6>
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text">Title</span>
</div>
<input type="text" class="form-control paste-excerpt" name="paste-excerpt"
placeholder="Anything you type here will be visible by anyone, including search engines."
v-model="newPaste.title" maxlength="60">
</div>
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text" id="basic-addon1"><strong><svg xmlns="http://www.w3.org/2000/svg" width="18"
height="18" viewBox="0 0 24 24">
<path
d="M12 2c5.514 0 10 4.486 10 10s-4.486 10-10 10-10-4.486-10-10 4.486-10 10-10zm0-2c-6.627 0-12 5.373-12 12s5.373 12 12 12 12-5.373 12-12-5.373-12-12-12zm0 18v-1.511h-.5v1.511h-1v-1.511h-2.484l.25-1.489h.539c.442 0 .695-.425.695-.854v-4.444c0-.416-.242-.702-.683-.702h-.817v-1.5h2.5v-1.5h1v1.5h.5v-1.5h1v1.526c2.158.073 3.012.891 3.257 1.812.29 1.09-.429 2.005-1.046 2.228.75.192 1.789.746 1.789 2.026 0 1.742-1.344 2.908-4 2.908v1.5h-1zm-.5-5.503v2.503c1.984 0 3.344-.188 3.344-1.258 0-1.148-1.469-1.245-3.344-1.245zm0-.997c1.105 0 2.789-.078 2.789-1.25 0-1-1.039-1.25-2.789-1.25v2.5z"
fill="#eee" /></svg> tip</strong></span>
</div>
<input type="text" class="form-control paste-btc-tip-address" name="paste-btc-tip-address"
placeholder="Put a BTC address to ask for a tip. Leave it empty to let us use our."
v-model="newPaste.btcTipAddress" maxlength="50">
</div>
</div>
</div>
<div class="d-flex justify-content-between" v-if="displayBottomToolBar">>
<div>
<div class="file-upload">
<button type="button" class="btn btn-info" @click.prevent="handleCancelClone()">Cancel clone</button>
</div>
</div>
<div class="form-group select-date-clone paste-option">
<div class="input-group">
<select id="expiration" name="expiration" class="custom-select" v-model="newPaste.expiration">
<option value="burn_after_reading">Burn after reading</option>
<option selected value="1_day">Expire in 1 day</option>
<option value="1_month">Expire in 1 month</option>
<option value="never">Never expire</option>
</select>
<div class="input-group-append">
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</div>
</div>
</div>
</form>
</div>
% rebase("base", settings=settings, pastes_count=pastes_count)

10
zerobin/wsgi.py Normal file
View File

@ -0,0 +1,10 @@
from zerobin.routes import get_app
# Remember you can set the following environment variables to configure
# how get_app() setup the 0bin:
#
# - ZEROBIN_DEBUG =
# - ZEROBIN_DATA_DIR
# - ZEROBIN_CONFIG_DIR
settings, app = get_app()