bepasty-1.0.0/0000755000175000017500000000000014023461127013135 5ustar useruser00000000000000bepasty-1.0.0/setup.cfg0000644000175000017500000000023214023461127014753 0ustar useruser00000000000000[tool:pytest] norecursedirs = .eggs .git .tox build markers = slow needs_server [flake8] max-line-length = 120 [egg_info] tag_build = tag_date = 0 bepasty-1.0.0/.github/0000755000175000017500000000000014023461127014475 5ustar useruser00000000000000bepasty-1.0.0/.github/workflows/0000755000175000017500000000000014023461127016532 5ustar useruser00000000000000bepasty-1.0.0/.github/workflows/ci.yml0000644000175000017500000000410614023456630017654 0ustar useruser00000000000000# badge: https://github.com/bepasty/bepasty-server/workflows/CI/badge.svg?branch=master name: CI on: push: branches: [ master ] paths: - '**.py' - '**.yml' - '**.cfg' - '**.ini' - 'requirements.d/*' - '!docs/**' pull_request: branches: [ master ] paths: - '**.py' - '**.yml' - '**.cfg' - '**.ini' - 'requirements.d/*' - '!docs/**' jobs: lint: runs-on: ubuntu-latest timeout-minutes: 10 steps: - uses: actions/checkout@v2 - name: Set up Python uses: actions/setup-python@v2 with: python-version: 3.8 - name: Lint with flake8 run: | pip install flake8 flake8 src scripts setup.py pytest: needs: lint strategy: matrix: include: - os: ubuntu-18.04 python-version: 3.5 toxenv: py35 - os: ubuntu-18.04 python-version: 3.6 toxenv: py36 - os: ubuntu-20.04 python-version: 3.7 toxenv: py37 - os: ubuntu-20.04 python-version: 3.8 toxenv: py38 - os: ubuntu-20.04 python-version: 3.9 toxenv: py39 env: TOXENV: ${{ matrix.toxenv }} runs-on: ${{ matrix.os }} timeout-minutes: 10 steps: - uses: actions/checkout@v2 with: # just fetching 1 commit is not enough for setuptools-scm, so we fetch all fetch-depth: 0 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v2 with: python-version: ${{ matrix.python-version }} - name: Install Python requirements run: | python -m pip install --upgrade pip setuptools wheel pip install -r requirements.d/dev.txt - name: run pytest via tox run: | tox --skip-missing-interpreters - name: Upload coverage to Codecov uses: codecov/codecov-action@v1 env: OS: ${{ runner.os }} python: ${{ matrix.python-version }} with: env_vars: OS, python bepasty-1.0.0/tox.ini0000644000175000017500000000077214023456630014461 0ustar useruser00000000000000[tox] envlist=py{35,36,37,38,39},flake8 [testenv] deps = -r{toxinidir}/requirements.d/dev.txt setenv = PYTHONPATH = {toxinidir} # as we do not start the server that serves the selenium tests using the misc. # python versions configured above (but manually with 1 specific python # version), there is no point in running these tests with tox: commands = pytest -m "not needs_server" --cov=bepasty --pyargs {posargs:bepasty.tests} [testenv:flake8] changedir = deps = flake8 commands = flake8 src/ setup.py bepasty-1.0.0/README.rst0000644000175000017500000000412013753341545014633 0ustar useruser00000000000000bepasty ======= bepasty is like a pastebin for all kinds of files (text, image, audio, video, documents, ..., binary). The documentation is there: https://bepasty-server.readthedocs.org/en/latest/ Features -------- * Generic: - you can upload multiple files at once, simply by drag and drop - after upload, you get a unique link to a view of each file - on that view, we show actions you can do with the file, metadata of the file and, if possible, we also render the file contents - if you uploaded multiple files, you can create a pastebin with the list of all these files - with a single click! - Set an expiration date for your files * Text files: - we highlight all text file types supported by pygments (a lot!) - we display line numbers - we link from line numbers to their anchors, so you can easily get a link to a specific line * Image files: - we show the image (format support depends on browser) * Audio and video files: - we show the html5 player for it (format support depends on browser) * asciinema recordings: - we show the asciinema player for .cast files * URLs: - we support linking to / redirecting to external URLs, you can use this as a link shortener (avoiding privacy / data protection issues that may exist with other link shorteners) * PDFs: - we support rendering PDFs in your browser (if your browser is able to) * Storage: we use a storage backend api, currently we have backends for: - filesystem storage (just use a filesystem directory to store .meta and .data files) - currently there are no other storage implementations in master branch and releases. The "ceph cluster" storage implementation has issues and currently lives in branch "ceph-storage" until these issues are fixed. * Keeping some control: - flexible permissions: read, create, modify, delete, list, admin - assign permissions to users of login secrets - assign default permissions to not-logged-in users - you can purge files from storage by age, inactivity, size, type, ... - you can do consistency checks on the storage bepasty-1.0.0/.readthedocs.yml0000644000175000017500000000016513747602022016230 0ustar useruser00000000000000version: 2 formats: all python: version: 3.7 install: - method: pip path: . system_packages: false bepasty-1.0.0/requirements.d/0000755000175000017500000000000014023461127016102 5ustar useruser00000000000000bepasty-1.0.0/requirements.d/dev.txt0000644000175000017500000000006413456157460017434 0ustar useruser00000000000000tox flake8 pytest pytest-cov selenium codecov twine bepasty-1.0.0/CHANGES.rst0000644000175000017500000002162014023456630014743 0ustar useruser00000000000000ChangeLog ========= Release 1.0.0 (2021-03-14) -------------------------- Compatibility: * require Python >= 3.5, drop Python 2.x support Fixes: * pygments compatibility fix, #258. don't crash on ``('JSONBareObject', (), (), ())``. Other changes: * move away from Travis-CI, use github workflow for CI Release 0.6.0 (2020-11-14) -------------------------- Compatibility: * drop python 3.4 support, #195 * note: this will likely be the last bepasty release supporting Python 2.x (2.7) and 3.5 (both are not supported by Python development any more). Fixes: * fix bad types for b64(en|de)code, #200 * use simple links in list/display view instead of
tags * security fix: if PERMISSIONS in config are changed, we invalidate old clientside cookies now. * fix creating empty file in storage/* * fix dealing with expired items * fix item.meta.get() in ItemDownloadView(), must be called with an argument. * fix typo in get_maxlife (MONTH => MONTHS). New features: * add support for asciinema recordings, #175 * show QR code with link to bepasty item, #176 * support text/x-bepasty-redirect for URL redirects: just paste the target URL and choose this as mimetype to create a redirect. you may use the delay= url argument to adjust the delay, default is 3s. * add "modify" operation to modify uploaded data (web UI and REST), this is controlled by "modify" permissions entry. * add optional python-magic support for application/octet-stream. Disabled by default, you can enable via: USE_PYTHON_MAGIC = True * REST api: - add delete/lock/unlock REST api - use json for error response of REST api - use application/json for upload REST api Other changes: * support / test on py38, py39, #223 * move development section from README to project docs, #192 * use twine to upload releases, qubes gpg support, #197 * add config for readthedocs, #191 * code: some cleanups, fix warnings, fix minor errors * theme: - upgrade to use bootstrap 4 - use font-awesome everywhere, remove font-glyphicon, #232 - use xstatic package for local font delivery - sort permission icons - misc. cosmetic fixes * robustness / consistency improvements: - handle bad Transaction-ID, bad Content-Length, bad Maxlife-{Value,Unit}, bad Range/Content-Range header - add exception handler for REST api to get consistent behaviour. * tests: - add test for APP_BASE_PATH (for our prefix middleware) - add screen shots test for UI - add REST api tests Release 0.5.0 (2018-10-15) -------------------------- Compatibility: * drop support for python 2.6 and 3.3 * add support for python 3.5, 3.6 and 3.7 * thus, you now need python 2.7 or python >= 3.4 * changes in source code layout: package bepasty is below src/ now * thus, you need to install bepasty now: pip install -e . * changed maxlife default from FOREVER to 1 MONTH. this avoids creating an ever-growing pastebin. users can still give other value if they like. Fixes: * REST api: fix off-by-one error in range calculations, #124 * config: reduce default body size by a 8kiB safety margin, #155 * multiple abort buttons for multiple file uploads, #29 * progress bar fixes, #131 * fix display of "undefined", should be "never", #129 * abort button now works w/ multiple files, #111 * upload form: don't linebreak in numbers, #122 * +list: work around 0-byte .meta files breaking the view, #147 New features: * run bepasty at non-root URLs, see APP_BASE_PATH in the config. * use icons instead of text for permissions (with hover-text) * REST api: GET /apis/rest/items returns the list of all items Other changes: * re-style upload form * add a favicon.ico (plus svg source) * docs updates * docs/config: clarify config updating, credentials/secrets, #151 * lots of cleanups for packaging, testing, source code * upgrade xstatic package requirements, #171 Release 0.4.0 (2014-11-11) -------------------------- New features: * shorter, easy-to-read URLs / filenames (old uuid4 style URLs still supported, but not generated any more for new items) * "list" permission separated from "admin" permission. - list: be able to list (discover) all pastebins - admin: be able to lock/unlock files, do actions even if upload is not completed or item is locked Make sure you update your PERMISSIONS configuration (you likely want to give "list" to the site administrator). By giving "list" (and/or "delete") permission to more users, you could operate your bepasty site in a rather public way (users seeing stuff from other users, maybe even being able to delete stuff they see). Fixes: * give configured limits to JS also, so stuff has not to be kept in sync manually, fixes #109 * highlighted text file views: set fixed width to line number column, fixes #108 * fixed crash for inline and download views when item was already deleted Other changes: * support Python 3.3+ additionally to 2.6+ * improved documentation, esp. about REST api * improve sample configs Release 0.3.0 (2014-08-22) -------------------------- New features: * support http basic auth header (it just reads the password from there, the user name is ignored). this is useful for scripting, e.g. you can do now: $ curl -F 'file=@somefile;type=text/plain' http://user:password@localhost:5000/+upload * you can give the filename for the list items now * do not use paste.txt as default filename, but .txt or .bin (this is less pretty, but avoids collisions if you download multiple files) * allow uploading of multiple files via the fileselector of the browser * display download (view) timestamp * sorting of file lists * use iso-8859-1 if decoding with utf-8 fails * let admin directly delete locked files, without having to unlock first * new bepasty-object cli command * added REST api for bepasty-client-cli * MAX_RENDER_SIZE can be used to set up maximum sizes for items of misc. types, so bepasty e.g. won't try to render a 1 GB text file with highlighting. * offer a "max. lifetime" when creating a pastebin * if you link to some specific text line, it will highlight that line now * add filename to the pastebin url (as anchor) Removed features: * removed ceph-storage implementation due to bugs, missing features and general lack of maintenance. it is still in the repo in branch ceph-storage, waiting to be merged back after these issues have been fixed: https://github.com/bepasty/bepasty-server/issues/13 https://github.com/bepasty/bepasty-server/issues/38 Fixes: * security fix: when showing potentially dangerous text/* types, force the content-type to be text/plain and also turn the browser's sniffer off. * security fix: prevent disclosure of locked item's metadata * use POST for delete/lock actions * application/x-pdf content-type items are offer for in-browser rendering, too * fix typo in cli command bepasty-object set --incomplete (not: uncomplete) * quite some UI / UX and other bug fixes * filesystem storage: check if the configured directory is actually writeable Other changes: * using xstatic packages now for all 3rd party static files * docs updated / enhanced No release 0.2.0 ---------------- We made quite quick progress due to many contributions from EuroPython 2014 sprint participants, so there was no 0.2.0 release and we directly jumped to 0.3.0. Release 0.1.0 (2014-06-29) -------------------------- New features: * add a textarea so one now actually can paste (not just upload) * simple login/logout and permissions system - see PERMISSIONS in config.py. * add lock/unlock functionality to web UI (admin) * add "List all items" on web UI (admin) * add link to online documentation * support inline viewing of PDFs * support Python 2.6 * after upload of multiple files, offer creation of list item * file uploads can be aborted (partially uploaded file will get deleted) * store upload timestamp into metadata * Compute hash of chunked uploads in a background thread directly after upload has finished. * new migrate cli subcommand to upgrade stored metadata (see --help for details) * new purge cli subcommand (see --help for details). you can use this to purge by age (since upload), inactivity (since last download), size or (mime)type. BEWARE: giving no criteria (like age, size, ...) means: purge all. Giving multiple criteria means they all must apply for files to get purged (AND - if you need OR, just run the command multiple times). * new consistency cli subcommand (see --help for details). you can check consistency of hash/size in metadata against what you have in storage. Optionally, you can compute hashes (if an empty hash was stored) or fix the metadata with the computed hash/size values. also, you can remove files with inconsistent hash / size. Fixes: * for chunked upload, a wrong hash was computed. Fixed. * misc. cosmetic UI fixes / misc. minor bug fixes * add project docs * use monospace font for textarea * now correctly positions to linenumber anchors Release 0.0.1 (2014-02-09) -------------------------- * first pypi release. release early, release often! :) bepasty-1.0.0/AUTHORS0000644000175000017500000000071514023456630014213 0ustar useruser00000000000000Bastian Blank Christian Fischer Thomas Waldmann Dennis Schmalacker Ana Balica Daniel Gonzalez Valentin Pratz Darko Ronic OGAWA Hirofumi Janne Heß (add your name/email above this line if you contributed to this project) bepasty-1.0.0/scripts/0000755000175000017500000000000014023461127014624 5ustar useruser00000000000000bepasty-1.0.0/scripts/sdist-sign0000755000175000017500000000046413456157460016655 0ustar useruser00000000000000#!/bin/bash R=$1 if [ "$R" = "" ]; then echo "Usage: sdist-sign 1.2.3" exit fi if [ "$QUBES_GPG_DOMAIN" = "" ]; then GPG=gpg else GPG=qubes-gpg-client-wrapper fi python setup.py sdist D=dist/bepasty-$R.tar.gz $GPG --detach-sign --local-user "Thomas Waldmann" --armor --output $D.asc $D bepasty-1.0.0/scripts/upload-pypi0000755000175000017500000000042613456157460017032 0ustar useruser00000000000000#!/bin/bash R=$1 if [ "$R" = "" ]; then echo "Usage: upload-pypi 1.2.3 [test]" exit fi if [ "$2" = "test" ]; then export TWINE_REPOSITORY_URL=https://test.pypi.org/legacy/ else export TWINE_REPOSITORY_URL= fi D=dist/bepasty-$R.tar.gz twine upload $D.asc $D bepasty-1.0.0/PKG-INFO0000644000175000017500000000710714023461127014237 0ustar useruser00000000000000Metadata-Version: 2.1 Name: bepasty Version: 1.0.0 Summary: a binary pastebin / file upload service Home-page: https://github.com/bepasty/bepasty-server/ Author: The Bepasty Team (see AUTHORS file) Author-email: Maintainer: Thomas Waldmann Maintainer-email: tw@waldmann-edv.de License: BSD 2-clause Description: bepasty ======= bepasty is like a pastebin for all kinds of files (text, image, audio, video, documents, ..., binary). The documentation is there: https://bepasty-server.readthedocs.org/en/latest/ Features -------- * Generic: - you can upload multiple files at once, simply by drag and drop - after upload, you get a unique link to a view of each file - on that view, we show actions you can do with the file, metadata of the file and, if possible, we also render the file contents - if you uploaded multiple files, you can create a pastebin with the list of all these files - with a single click! - Set an expiration date for your files * Text files: - we highlight all text file types supported by pygments (a lot!) - we display line numbers - we link from line numbers to their anchors, so you can easily get a link to a specific line * Image files: - we show the image (format support depends on browser) * Audio and video files: - we show the html5 player for it (format support depends on browser) * asciinema recordings: - we show the asciinema player for .cast files * URLs: - we support linking to / redirecting to external URLs, you can use this as a link shortener (avoiding privacy / data protection issues that may exist with other link shorteners) * PDFs: - we support rendering PDFs in your browser (if your browser is able to) * Storage: we use a storage backend api, currently we have backends for: - filesystem storage (just use a filesystem directory to store .meta and .data files) - currently there are no other storage implementations in master branch and releases. The "ceph cluster" storage implementation has issues and currently lives in branch "ceph-storage" until these issues are fixed. * Keeping some control: - flexible permissions: read, create, modify, delete, list, admin - assign permissions to users of login secrets - assign default permissions to not-logged-in users - you can purge files from storage by age, inactivity, size, type, ... - you can do consistency checks on the storage Keywords: text image audio video binary pastebin upload download service wsgi flask Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: Web Environment Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Requires-Python: >=3.5 Provides-Extra: magic bepasty-1.0.0/docs/0000755000175000017500000000000014023461127014065 5ustar useruser00000000000000bepasty-1.0.0/docs/source/0000755000175000017500000000000014023461127015365 5ustar useruser00000000000000bepasty-1.0.0/docs/source/user-cli.rst0000664000175000017500000000347112430402115017641 0ustar useruser00000000000000================================== Using bepasty with non-web clients ================================== pastebinit ========== pastebinit is a popular pastebin client (included in debian, ubuntu and maybe elsewhere) that can be configured to work with bepasty: Configuration ------------- ~/.pastebinit.xml:: https://bepasty.example.org Notes: * we set an empty default format so pastebinit will transmit this (and not its internal format default [which is "text" and completely useless for us as it is not a valid contenttype]) ~/.pastebin.d/bepasty.conf:: [pastebin] basename = bepasty.example.org regexp = https://bepasty.example.org [format] content = text title = filename format = contenttype page = page password = token [defaults] page = +upload Usage ----- Simplest:: echo "test" | pastebinit More advanced:: # give title (filename), password, input file pastebinit -t example.py -p yourpassword -i example.py # read from stdin, give title (filename), give format (contenttype) cat README | pastebinit -t README -f text/plain Notes: * we use -t ("title") to transmit the desired filename (we do not have a "title", but the filename that is used for downloading the pastebin is prominently displayed above the content, so can be considered as title also). * bepasty guesses the contenttype from the filename given with -t. if you do not give a filename there or the contenttype is not guessable from it, you may need to give -f also (e.g. -f text/plain). * if you give the contenttype, but not the filename, bepasty will make up a filename. * you need to use -p if the bepasty instance you use requires you to log in before you can create pastebins. bepasty-1.0.0/docs/source/index.rst0000664000175000017500000000060512430402115017221 0ustar useruser00000000000000Welcome to bepasty's documentation! =================================== bepasty is like a pastebin for every kind of file (text, image, audio, video, documents, ...). You can upload multiple files at once, simply by drag and drop. Contents -------- .. toctree:: :maxdepth: 2 intro user rest user-cli quickstart install-tutorial changelog project license bepasty-1.0.0/docs/source/rest.rst0000644000175000017500000002311613753644310017105 0ustar useruser00000000000000======================== Using bepasty's REST-API ======================== The Rest-API enables you to upload and download files, as well as retrieve informations about the file on the server. Currently the REST API provides the following API Endpoints:: GET /apis/rest GET /apis/rest/items POST /apis/rest/items GET /apis/rest/items/ GET /apis/rest/items//download POST /apis/rest/items//modify POST /apis/rest/items//delete POST /apis/rest/items//lock POST /apis/rest/items//unlock Errors ====== The error response from REST-API will set ``Content-Type: application/json``, and body as JSON format like the following example. (it was previously ``Content-Type: text/html; charset=utf-8`` and partial HTML page or plain string) Example:: { "error": { "code": , "message": "" } } Retrieving information for uploading ==================================== API Interface: :: GET /apis/rest GET Response by the server: Example Response:: { MAX_ALLOWED_FILE_SIZE: 5000000000, MAX_BODY_SIZE: 1048576 } This interface will give you important infos for uploading and downloading files to your bepasty server. By now only the MAX_BODY_SIZE will be delivered to you, as no more info is available. MAX_BODY_SIZE The maximum size of a post request's body. This is limited by the webserver and other middleware. See the documentation for more information. This also gives you the maximum size for the chunked upload. MAX_ALLOWED_FILE_SIZE The maximum allowed filesize that can be stored on the server. Files uploads bigger than this limit will be aborted and the file on the server will be deleted. Uploading a file ================ API Interface: :: POST /apis/rest/items When uploading a file, chunked upload is mandatory. Check the MAX_BODY_SIZE for the maximum chunk size that can be sent to the server. The body of the post request contains the base64 encoded binary of the file to be uploaded. (required permission: :ref:`create `) POST Request by the client: Post Request Body Contains the base64 encoded binary of the file to be uploaded. The following headers *can (cursive)* or **must (bold)** be delivered by every post request to the server: **Content-Range** The content-range header follows the specification by the w3c (http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.16). It has to be provided consistently and can resume a aborted file upload, together with the transaction-ID. **Transaction-ID** The transaction-ID will be provided by the server after the first upload chunk. After that first chunk, the transaction-id has to be provided by the client, to continue uploading the file. *Content-Type* The content-type of the file uploaded to the server. If the content-type is not given, the server will guess the content-type by the filename. If this fails the content-type will be 'application/octet-stream' *Content-Length* The content-length is mostly ignored by the server. It can be used to indicate the final file size. If your final file size is bigger than the maximum allowed size on the server, the upload will be aborted. The real filesize will be calculated by the server while uploading. *Content-Filename* The content-filename header can be used to name the file on the server. If no content-filename is passed, the server will generate a name from scratch. Maximum filename size is 50 characters. *Maxlife-Unit* The maxlife-unit can be used with the maxlife-value header to define a lifetime for the file that is uploaded. The unit has to be one of these:: ['MINUTES', 'HOURS', 'DAYS', 'WEEKS', 'MONTHS', 'YEARS', 'FOREVER'] If this header is omitted the unit will be forever *Maxlife-Value* The maxlife-value header defines the value of the maxlife-unit. POST Response by the server: *Transaction-ID* Transaction-ID provided for continued upload in a chunked upload process. *Content-Disposition* The URI of the newly uploaded file on the server. Will only be provided when upload is finished and successful. Retrieving information about a file =================================== API Interface: :: GET /apis/rest/items/ (required permission: :ref:`read `) GET Request by the client: **itemname** The itemname of the file requested. GET Response by the server: Example Response:: { file-meta: { complete: true, filename: "Wallpaper Work.7z", hash: "dded24ba6f1d953bedb9d2745635a6f7462817061763b0d70f68b7952722f275", locked: false, size: 150225567, timestamp-download: 1414483078, timestamp-max-life: -1, timestamp-upload: 1414443534, type: "application/x-7z-compressed" }, uri: "/apis/rest/items/N24bFRZm" } *URI* The URI of the file on the server. Used to link to the download. *File-Meta* *Filename* The Filename of the uploaded file. *Size* The calculated size of the file on the server. *Timestamp-Upload* The timestamp of the moment the file was uploaded. *Timestamp-Download* The timestamp of the last download. *Timestamp-Max_life* The lifetime timestamp of the file in seconds. -1 means to keep the file forever. *Complete* True if the file upload is completed. False if it isn't *Locked* Whether the file is locked or not. *Hash* The sha256 hash of the file uploaded. Calculated by the server. *Type* Mimetype of the file uploaded. If no filetype is provided this will be set to 'application/octet-stream'. Retrieving Item List ==================== API Interface: :: GET /apis/rest/items (required permission: :ref:`list `) GET Request by the client: No Parameters GET Response by the server: Example Response:: { "N24bFRZm": { file-meta: { complete: true, filename: "Wallpaper Work.7z", hash: "dded24ba6f1d953bedb9d2745635a6f7462817061763b0d70f68b7952722f275", locked: false, size: 150225567, timestamp-download: 1414483078, timestamp-max-life: -1, timestamp-upload: 1414443534, type: "application/x-7z-compressed" }, uri: "/apis/rest/items/N24bFRZm" }, ... } Parameters are the same as in *Retrieving information about a file*. Downloading a file ================== API Interface: :: GET /apis/rest/items//download (required permission: :ref:`read `) GET Response by the server: Example Response:: Content-Type: application/x-7z-compressed Content-Length: 150225568 Content-Disposition: attachment; filename="Wallpaper Work.7z" Content-Range: bytes 0-150225567/150225567 Opens up a stream and delivers the binary data directly. The above headers can be found in the HTTP Response. Modifying metadata ================== API Interface: :: POST /apis/rest/items//modify Modify metadata specified by ````. (required permission: :ref:`modify `) POST Request by the client: **itemname** The itemname of the target file. **Content-Type** The content-type header must be ``application/json`` New metadata is specified by JSON in the request body. Currently this API is supporting to modify ``filename`` and ``type``. For example, if you want to modify the filename:: {"filename": "new-filename.txt"} if you want to modify both filename and type:: {"filename": "new-filename.txt", "type": "new-mimetype"} POST Response by the server: On success, status code == 200. Otherwise status code != 200. Deleting a file =============== API Interface: :: POST /apis/rest/items//delete Delete a file specified by ````. (required permission: :ref:`delete `) POST Request by the client: **itemname** The itemname of the target file. POST Response by the server: On success, status code == 200. Otherwise status code != 200. Locking a file ============== API Interface: :: POST /apis/rest/items//lock Lock a file specified by ````. (required permission: :ref:`admin `) POST Request by the client: **itemname** The itemname of the target file. POST Response by the server: On success, status code == 200. Otherwise status code != 200. Unlocking a file ================ API Interface: :: POST /apis/rest/items//unlock Lock a file specified by ````. (required permission: :ref:`admin `) POST Request by the client: **itemname** The itemname of the target file. POST Response by the server: On success, status code == 200. Otherwise status code != 200. bepasty-1.0.0/docs/source/user.rst0000644000175000017500000001263013753644310017105 0ustar useruser00000000000000============================= Using bepasty's web interface ============================= .. _permissions: Logging in and Permissions ========================== You may need to log in to get enough permissions required for the misc. functions of bepasty. Your current permissions are shown on the web interface (in the navigation bar). To log in, you need to know credentials - ask the admin who runs the site. Bepasty does **not** use the usual user/password scheme, but **only** uses passwords (or passphrases) as credentials - there are no separate user names. The site admin can assign permissions to login credentials (and also to the anonymous, not logged-in user): * create: be able to create pastebins * modify: be able to modify pastebins * read: be able to read / download pastebins * delete: be able to delete pastebins * list: be able to list (discover) all pastebins * admin: be able to lock/unlock files, do actions even if upload is not completed or item is locked Be careful about who you give what permissions - especially "admin" and "list" are rather critical ones. If you want good privacy, do not give "list" to anybody (except site administrator maybe). If you want to do everything rather in the public, you may give "list" to users (or even use it by default for not-logged-in users). "admin" likely should be given only to very trusted people, like site administrator. Pasting text ============ Just paste it into that big upper text input box. Content-Type: Below the box you can optionally choose the type of your content (if you don't, it will become plain text). Just start typing some letters of the type, e.g. if you pasted some python code, type pyt and see how it offers you some choices based on that. Based on that type, we will highlight your text (using the Pygments library, which supports a lot of text formats). File name: You can optionally give a filename for your paste. If someone later downloads it, the browser will use the filename you gave. If you don't give a filename, bepasty will make something up. Maximum lifetime: The file will be automatically deleted after this time is over When finished, click on "Submit". bepasty will save your text using a unique ID and redirect you to the URL where you can view or download your pasted text. Uploading files =============== See that big box below the text input box - you can: * click it to upload files via the file selection dialogue of your browser * drag files from your desktop and drop them there Note: some features require a modern browser, like a current Firefox or Chrome/Chromium with Javascript enabled. It will show you a progress indication while the files upload. After the files are uploaded, bepasty will show you a list of links to the individual views of each uploaded file. Make sure you keep all the links (open the links in new tabs / new windows) - they are the only way to access the files. Additionally, bepasty prepared a file list for you (that has all the unique IDs of your uploaded files). If you create a list item by hitting the respective button, bepasty will store that list in yet another pastebin item, so you need to remember only that one URL. It's also a nice way to communicate a collection of files as you only need to communicate that one URL (not each individual file's URL). Viewing / Downloading files =========================== Just visit the file's unique URL to view, download or delete it. bepasty will show you metadata like: * file name * precise file size * upload date/time (UTC) * (last) download date/time (UTC) - viewing the data also counts as download * expiration date, if set * sha256 hash of the file contents bepasty also supports directly displaying the data, for these content types: * lists of files (if a list item was created at upload time) * text files (highlighted depending on the content-type) * PDFs (if you browser can render PDFs or has a plugin doing that) * asciinema cast files * image files, like jpeg, png and svg * audio/video files (using the html5 player widget, format support might depend on your browser and OS) * for other file types, you need to download them and open them with the appropriate application File hashes =========== If you're unfamiliar with hashes like SHA256, you might wonder what they are good for and why we show them. A hash is something like a checksum or fingerprint of the file contents. We compute the hash while or directly after the file upload. If you have 2 files at different places and they have the same SHA256 hash, you can be pretty sure that they have completely identical contents. If you are looking at a file you just uploaded yourself, it might be interesting to compare the size and hash with the values you see for your local file to make sure the upload worked correctly. Same after you download a file: check whether the hash matches for the file on bepasty and your downloaded file. If you transfer a file from location A via bepasty (B) to location C, you can also compare the file hashes at locations A and C to make sure the file was not modified or corrupted while being transferred. Important stuff to remember =========================== * if you get credentials from an admin, do not make them available to other persons except if explicitly allowed * files may go away at any time, always remember that a pastebin site is only for short-term temporary storage (how long this is depends on the site's / site admin's policy and available disk space) bepasty-1.0.0/docs/source/project.rst0000644000175000017500000000273413456160111017571 0ustar useruser00000000000000============================ The bepasty software Project ============================ History ======= The initial version of the bepasty(-server) software was developed in 48h in the WSGI Wrestle 2013 contest by: * `Bastian Blank `_ * `Christian Fischer `_ Project site ============ Source code repository, issue tracker (bugs, ideas about enhancements, todo, feedback, ...), link to documentation is all there: https://github.com/bepasty/ Contributing ============ Feedback is welcome. If you find some issue, have some idea or some patch, please submit them via the issue tracker. Or even better: if you use git, fork our repo, make your changes and submit a pull request. For small fixes, you can even just edit the files on github (github will then fork, change and submit a pull request automatically). Development =========== :: # Create a new virtualenv virtualenv bepasty-server-env # Activate the virtualenv source bepasty-server-env/bin/activate # Clone the official bepasty-server (or your fork, if you want to send PULL requests) git clone https://github.com/bepasty/bepasty-server.git cd bepasty-server # This will use the current directory for the installed package. # Very useful during development! It will also autoreload when files are changed pip install -e . # Run the bepasty-server in debug mode. The server is reachable in http://127.0.0.1:5000 bepasty-server --debug bepasty-1.0.0/docs/source/conf.py0000644000175000017500000002027613747602022016676 0ustar useruser00000000000000# -*- coding: utf-8 -*- # # bepasty documentation build configuration file, created by # sphinx-quickstart on Fri Jan 24 22:36:13 2014. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # on rtfd, the project gets installed via pip and scm_version generates _version: from bepasty._version import version # General information about the project. project = 'bepasty' author = 'The %s Team' % project copyright = '2020, %s' % author description = 'a binary pastebin / file upload service' # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. # needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.todo', 'sphinx.ext.viewcode', ] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. # source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = version # The full version, including alpha/beta/rc tags. release = version # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: # today = '' # Else, today_fmt is used as the format for a strftime call. # today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = [] # The reST default role (used for this markup: `text`) to use for all # documents. # default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. # add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). # add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. # show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. # modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. # keep_warnings = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'default' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. # html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. # html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". # html_title = None # A shorter title for the navigation bar. Default is the same as html_title. # html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. # html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. # html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. # html_extra_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. # html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. # html_use_smartypants = True # Custom sidebar templates, maps document names to template names. # html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. # html_additional_pages = {} # If false, no module index is generated. # html_domain_indices = True # If false, no index is generated. # html_use_index = True # If true, the index is split into individual pages for each letter. # html_split_index = False # If true, links to the reST sources are added to the pages. # html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. # html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. # html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. # html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). # html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'bepastydoc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # 'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). # 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. # 'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ ('index', 'bepasty.tex', u'bepasty Documentation', author, 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. # latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. # latex_use_parts = False # If true, show page references after internal links. # latex_show_pagerefs = False # If true, show URL addresses after external links. # latex_show_urls = False # Documents to append as an appendix to all manuals. # latex_appendices = [] # If false, no module index is generated. # latex_domain_indices = True # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'bepasty', u'bepasty Documentation', [author], 1) ] # If true, show URL addresses after external links. # man_show_urls = False # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ('index', 'bepasty', u'bepasty Documentation', author, 'bepasty', description, 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. # texinfo_appendices = [] # If false, no module index is generated. # texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. # texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. # texinfo_no_detailmenu = False bepasty-1.0.0/docs/source/quickstart.rst0000644000175000017500000000733213753644310020324 0ustar useruser00000000000000Quickstart ========== Installing bepasty ------------------ You can install bepasty either from PyPi (latest release) or from the git repository (latest available code). :: # from PyPi: pip install bepasty # if you'ld like to have python-magic to help determining files' mime types, use: pip install bepasty[magic] # from git repo pip install -e git+https://github.com/bepasty/bepasty-server.git#egg=bepasty-server Configuring bepasty ------------------- Before you can use bepasty, you need to carefully configure it (it won't work in default configuration and most of the configuration settings need your attention). When setting up permissions and giving out login secrets, carefully think about whom you give which permissions, especially when setting up the ``DEFAULT_PERMISSIONS`` (which apply to not-logged-in users). Here is the documentation straight from its config: .. autoclass:: bepasty.config.Config :members: To create a local and non-default configuration, copy ``bepasty/config.py`` to e.g. ``/srv/bepasty/bepasty.conf`` first, remove the ``class Config`` and remove all indents in the file. The comments can be removed too, if you feel the need to. At last modify these two configs variables: then modify it: :: # Note: no Config class required, just simple KEY = value lines: SECRET_KEY = '........................' STORAGE = 'filesystem' STORAGE_FILESYSTEM_DIRECTORY = '/srv/bepasty/storage/' # ... Important notes: * if you copied the file from the ``bepasty/config.py`` it will have a "class Config" in it and all the settings are inside that class. This is **not** what you need. Due to how flask config files work, you need to remove the class statement and outdent all the settings, so you just have global KEY = VALUE statements left on the top level of the config file. * if you run over http (like for trying it locally / for development), you need to change the configuration to use SESSION_SECURE_COOKIE = False (otherwise you can not login as it won't transmit the cookie over unsecure http). Starting bepasty server ----------------------- You can run the bepasty server with your local configuration by pointing to it via the BEPASTY_CONFIG environment variable like this: :: BEPASTY_CONFIG=/srv/bepasty/bepasty.conf bepasty-server Important note: * Use an absolute path as value for BEPASTY_CONFIG. The builtin WSGI server is recommended only for development and non-production use. For production, you should use a WSGI server like gunicorn, apache+mod-wsgi, nginx+uwsgi, etc. :: gunicorn bepasty.wsgi Invoking CLI commands --------------------- All bepasty commands expect either a --config argument or that the BEPASTY_CONFIG environment variable points to your configuration file. The "object" command operates on objects stored in the storage. You can get infos about them ("info" subcommand), you can set some flags on them ("set"), you can remove all or some ("purge"), you can check the consistency ("consistency"), etc... To get help about the object command, use: :: bepasty-object --help To get help about the object purge subcommand, use: :: bepasty-object purge --help To run the object purge subcommand (here: dry-run == do not remove anything, files >= 10MiB AND age >= 14 days), use something like: :: bepasty-object purge --dry-run --size 10 --age 14 '*' If you upgraded bepasty, you might need to upgrade the stored metadata to the current bepasty metadata schema: :: bepasty-object migrate '*' Note: the '*' needs to be quoted with single-quotes so the shell does not expand it. it tells the command to operate on all names in the storage (you could also give some specific names instead of '*'). bepasty-1.0.0/docs/source/_static/0000755000175000017500000000000014023461127017013 5ustar useruser00000000000000bepasty-1.0.0/docs/source/_static/.keep0000664000175000017500000000000012270557120017731 0ustar useruser00000000000000bepasty-1.0.0/docs/source/license.rst0000664000175000017500000000013212354105214017534 0ustar useruser00000000000000License ======= .. include:: ../../LICENSE Authors ======= .. include:: ../../AUTHORS bepasty-1.0.0/docs/source/changelog.rst0000664000175000017500000000003712354106074020052 0ustar useruser00000000000000.. include:: ../../CHANGES.rst bepasty-1.0.0/docs/source/install-tutorial.rst0000664000175000017500000000573213347774710021454 0ustar useruser00000000000000 ===================================================== Installation tutorial with Debian, NGinx and gunicorn ===================================================== preliminary packages: :: apt-get install build-essential nginx supervisor python-dev git-core python-pip python-virtualenv commands to run :: # add user bepasty to system adduser bepasty # change to user bepasty sudo su - bepasty # clone repository from github git clone https://github.com/bepasty/bepasty-server.git repo # create folder for storage mkdir storage # create folder for logs mkdir logs # create virtualenv virtualenv . # activate virtualenv . bin/activate cd repo # install bepasty and requirements pip install -e . # add gunicorn and gevent for hosting pip install gunicorn gevent config file for bepasty -- ``/home/bepasty/bepasty.conf``: Copy ``src/bepasty/config.py`` to ``/home/bepasty/bepasty.conf`` first, remove the ``class Config`` and remove all indents in the file. The comments can be removed too, if you feel the need to. At last modify these two configs variables: :: STORAGE = 'filesystem' STORAGE_FILESYSTEM_DIRECTORY = '/home/bepasty/storage/' add this content to ``/home/bepasty/bin/gunicorn_bepasty``: :: #!/bin/bash NAME="bepasty" HOME=/home/bepasty SOCKFILE=$HOME/gunicorn.sock # we will communicate using this unix socket PIDFILE=$HOME/gunicorn.pid NUM_WORKERS=3 # how many worker processes should Gunicorn spawn export BEPASTY_CONFIG=$HOME/bepasty.conf source $HOME/bin/activate cd $HOME/repo exec gunicorn bepasty.wsgi \ --name $NAME \ --workers $NUM_WORKERS \ --log-level=info \ --bind=unix:$SOCKFILE \ --pid $PIDFILE \ -k gevent Make it executable: ``chmod +x ~/bin/gunicorn_bepasty`` A nginx configuration i.e. in ``/etc/nginx/conf.d/bepasty.conf``: :: upstream pasty_server { server unix:/home/bepasty/gunicorn.sock fail_timeout=0; } server { listen 80; #listen [::]:80; #uncomment this if your server supports IPv6 server_name paste.example.org; # <-- add your domainname here access_log /home/bepasty/logs/nginx-access.log; error_log /home/bepasty/logs/nginx-error.log; client_max_body_size 32M; location / { proxy_set_header Host $http_host; proxy_pass http://pasty_server; } location /static/ { alias /home/bepasty/repo/src/bepasty/static/; } } Now reload your nginx configuration: `service nginx reload`. Supervisord config i.e. in ``/etc/supervisor/conf.d/bepasty.conf``: :: [program:bepasty] command = /home/bepasty/bin/gunicorn_bepasty ; Command to start app user = bepasty ; User to run as stdout_logfile = /home/bepasty/logs/gunicorn_supervisor.log ; Where to write log messages redirect_stderr = true ; Save stderr in the same log Finally reload supervisor: `service supervisor reload` bepasty-1.0.0/docs/source/intro.rst0000664000175000017500000000003612354333741017260 0ustar useruser00000000000000.. include:: ../../README.rst bepasty-1.0.0/docs/source/_templates/0000755000175000017500000000000014023461127017522 5ustar useruser00000000000000bepasty-1.0.0/docs/source/_templates/.keep0000664000175000017500000000000012270557126020446 0ustar useruser00000000000000bepasty-1.0.0/docs/build/0000755000175000017500000000000014023461127015164 5ustar useruser00000000000000bepasty-1.0.0/docs/build/.keep0000664000175000017500000000000012270557106016106 0ustar useruser00000000000000bepasty-1.0.0/docs/Makefile0000664000175000017500000001516712270556315015547 0ustar useruser00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = build # User-friendly check for sphinx-build ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) endif # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " xml to make Docutils-native XML files" @echo " pseudoxml to make pseudoxml-XML files for display purposes" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/bepasty.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/bepasty.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/bepasty" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/bepasty" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." latexpdfja: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through platex and dvipdfmx..." $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." xml: $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml @echo @echo "Build finished. The XML files are in $(BUILDDIR)/xml." pseudoxml: $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml @echo @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." bepasty-1.0.0/MANIFEST.in0000644000175000017500000000036414023456630014701 0ustar useruser00000000000000# stuff we need to include into the sdist is handled automatically by # setuptools_scm - it includes all git-committed files. # but we want to exclude some committed files/dirs not needed in the sdist: exclude .gitattributes .gitignore .github bepasty-1.0.0/src/0000755000175000017500000000000014023461127013724 5ustar useruser00000000000000bepasty-1.0.0/src/bepasty/0000755000175000017500000000000014023461127015373 5ustar useruser00000000000000bepasty-1.0.0/src/bepasty/__init__.py0000664000175000017500000000000013347732033017501 0ustar useruser00000000000000bepasty-1.0.0/src/bepasty/templates/0000755000175000017500000000000014023461127017371 5ustar useruser00000000000000bepasty-1.0.0/src/bepasty/templates/qr.html0000644000175000017500000001170313753337517020721 0ustar useruser00000000000000{% extends "_layout.html" %} {% block extra_link %} {% endblock %} {% block content %}
{% endblock %} bepasty-1.0.0/src/bepasty/templates/display.html0000644000175000017500000001220613753337517021743 0ustar useruser00000000000000{% extends "_layout.html" %} {%- import '_utils.html' as utils -%} {% block content %}

{{ item.meta['filename'] }}

{% if not item.meta['locked'] or may(ADMIN) %} QR Download Inline {% endif %} {% if may(MODIFY) %}
{{ utils.input_filename(item.meta['filename']) }}
{{ utils.input_contenttype(item.meta['type']) }}
{% endif %} {% if may(DELETE) %}
{% endif %} {% if may(ADMIN) %} {% if not item.meta['locked'] %}
{% else %}
{% endif %} {% endif %}

Type: {{ item.meta['type'] }}, Size: {{ item.meta['size'] }} bytes, SHA256: {{ item.meta['hash'] }}.
UTC timestamps: upload: {{ item.meta['timestamp-upload'] | datetime }}, download: {{ item.meta['timestamp-download'] | datetime }}, {% if item.meta['timestamp-max-life'] > 0 %} max lifetime: {{ item.meta['timestamp-max-life'] | datetime }}. {% else %} max lifetime: forever. {% endif %}

{{ rendered_content }}
{% endblock content %} {% block extra_link %} {% endblock %} {% block extra_script %} {% if may(MODIFY) %} {% endif %} {% endblock extra_script %} bepasty-1.0.0/src/bepasty/templates/filelist_tableonly.html0000664000175000017500000000010013347732033024141 0ustar useruser00000000000000{% import "_utils.html" as utils %} {{ utils.filelist(files) }} bepasty-1.0.0/src/bepasty/templates/error.html0000644000175000017500000000046713753337517021435 0ustar useruser00000000000000{% extends "_layout.html" %} {% block content %}

{{ heading }}

{{ body }}
{% endblock content %} bepasty-1.0.0/src/bepasty/templates/_utils.html0000644000175000017500000000760713753337517021606 0ustar useruser00000000000000{% macro filelist(files) %} {% for file in files %} {% endfor %}
Filename
Type
Size [B] Upload [UTC]
Download [UTC]
Actions
{{ file['filename'] }}
{{ file['type'] }}
{{ file['size'] }} {{ file['timestamp-upload'] | datetime }}
{% if file['timestamp-download'] %} {{ file['timestamp-download'] | datetime }} {% else %} never {% endif %}
display download inline {% if may(DELETE) %}
{% endif %} {% if may(ADMIN) %} {% if not file['locked'] %}
{% else %}
{% endif %} {% endif %}
{% endmacro %} {% macro input_filename(value) -%} {%- endmacro %} {% macro input_contenttype(value) -%} {%- endmacro %} {% macro contenttype_autocomplete(selector, contenttypes) -%} var availableTypes = ["{{ contenttypes | join('","') | safe}}"]; {{ selector|safe }}.autocomplete({source: availableTypes}); {%- endmacro %} bepasty-1.0.0/src/bepasty/templates/redirect.html0000644000175000017500000000104713452464142022067 0ustar useruser00000000000000

Redirecting ...

If you do not get redirected automatically in {{ delay }}s, feel free to click the link:

{{ url }}

Warning

Please note that the target site's content is neither controlled nor verified by us. Not even the link has been verified by us.

You use the internet on your own risk. If you break something, you own the pieces.

bepasty-1.0.0/src/bepasty/templates/_layout.html0000644000175000017500000001247013753341545021751 0ustar useruser00000000000000 Bepasty {% block extra_link %}{% endblock %}
{% block content %}{% endblock %}
{% block extra_script %}{% endblock %} bepasty-1.0.0/src/bepasty/templates/filelist.html0000644000175000017500000000032113753337517022104 0ustar useruser00000000000000{% import "_utils.html" as utils %} {% extends "_layout.html" %} {% block content %}
{{ utils.filelist(files) }}
{% endblock content %} bepasty-1.0.0/src/bepasty/templates/index.html0000644000175000017500000001544213753337517021412 0ustar useruser00000000000000{% extends "_layout.html" %} {%- import '_utils.html' as utils %} {% macro maximum_lifetime() -%}

{% endmacro %} {% block content %} {% if may(CREATE) %}
{{ utils.input_contenttype() }}
{{ utils.input_filename() }}

{{ maximum_lifetime() }} drag and drop files here - or click to select files
{% else %}

bepasty, the Binary File Upload Site

Free and Nice

A pastebin for all the stuff,
not just for text.

Free and Open Source

bepasty is free and open source software.
bepasty project on github

Awesome Code

Powered by Python and Flask.

{% endif %} {% endblock content %} {% block extra_link %} {% endblock %} {% block extra_script %} {% endblock %} bepasty-1.0.0/src/bepasty/static/0000755000175000017500000000000014023461127016662 5ustar useruser00000000000000bepasty-1.0.0/src/bepasty/static/app/0000755000175000017500000000000014023461127017442 5ustar useruser00000000000000bepasty-1.0.0/src/bepasty/static/app/bepasty.svg0000644000175000017500000001217013747573541021653 0ustar useruser00000000000000 image/svg+xml B pasty bepasty-1.0.0/src/bepasty/static/app/favicon.ico0000664000175000017500000001027613347732033021600 0ustar useruser00000000000000  ( @ OOzx( B600|v0)(|0?~0A0D&qo0}0,(Q)@h?Y0*K%F60t"WXXXXXc0a09%01$+00K%4Z*0(4rN.H|.4#"444/eEmO*??  '''ggf?bepasty-1.0.0/src/bepasty/static/app/css/0000755000175000017500000000000014023461127020232 5ustar useruser00000000000000bepasty-1.0.0/src/bepasty/static/app/css/style.css0000644000175000017500000000400313753337517022117 0ustar useruser00000000000000/* base styles -------------------------------------------------- */ html, body { height: 100%; max-width: 100%; margin: 0; -webkit-font-smoothing: antialiased; font-size: 100%; background-color: #fff; } #footer { height: 30px; position: relative; } #formupload { height: 400px; width: 100%; font-family: monospace; } #files p { word-break: break-all; } #files p .break-word { word-break: break-word; font-weight: normal !important; } #filelist { height: auto; width: 100%; font-family: monospace; } .fileupload-abort { float: right; margin-left: 10px; } .dropzone { width: 100%; padding: 3em; } #wrapper { min-height: 100%; margin: 0 auto -30px; padding: 0 0 60px; } #wrapper > .container { padding: 0 15px 60px; } .jumbotron .fa { font-size: 80px; } .jumbotron { font-size: 100%; text-align: center; } .alert { padding: 5px 10px; } .alert-processing { color: #464646; background-color: #f0f0f0; border-color: #eeeeee; } .alert-processing hr { border-top-color: #e6e6e6; } .alert-processing .alert-link { color: #959595; } /* if data is too wide, show scrollbars */ .data { width: 100%; overflow: auto; } .line-highlight { background-color: #ffc; } /* override pygments table highlighting style */ table.highlighttable { width: 100%; border: 1px solid #ccc; background-color: #f5f5f5; } .linenodiv pre, .highlight pre { margin-bottom: 0; -webkit-border-radius: 0; -moz-border-radius: 0; border-radius: 0; padding: 9.5px; } .highlight p { margin: 0; padding: 0 0.7em; } .linenodiv pre { border-right-style: none; } .linenodiv pre a:hover { text-decoration: none; } .highlight pre { padding-left: 0; padding-right: 0; border-left: 1px solid #ccc; } /* Set minimal width and let the contents push the block width */ .linenos { width: 1px; } /* autocomplete for modify dialog */ .ui-autocomplete { z-index: 1065; } bepasty-1.0.0/src/bepasty/static/app/js/0000755000175000017500000000000014023461127020056 5ustar useruser00000000000000bepasty-1.0.0/src/bepasty/static/app/js/qrcode.js0000644000175000017500000013013113747602022021673 0ustar useruser00000000000000// https://github.com/kazuhikoarase/qrcode-generator/blob/master/js/qrcode.js //--------------------------------------------------------------------- // // QR Code Generator for JavaScript // // Copyright (c) 2009 Kazuhiko Arase // // URL: http://www.d-project.com/ // // Licensed under the MIT license: // http://www.opensource.org/licenses/mit-license.php // // The word 'QR Code' is registered trademark of // DENSO WAVE INCORPORATED // http://www.denso-wave.com/qrcode/faqpatent-e.html // //--------------------------------------------------------------------- var qrcode = function() { //--------------------------------------------------------------------- // qrcode //--------------------------------------------------------------------- /** * qrcode * @param typeNumber 1 to 40 * @param errorCorrectionLevel 'L','M','Q','H' */ var qrcode = function(typeNumber, errorCorrectionLevel) { var PAD0 = 0xEC; var PAD1 = 0x11; var _typeNumber = typeNumber; var _errorCorrectionLevel = QRErrorCorrectionLevel[errorCorrectionLevel]; var _modules = null; var _moduleCount = 0; var _dataCache = null; var _dataList = new Array(); var _this = {}; var makeImpl = function(test, maskPattern) { _moduleCount = _typeNumber * 4 + 17; _modules = function(moduleCount) { var modules = new Array(moduleCount); for (var row = 0; row < moduleCount; row += 1) { modules[row] = new Array(moduleCount); for (var col = 0; col < moduleCount; col += 1) { modules[row][col] = null; } } return modules; }(_moduleCount); setupPositionProbePattern(0, 0); setupPositionProbePattern(_moduleCount - 7, 0); setupPositionProbePattern(0, _moduleCount - 7); setupPositionAdjustPattern(); setupTimingPattern(); setupTypeInfo(test, maskPattern); if (_typeNumber >= 7) { setupTypeNumber(test); } if (_dataCache == null) { _dataCache = createData(_typeNumber, _errorCorrectionLevel, _dataList); } mapData(_dataCache, maskPattern); }; var setupPositionProbePattern = function(row, col) { for (var r = -1; r <= 7; r += 1) { if (row + r <= -1 || _moduleCount <= row + r) continue; for (var c = -1; c <= 7; c += 1) { if (col + c <= -1 || _moduleCount <= col + c) continue; if ( (0 <= r && r <= 6 && (c == 0 || c == 6) ) || (0 <= c && c <= 6 && (r == 0 || r == 6) ) || (2 <= r && r <= 4 && 2 <= c && c <= 4) ) { _modules[row + r][col + c] = true; } else { _modules[row + r][col + c] = false; } } } }; var getBestMaskPattern = function() { var minLostPoint = 0; var pattern = 0; for (var i = 0; i < 8; i += 1) { makeImpl(true, i); var lostPoint = QRUtil.getLostPoint(_this); if (i == 0 || minLostPoint > lostPoint) { minLostPoint = lostPoint; pattern = i; } } return pattern; }; var setupTimingPattern = function() { for (var r = 8; r < _moduleCount - 8; r += 1) { if (_modules[r][6] != null) { continue; } _modules[r][6] = (r % 2 == 0); } for (var c = 8; c < _moduleCount - 8; c += 1) { if (_modules[6][c] != null) { continue; } _modules[6][c] = (c % 2 == 0); } }; var setupPositionAdjustPattern = function() { var pos = QRUtil.getPatternPosition(_typeNumber); for (var i = 0; i < pos.length; i += 1) { for (var j = 0; j < pos.length; j += 1) { var row = pos[i]; var col = pos[j]; if (_modules[row][col] != null) { continue; } for (var r = -2; r <= 2; r += 1) { for (var c = -2; c <= 2; c += 1) { if (r == -2 || r == 2 || c == -2 || c == 2 || (r == 0 && c == 0) ) { _modules[row + r][col + c] = true; } else { _modules[row + r][col + c] = false; } } } } } }; var setupTypeNumber = function(test) { var bits = QRUtil.getBCHTypeNumber(_typeNumber); for (var i = 0; i < 18; i += 1) { var mod = (!test && ( (bits >> i) & 1) == 1); _modules[Math.floor(i / 3)][i % 3 + _moduleCount - 8 - 3] = mod; } for (var i = 0; i < 18; i += 1) { var mod = (!test && ( (bits >> i) & 1) == 1); _modules[i % 3 + _moduleCount - 8 - 3][Math.floor(i / 3)] = mod; } }; var setupTypeInfo = function(test, maskPattern) { var data = (_errorCorrectionLevel << 3) | maskPattern; var bits = QRUtil.getBCHTypeInfo(data); // vertical for (var i = 0; i < 15; i += 1) { var mod = (!test && ( (bits >> i) & 1) == 1); if (i < 6) { _modules[i][8] = mod; } else if (i < 8) { _modules[i + 1][8] = mod; } else { _modules[_moduleCount - 15 + i][8] = mod; } } // horizontal for (var i = 0; i < 15; i += 1) { var mod = (!test && ( (bits >> i) & 1) == 1); if (i < 8) { _modules[8][_moduleCount - i - 1] = mod; } else if (i < 9) { _modules[8][15 - i - 1 + 1] = mod; } else { _modules[8][15 - i - 1] = mod; } } // fixed module _modules[_moduleCount - 8][8] = (!test); }; var mapData = function(data, maskPattern) { var inc = -1; var row = _moduleCount - 1; var bitIndex = 7; var byteIndex = 0; var maskFunc = QRUtil.getMaskFunction(maskPattern); for (var col = _moduleCount - 1; col > 0; col -= 2) { if (col == 6) col -= 1; while (true) { for (var c = 0; c < 2; c += 1) { if (_modules[row][col - c] == null) { var dark = false; if (byteIndex < data.length) { dark = ( ( (data[byteIndex] >>> bitIndex) & 1) == 1); } var mask = maskFunc(row, col - c); if (mask) { dark = !dark; } _modules[row][col - c] = dark; bitIndex -= 1; if (bitIndex == -1) { byteIndex += 1; bitIndex = 7; } } } row += inc; if (row < 0 || _moduleCount <= row) { row -= inc; inc = -inc; break; } } } }; var createBytes = function(buffer, rsBlocks) { var offset = 0; var maxDcCount = 0; var maxEcCount = 0; var dcdata = new Array(rsBlocks.length); var ecdata = new Array(rsBlocks.length); for (var r = 0; r < rsBlocks.length; r += 1) { var dcCount = rsBlocks[r].dataCount; var ecCount = rsBlocks[r].totalCount - dcCount; maxDcCount = Math.max(maxDcCount, dcCount); maxEcCount = Math.max(maxEcCount, ecCount); dcdata[r] = new Array(dcCount); for (var i = 0; i < dcdata[r].length; i += 1) { dcdata[r][i] = 0xff & buffer.getBuffer()[i + offset]; } offset += dcCount; var rsPoly = QRUtil.getErrorCorrectPolynomial(ecCount); var rawPoly = qrPolynomial(dcdata[r], rsPoly.getLength() - 1); var modPoly = rawPoly.mod(rsPoly); ecdata[r] = new Array(rsPoly.getLength() - 1); for (var i = 0; i < ecdata[r].length; i += 1) { var modIndex = i + modPoly.getLength() - ecdata[r].length; ecdata[r][i] = (modIndex >= 0)? modPoly.getAt(modIndex) : 0; } } var totalCodeCount = 0; for (var i = 0; i < rsBlocks.length; i += 1) { totalCodeCount += rsBlocks[i].totalCount; } var data = new Array(totalCodeCount); var index = 0; for (var i = 0; i < maxDcCount; i += 1) { for (var r = 0; r < rsBlocks.length; r += 1) { if (i < dcdata[r].length) { data[index] = dcdata[r][i]; index += 1; } } } for (var i = 0; i < maxEcCount; i += 1) { for (var r = 0; r < rsBlocks.length; r += 1) { if (i < ecdata[r].length) { data[index] = ecdata[r][i]; index += 1; } } } return data; }; var createData = function(typeNumber, errorCorrectionLevel, dataList) { var rsBlocks = QRRSBlock.getRSBlocks(typeNumber, errorCorrectionLevel); var buffer = qrBitBuffer(); for (var i = 0; i < dataList.length; i += 1) { var data = dataList[i]; buffer.put(data.getMode(), 4); buffer.put(data.getLength(), QRUtil.getLengthInBits(data.getMode(), typeNumber) ); data.write(buffer); } // calc num max data. var totalDataCount = 0; for (var i = 0; i < rsBlocks.length; i += 1) { totalDataCount += rsBlocks[i].dataCount; } if (buffer.getLengthInBits() > totalDataCount * 8) { throw new Error('code length overflow. (' + buffer.getLengthInBits() + '>' + totalDataCount * 8 + ')'); } // end code if (buffer.getLengthInBits() + 4 <= totalDataCount * 8) { buffer.put(0, 4); } // padding while (buffer.getLengthInBits() % 8 != 0) { buffer.putBit(false); } // padding while (true) { if (buffer.getLengthInBits() >= totalDataCount * 8) { break; } buffer.put(PAD0, 8); if (buffer.getLengthInBits() >= totalDataCount * 8) { break; } buffer.put(PAD1, 8); } return createBytes(buffer, rsBlocks); }; _this.addData = function(data) { var newData = qr8BitByte(data); _dataList.push(newData); _dataCache = null; }; _this.isDark = function(row, col) { if (row < 0 || _moduleCount <= row || col < 0 || _moduleCount <= col) { throw new Error(row + ',' + col); } return _modules[row][col]; }; _this.getModuleCount = function() { return _moduleCount; }; _this.make = function() { makeImpl(false, getBestMaskPattern() ); }; _this.createTableTag = function(cellSize, margin) { cellSize = cellSize || 2; margin = (typeof margin == 'undefined')? cellSize * 4 : margin; var qrHtml = ''; qrHtml += ''; qrHtml += ''; for (var r = 0; r < _this.getModuleCount(); r += 1) { qrHtml += ''; for (var c = 0; c < _this.getModuleCount(); c += 1) { qrHtml += ''; } qrHtml += ''; qrHtml += '
'; } qrHtml += '
'; return qrHtml; }; _this.createSvgTag = function(cellSize, margin) { cellSize = cellSize || 2; margin = (typeof margin == 'undefined')? cellSize * 4 : margin; var size = _this.getModuleCount() * cellSize + margin * 2; var c, mc, r, mr, qrSvg='', rect; rect = 'l' + cellSize + ',0 0,' + cellSize + ' -' + cellSize + ',0 0,-' + cellSize + 'z '; qrSvg += '>> 8); bytes.push(b & 0xff); } } else { bytes.push(unknownChar); } } } return bytes; }; }; //--------------------------------------------------------------------- // QRMode //--------------------------------------------------------------------- var QRMode = { MODE_NUMBER : 1 << 0, MODE_ALPHA_NUM : 1 << 1, MODE_8BIT_BYTE : 1 << 2, MODE_KANJI : 1 << 3 }; //--------------------------------------------------------------------- // QRErrorCorrectionLevel //--------------------------------------------------------------------- var QRErrorCorrectionLevel = { L : 1, M : 0, Q : 3, H : 2 }; //--------------------------------------------------------------------- // QRMaskPattern //--------------------------------------------------------------------- var QRMaskPattern = { PATTERN000 : 0, PATTERN001 : 1, PATTERN010 : 2, PATTERN011 : 3, PATTERN100 : 4, PATTERN101 : 5, PATTERN110 : 6, PATTERN111 : 7 }; //--------------------------------------------------------------------- // QRUtil //--------------------------------------------------------------------- var QRUtil = function() { var PATTERN_POSITION_TABLE = [ [], [6, 18], [6, 22], [6, 26], [6, 30], [6, 34], [6, 22, 38], [6, 24, 42], [6, 26, 46], [6, 28, 50], [6, 30, 54], [6, 32, 58], [6, 34, 62], [6, 26, 46, 66], [6, 26, 48, 70], [6, 26, 50, 74], [6, 30, 54, 78], [6, 30, 56, 82], [6, 30, 58, 86], [6, 34, 62, 90], [6, 28, 50, 72, 94], [6, 26, 50, 74, 98], [6, 30, 54, 78, 102], [6, 28, 54, 80, 106], [6, 32, 58, 84, 110], [6, 30, 58, 86, 114], [6, 34, 62, 90, 118], [6, 26, 50, 74, 98, 122], [6, 30, 54, 78, 102, 126], [6, 26, 52, 78, 104, 130], [6, 30, 56, 82, 108, 134], [6, 34, 60, 86, 112, 138], [6, 30, 58, 86, 114, 142], [6, 34, 62, 90, 118, 146], [6, 30, 54, 78, 102, 126, 150], [6, 24, 50, 76, 102, 128, 154], [6, 28, 54, 80, 106, 132, 158], [6, 32, 58, 84, 110, 136, 162], [6, 26, 54, 82, 110, 138, 166], [6, 30, 58, 86, 114, 142, 170] ]; var G15 = (1 << 10) | (1 << 8) | (1 << 5) | (1 << 4) | (1 << 2) | (1 << 1) | (1 << 0); var G18 = (1 << 12) | (1 << 11) | (1 << 10) | (1 << 9) | (1 << 8) | (1 << 5) | (1 << 2) | (1 << 0); var G15_MASK = (1 << 14) | (1 << 12) | (1 << 10) | (1 << 4) | (1 << 1); var _this = {}; var getBCHDigit = function(data) { var digit = 0; while (data != 0) { digit += 1; data >>>= 1; } return digit; }; _this.getBCHTypeInfo = function(data) { var d = data << 10; while (getBCHDigit(d) - getBCHDigit(G15) >= 0) { d ^= (G15 << (getBCHDigit(d) - getBCHDigit(G15) ) ); } return ( (data << 10) | d) ^ G15_MASK; }; _this.getBCHTypeNumber = function(data) { var d = data << 12; while (getBCHDigit(d) - getBCHDigit(G18) >= 0) { d ^= (G18 << (getBCHDigit(d) - getBCHDigit(G18) ) ); } return (data << 12) | d; }; _this.getPatternPosition = function(typeNumber) { return PATTERN_POSITION_TABLE[typeNumber - 1]; }; _this.getMaskFunction = function(maskPattern) { switch (maskPattern) { case QRMaskPattern.PATTERN000 : return function(i, j) { return (i + j) % 2 == 0; }; case QRMaskPattern.PATTERN001 : return function(i, j) { return i % 2 == 0; }; case QRMaskPattern.PATTERN010 : return function(i, j) { return j % 3 == 0; }; case QRMaskPattern.PATTERN011 : return function(i, j) { return (i + j) % 3 == 0; }; case QRMaskPattern.PATTERN100 : return function(i, j) { return (Math.floor(i / 2) + Math.floor(j / 3) ) % 2 == 0; }; case QRMaskPattern.PATTERN101 : return function(i, j) { return (i * j) % 2 + (i * j) % 3 == 0; }; case QRMaskPattern.PATTERN110 : return function(i, j) { return ( (i * j) % 2 + (i * j) % 3) % 2 == 0; }; case QRMaskPattern.PATTERN111 : return function(i, j) { return ( (i * j) % 3 + (i + j) % 2) % 2 == 0; }; default : throw new Error('bad maskPattern:' + maskPattern); } }; _this.getErrorCorrectPolynomial = function(errorCorrectLength) { var a = qrPolynomial([1], 0); for (var i = 0; i < errorCorrectLength; i += 1) { a = a.multiply(qrPolynomial([1, QRMath.gexp(i)], 0) ); } return a; }; _this.getLengthInBits = function(mode, type) { if (1 <= type && type < 10) { // 1 - 9 switch(mode) { case QRMode.MODE_NUMBER : return 10; case QRMode.MODE_ALPHA_NUM : return 9; case QRMode.MODE_8BIT_BYTE : return 8; case QRMode.MODE_KANJI : return 8; default : throw new Error('mode:' + mode); } } else if (type < 27) { // 10 - 26 switch(mode) { case QRMode.MODE_NUMBER : return 12; case QRMode.MODE_ALPHA_NUM : return 11; case QRMode.MODE_8BIT_BYTE : return 16; case QRMode.MODE_KANJI : return 10; default : throw new Error('mode:' + mode); } } else if (type < 41) { // 27 - 40 switch(mode) { case QRMode.MODE_NUMBER : return 14; case QRMode.MODE_ALPHA_NUM : return 13; case QRMode.MODE_8BIT_BYTE : return 16; case QRMode.MODE_KANJI : return 12; default : throw new Error('mode:' + mode); } } else { throw new Error('type:' + type); } }; _this.getLostPoint = function(qrcode) { var moduleCount = qrcode.getModuleCount(); var lostPoint = 0; // LEVEL1 for (var row = 0; row < moduleCount; row += 1) { for (var col = 0; col < moduleCount; col += 1) { var sameCount = 0; var dark = qrcode.isDark(row, col); for (var r = -1; r <= 1; r += 1) { if (row + r < 0 || moduleCount <= row + r) { continue; } for (var c = -1; c <= 1; c += 1) { if (col + c < 0 || moduleCount <= col + c) { continue; } if (r == 0 && c == 0) { continue; } if (dark == qrcode.isDark(row + r, col + c) ) { sameCount += 1; } } } if (sameCount > 5) { lostPoint += (3 + sameCount - 5); } } } // LEVEL2 for (var row = 0; row < moduleCount - 1; row += 1) { for (var col = 0; col < moduleCount - 1; col += 1) { var count = 0; if (qrcode.isDark(row, col) ) count += 1; if (qrcode.isDark(row + 1, col) ) count += 1; if (qrcode.isDark(row, col + 1) ) count += 1; if (qrcode.isDark(row + 1, col + 1) ) count += 1; if (count == 0 || count == 4) { lostPoint += 3; } } } // LEVEL3 for (var row = 0; row < moduleCount; row += 1) { for (var col = 0; col < moduleCount - 6; col += 1) { if (qrcode.isDark(row, col) && !qrcode.isDark(row, col + 1) && qrcode.isDark(row, col + 2) && qrcode.isDark(row, col + 3) && qrcode.isDark(row, col + 4) && !qrcode.isDark(row, col + 5) && qrcode.isDark(row, col + 6) ) { lostPoint += 40; } } } for (var col = 0; col < moduleCount; col += 1) { for (var row = 0; row < moduleCount - 6; row += 1) { if (qrcode.isDark(row, col) && !qrcode.isDark(row + 1, col) && qrcode.isDark(row + 2, col) && qrcode.isDark(row + 3, col) && qrcode.isDark(row + 4, col) && !qrcode.isDark(row + 5, col) && qrcode.isDark(row + 6, col) ) { lostPoint += 40; } } } // LEVEL4 var darkCount = 0; for (var col = 0; col < moduleCount; col += 1) { for (var row = 0; row < moduleCount; row += 1) { if (qrcode.isDark(row, col) ) { darkCount += 1; } } } var ratio = Math.abs(100 * darkCount / moduleCount / moduleCount - 50) / 5; lostPoint += ratio * 10; return lostPoint; }; return _this; }(); //--------------------------------------------------------------------- // QRMath //--------------------------------------------------------------------- var QRMath = function() { var EXP_TABLE = new Array(256); var LOG_TABLE = new Array(256); // initialize tables for (var i = 0; i < 8; i += 1) { EXP_TABLE[i] = 1 << i; } for (var i = 8; i < 256; i += 1) { EXP_TABLE[i] = EXP_TABLE[i - 4] ^ EXP_TABLE[i - 5] ^ EXP_TABLE[i - 6] ^ EXP_TABLE[i - 8]; } for (var i = 0; i < 255; i += 1) { LOG_TABLE[EXP_TABLE[i] ] = i; } var _this = {}; _this.glog = function(n) { if (n < 1) { throw new Error('glog(' + n + ')'); } return LOG_TABLE[n]; }; _this.gexp = function(n) { while (n < 0) { n += 255; } while (n >= 256) { n -= 255; } return EXP_TABLE[n]; }; return _this; }(); //--------------------------------------------------------------------- // qrPolynomial //--------------------------------------------------------------------- function qrPolynomial(num, shift) { if (typeof num.length == 'undefined') { throw new Error(num.length + '/' + shift); } var _num = function() { var offset = 0; while (offset < num.length && num[offset] == 0) { offset += 1; } var _num = new Array(num.length - offset + shift); for (var i = 0; i < num.length - offset; i += 1) { _num[i] = num[i + offset]; } return _num; }(); var _this = {}; _this.getAt = function(index) { return _num[index]; }; _this.getLength = function() { return _num.length; }; _this.multiply = function(e) { var num = new Array(_this.getLength() + e.getLength() - 1); for (var i = 0; i < _this.getLength(); i += 1) { for (var j = 0; j < e.getLength(); j += 1) { num[i + j] ^= QRMath.gexp(QRMath.glog(_this.getAt(i) ) + QRMath.glog(e.getAt(j) ) ); } } return qrPolynomial(num, 0); }; _this.mod = function(e) { if (_this.getLength() - e.getLength() < 0) { return _this; } var ratio = QRMath.glog(_this.getAt(0) ) - QRMath.glog(e.getAt(0) ); var num = new Array(_this.getLength() ); for (var i = 0; i < _this.getLength(); i += 1) { num[i] = _this.getAt(i); } for (var i = 0; i < e.getLength(); i += 1) { num[i] ^= QRMath.gexp(QRMath.glog(e.getAt(i) ) + ratio); } // recursive call return qrPolynomial(num, 0).mod(e); }; return _this; } //--------------------------------------------------------------------- // QRRSBlock //--------------------------------------------------------------------- var QRRSBlock = function() { var RS_BLOCK_TABLE = [ // L // M // Q // H // 1 [1, 26, 19], [1, 26, 16], [1, 26, 13], [1, 26, 9], // 2 [1, 44, 34], [1, 44, 28], [1, 44, 22], [1, 44, 16], // 3 [1, 70, 55], [1, 70, 44], [2, 35, 17], [2, 35, 13], // 4 [1, 100, 80], [2, 50, 32], [2, 50, 24], [4, 25, 9], // 5 [1, 134, 108], [2, 67, 43], [2, 33, 15, 2, 34, 16], [2, 33, 11, 2, 34, 12], // 6 [2, 86, 68], [4, 43, 27], [4, 43, 19], [4, 43, 15], // 7 [2, 98, 78], [4, 49, 31], [2, 32, 14, 4, 33, 15], [4, 39, 13, 1, 40, 14], // 8 [2, 121, 97], [2, 60, 38, 2, 61, 39], [4, 40, 18, 2, 41, 19], [4, 40, 14, 2, 41, 15], // 9 [2, 146, 116], [3, 58, 36, 2, 59, 37], [4, 36, 16, 4, 37, 17], [4, 36, 12, 4, 37, 13], // 10 [2, 86, 68, 2, 87, 69], [4, 69, 43, 1, 70, 44], [6, 43, 19, 2, 44, 20], [6, 43, 15, 2, 44, 16], // 11 [4, 101, 81], [1, 80, 50, 4, 81, 51], [4, 50, 22, 4, 51, 23], [3, 36, 12, 8, 37, 13], // 12 [2, 116, 92, 2, 117, 93], [6, 58, 36, 2, 59, 37], [4, 46, 20, 6, 47, 21], [7, 42, 14, 4, 43, 15], // 13 [4, 133, 107], [8, 59, 37, 1, 60, 38], [8, 44, 20, 4, 45, 21], [12, 33, 11, 4, 34, 12], // 14 [3, 145, 115, 1, 146, 116], [4, 64, 40, 5, 65, 41], [11, 36, 16, 5, 37, 17], [11, 36, 12, 5, 37, 13], // 15 [5, 109, 87, 1, 110, 88], [5, 65, 41, 5, 66, 42], [5, 54, 24, 7, 55, 25], [11, 36, 12, 7, 37, 13], // 16 [5, 122, 98, 1, 123, 99], [7, 73, 45, 3, 74, 46], [15, 43, 19, 2, 44, 20], [3, 45, 15, 13, 46, 16], // 17 [1, 135, 107, 5, 136, 108], [10, 74, 46, 1, 75, 47], [1, 50, 22, 15, 51, 23], [2, 42, 14, 17, 43, 15], // 18 [5, 150, 120, 1, 151, 121], [9, 69, 43, 4, 70, 44], [17, 50, 22, 1, 51, 23], [2, 42, 14, 19, 43, 15], // 19 [3, 141, 113, 4, 142, 114], [3, 70, 44, 11, 71, 45], [17, 47, 21, 4, 48, 22], [9, 39, 13, 16, 40, 14], // 20 [3, 135, 107, 5, 136, 108], [3, 67, 41, 13, 68, 42], [15, 54, 24, 5, 55, 25], [15, 43, 15, 10, 44, 16], // 21 [4, 144, 116, 4, 145, 117], [17, 68, 42], [17, 50, 22, 6, 51, 23], [19, 46, 16, 6, 47, 17], // 22 [2, 139, 111, 7, 140, 112], [17, 74, 46], [7, 54, 24, 16, 55, 25], [34, 37, 13], // 23 [4, 151, 121, 5, 152, 122], [4, 75, 47, 14, 76, 48], [11, 54, 24, 14, 55, 25], [16, 45, 15, 14, 46, 16], // 24 [6, 147, 117, 4, 148, 118], [6, 73, 45, 14, 74, 46], [11, 54, 24, 16, 55, 25], [30, 46, 16, 2, 47, 17], // 25 [8, 132, 106, 4, 133, 107], [8, 75, 47, 13, 76, 48], [7, 54, 24, 22, 55, 25], [22, 45, 15, 13, 46, 16], // 26 [10, 142, 114, 2, 143, 115], [19, 74, 46, 4, 75, 47], [28, 50, 22, 6, 51, 23], [33, 46, 16, 4, 47, 17], // 27 [8, 152, 122, 4, 153, 123], [22, 73, 45, 3, 74, 46], [8, 53, 23, 26, 54, 24], [12, 45, 15, 28, 46, 16], // 28 [3, 147, 117, 10, 148, 118], [3, 73, 45, 23, 74, 46], [4, 54, 24, 31, 55, 25], [11, 45, 15, 31, 46, 16], // 29 [7, 146, 116, 7, 147, 117], [21, 73, 45, 7, 74, 46], [1, 53, 23, 37, 54, 24], [19, 45, 15, 26, 46, 16], // 30 [5, 145, 115, 10, 146, 116], [19, 75, 47, 10, 76, 48], [15, 54, 24, 25, 55, 25], [23, 45, 15, 25, 46, 16], // 31 [13, 145, 115, 3, 146, 116], [2, 74, 46, 29, 75, 47], [42, 54, 24, 1, 55, 25], [23, 45, 15, 28, 46, 16], // 32 [17, 145, 115], [10, 74, 46, 23, 75, 47], [10, 54, 24, 35, 55, 25], [19, 45, 15, 35, 46, 16], // 33 [17, 145, 115, 1, 146, 116], [14, 74, 46, 21, 75, 47], [29, 54, 24, 19, 55, 25], [11, 45, 15, 46, 46, 16], // 34 [13, 145, 115, 6, 146, 116], [14, 74, 46, 23, 75, 47], [44, 54, 24, 7, 55, 25], [59, 46, 16, 1, 47, 17], // 35 [12, 151, 121, 7, 152, 122], [12, 75, 47, 26, 76, 48], [39, 54, 24, 14, 55, 25], [22, 45, 15, 41, 46, 16], // 36 [6, 151, 121, 14, 152, 122], [6, 75, 47, 34, 76, 48], [46, 54, 24, 10, 55, 25], [2, 45, 15, 64, 46, 16], // 37 [17, 152, 122, 4, 153, 123], [29, 74, 46, 14, 75, 47], [49, 54, 24, 10, 55, 25], [24, 45, 15, 46, 46, 16], // 38 [4, 152, 122, 18, 153, 123], [13, 74, 46, 32, 75, 47], [48, 54, 24, 14, 55, 25], [42, 45, 15, 32, 46, 16], // 39 [20, 147, 117, 4, 148, 118], [40, 75, 47, 7, 76, 48], [43, 54, 24, 22, 55, 25], [10, 45, 15, 67, 46, 16], // 40 [19, 148, 118, 6, 149, 119], [18, 75, 47, 31, 76, 48], [34, 54, 24, 34, 55, 25], [20, 45, 15, 61, 46, 16] ]; var qrRSBlock = function(totalCount, dataCount) { var _this = {}; _this.totalCount = totalCount; _this.dataCount = dataCount; return _this; }; var _this = {}; var getRsBlockTable = function(typeNumber, errorCorrectionLevel) { switch(errorCorrectionLevel) { case QRErrorCorrectionLevel.L : return RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 0]; case QRErrorCorrectionLevel.M : return RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 1]; case QRErrorCorrectionLevel.Q : return RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 2]; case QRErrorCorrectionLevel.H : return RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 3]; default : return undefined; } }; _this.getRSBlocks = function(typeNumber, errorCorrectionLevel) { var rsBlock = getRsBlockTable(typeNumber, errorCorrectionLevel); if (typeof rsBlock == 'undefined') { throw new Error('bad rs block @ typeNumber:' + typeNumber + '/errorCorrectionLevel:' + errorCorrectionLevel); } var length = rsBlock.length / 3; var list = new Array(); for (var i = 0; i < length; i += 1) { var count = rsBlock[i * 3 + 0]; var totalCount = rsBlock[i * 3 + 1]; var dataCount = rsBlock[i * 3 + 2]; for (var j = 0; j < count; j += 1) { list.push(qrRSBlock(totalCount, dataCount) ); } } return list; }; return _this; }(); //--------------------------------------------------------------------- // qrBitBuffer //--------------------------------------------------------------------- var qrBitBuffer = function() { var _buffer = new Array(); var _length = 0; var _this = {}; _this.getBuffer = function() { return _buffer; }; _this.getAt = function(index) { var bufIndex = Math.floor(index / 8); return ( (_buffer[bufIndex] >>> (7 - index % 8) ) & 1) == 1; }; _this.put = function(num, length) { for (var i = 0; i < length; i += 1) { _this.putBit( ( (num >>> (length - i - 1) ) & 1) == 1); } }; _this.getLengthInBits = function() { return _length; }; _this.putBit = function(bit) { var bufIndex = Math.floor(_length / 8); if (_buffer.length <= bufIndex) { _buffer.push(0); } if (bit) { _buffer[bufIndex] |= (0x80 >>> (_length % 8) ); } _length += 1; }; return _this; }; //--------------------------------------------------------------------- // qr8BitByte //--------------------------------------------------------------------- var qr8BitByte = function(data) { var _mode = QRMode.MODE_8BIT_BYTE; var _data = data; var _bytes = qrcode.stringToBytes(data); var _this = {}; _this.getMode = function() { return _mode; }; _this.getLength = function(buffer) { return _bytes.length; }; _this.write = function(buffer) { for (var i = 0; i < _bytes.length; i += 1) { buffer.put(_bytes[i], 8); } }; return _this; }; //===================================================================== // GIF Support etc. // //--------------------------------------------------------------------- // byteArrayOutputStream //--------------------------------------------------------------------- var byteArrayOutputStream = function() { var _bytes = new Array(); var _this = {}; _this.writeByte = function(b) { _bytes.push(b & 0xff); }; _this.writeShort = function(i) { _this.writeByte(i); _this.writeByte(i >>> 8); }; _this.writeBytes = function(b, off, len) { off = off || 0; len = len || b.length; for (var i = 0; i < len; i += 1) { _this.writeByte(b[i + off]); } }; _this.writeString = function(s) { for (var i = 0; i < s.length; i += 1) { _this.writeByte(s.charCodeAt(i) ); } }; _this.toByteArray = function() { return _bytes; }; _this.toString = function() { var s = ''; s += '['; for (var i = 0; i < _bytes.length; i += 1) { if (i > 0) { s += ','; } s += _bytes[i]; } s += ']'; return s; }; return _this; }; //--------------------------------------------------------------------- // base64EncodeOutputStream //--------------------------------------------------------------------- var base64EncodeOutputStream = function() { var _buffer = 0; var _buflen = 0; var _length = 0; var _base64 = ''; var _this = {}; var writeEncoded = function(b) { _base64 += String.fromCharCode(encode(b & 0x3f) ); }; var encode = function(n) { if (n < 0) { // error. } else if (n < 26) { return 0x41 + n; } else if (n < 52) { return 0x61 + (n - 26); } else if (n < 62) { return 0x30 + (n - 52); } else if (n == 62) { return 0x2b; } else if (n == 63) { return 0x2f; } throw new Error('n:' + n); }; _this.writeByte = function(n) { _buffer = (_buffer << 8) | (n & 0xff); _buflen += 8; _length += 1; while (_buflen >= 6) { writeEncoded(_buffer >>> (_buflen - 6) ); _buflen -= 6; } }; _this.flush = function() { if (_buflen > 0) { writeEncoded(_buffer << (6 - _buflen) ); _buffer = 0; _buflen = 0; } if (_length % 3 != 0) { // padding var padlen = 3 - _length % 3; for (var i = 0; i < padlen; i += 1) { _base64 += '='; } } }; _this.toString = function() { return _base64; }; return _this; }; //--------------------------------------------------------------------- // base64DecodeInputStream //--------------------------------------------------------------------- var base64DecodeInputStream = function(str) { var _str = str; var _pos = 0; var _buffer = 0; var _buflen = 0; var _this = {}; _this.read = function() { while (_buflen < 8) { if (_pos >= _str.length) { if (_buflen == 0) { return -1; } throw new Error('unexpected end of file./' + _buflen); } var c = _str.charAt(_pos); _pos += 1; if (c == '=') { _buflen = 0; return -1; } else if (c.match(/^\s$/) ) { // ignore if whitespace. continue; } _buffer = (_buffer << 6) | decode(c.charCodeAt(0) ); _buflen += 6; } var n = (_buffer >>> (_buflen - 8) ) & 0xff; _buflen -= 8; return n; }; var decode = function(c) { if (0x41 <= c && c <= 0x5a) { return c - 0x41; } else if (0x61 <= c && c <= 0x7a) { return c - 0x61 + 26; } else if (0x30 <= c && c <= 0x39) { return c - 0x30 + 52; } else if (c == 0x2b) { return 62; } else if (c == 0x2f) { return 63; } else { throw new Error('c:' + c); } }; return _this; }; //--------------------------------------------------------------------- // gifImage (B/W) //--------------------------------------------------------------------- var gifImage = function(width, height) { var _width = width; var _height = height; var _data = new Array(width * height); var _this = {}; _this.setPixel = function(x, y, pixel) { _data[y * _width + x] = pixel; }; _this.write = function(out) { //--------------------------------- // GIF Signature out.writeString('GIF87a'); //--------------------------------- // Screen Descriptor out.writeShort(_width); out.writeShort(_height); out.writeByte(0x80); // 2bit out.writeByte(0); out.writeByte(0); //--------------------------------- // Global Color Map // black out.writeByte(0x00); out.writeByte(0x00); out.writeByte(0x00); // white out.writeByte(0xff); out.writeByte(0xff); out.writeByte(0xff); //--------------------------------- // Image Descriptor out.writeString(','); out.writeShort(0); out.writeShort(0); out.writeShort(_width); out.writeShort(_height); out.writeByte(0); //--------------------------------- // Local Color Map //--------------------------------- // Raster Data var lzwMinCodeSize = 2; var raster = getLZWRaster(lzwMinCodeSize); out.writeByte(lzwMinCodeSize); var offset = 0; while (raster.length - offset > 255) { out.writeByte(255); out.writeBytes(raster, offset, 255); offset += 255; } out.writeByte(raster.length - offset); out.writeBytes(raster, offset, raster.length - offset); out.writeByte(0x00); //--------------------------------- // GIF Terminator out.writeString(';'); }; var bitOutputStream = function(out) { var _out = out; var _bitLength = 0; var _bitBuffer = 0; var _this = {}; _this.write = function(data, length) { if ( (data >>> length) != 0) { throw new Error('length over'); } while (_bitLength + length >= 8) { _out.writeByte(0xff & ( (data << _bitLength) | _bitBuffer) ); length -= (8 - _bitLength); data >>>= (8 - _bitLength); _bitBuffer = 0; _bitLength = 0; } _bitBuffer = (data << _bitLength) | _bitBuffer; _bitLength = _bitLength + length; }; _this.flush = function() { if (_bitLength > 0) { _out.writeByte(_bitBuffer); } }; return _this; }; var getLZWRaster = function(lzwMinCodeSize) { var clearCode = 1 << lzwMinCodeSize; var endCode = (1 << lzwMinCodeSize) + 1; var bitLength = lzwMinCodeSize + 1; // Setup LZWTable var table = lzwTable(); for (var i = 0; i < clearCode; i += 1) { table.add(String.fromCharCode(i) ); } table.add(String.fromCharCode(clearCode) ); table.add(String.fromCharCode(endCode) ); var byteOut = byteArrayOutputStream(); var bitOut = bitOutputStream(byteOut); // clear code bitOut.write(clearCode, bitLength); var dataIndex = 0; var s = String.fromCharCode(_data[dataIndex]); dataIndex += 1; while (dataIndex < _data.length) { var c = String.fromCharCode(_data[dataIndex]); dataIndex += 1; if (table.contains(s + c) ) { s = s + c; } else { bitOut.write(table.indexOf(s), bitLength); if (table.size() < 0xfff) { if (table.size() == (1 << bitLength) ) { bitLength += 1; } table.add(s + c); } s = c; } } bitOut.write(table.indexOf(s), bitLength); // end code bitOut.write(endCode, bitLength); bitOut.flush(); return byteOut.toByteArray(); }; var lzwTable = function() { var _map = {}; var _size = 0; var _this = {}; _this.add = function(key) { if (_this.contains(key) ) { throw new Error('dup key:' + key); } _map[key] = _size; _size += 1; }; _this.size = function() { return _size; }; _this.indexOf = function(key) { return _map[key]; }; _this.contains = function(key) { return typeof _map[key] != 'undefined'; }; return _this; }; return _this; }; var createImgTag = function(width, height, getPixel, alt) { var gif = gifImage(width, height); for (var y = 0; y < height; y += 1) { for (var x = 0; x < width; x += 1) { gif.setPixel(x, y, getPixel(x, y) ); } } var b = byteArrayOutputStream(); gif.write(b); var base64 = base64EncodeOutputStream(); var bytes = b.toByteArray(); for (var i = 0; i < bytes.length; i += 1) { base64.writeByte(bytes[i]); } base64.flush(); var img = ''; img += ' -1) { return type.split("-")[1]; } return null; } bepasty-1.0.0/src/bepasty/static/app/js/fileuploader.js0000664000175000017500000001205013347732033023074 0ustar useruser00000000000000jqXHR = {}; $(function () { 'use strict'; // Generate human readable file size function humansize (size) { var suffix = ["B", "KiB", "MiB", "GiB", "TiB", "PiB"], tier = 0; while (size >= 1024) { size = size / 1024; tier++; } return Math.round(size * 10) / 10 + " " + suffix[tier]; } $('#fileupload') .fileupload({ dataType: 'json', autoUpload: true, singleFileUploads: true, maxChunkSize: MAX_BODY_SIZE, maxFileSize: MAX_ALLOWED_FILE_SIZE }) .on('fileuploadadd', function (e, data) { }) .on('fileuploadsubmit', function (e, data) { var $this = $(this); var file = data.files[0] // Create new item $.ajax({ type: 'POST', url: UPLOAD_NEW_URL, data: JSON.stringify({ filename: file.name, size: file.size, type: file.type, maxlife_unit: $("select[name=maxlife-unit] option:selected").val(), maxlife_value: $("input[name=maxlife-value]").val() }), contentType: 'application/json', success: function (result) { data.url = result.url; data.context = $('
') .appendTo('#files'); var abortButton = $('