@ -37,9 +37,9 @@ Calibre-Web is a web app providing a clean interface for browsing, reading and d
- "Magic Link" login to make it easy to log on eReaders
- Login via LDAP, google/github oauth and via proxy authentication
## Quick start
## Installation
#### Install via pip
#### Installation via pip (recommended)
1. Install calibre web via pip with the command `pip install calibreweb` (Depending on your OS and or distro the command could also be `pip3`).
2. Optional features can also be installed via pip, please refer to [this page](https://github.com/janeczku/calibre-web/wiki/Dependencies-in-Calibre-Web-Linux-Windows) for details
3. Calibre-Web can be started afterwards by typing `cps` or `python3 -m cps`
@ -47,18 +47,21 @@ Calibre-Web is a web app providing a clean interface for browsing, reading and d
#### Manual installation
1. Install dependencies by running `pip3 install --target vendor -r requirements.txt` (python3.x). Alternativly set up a python virtual environment.
2. Execute the command: `python3 cps.py` (or `nohup python3 cps.py` - recommended if you want to exit the terminal window)
Issues with Ubuntu:
Please note that running the above install command can fail on some versions of Ubuntu, saying `"can't combine user with prefix"`. This is a [known bug](https://github.com/pypa/pip/issues/3826) and can be remedied by using the command `pip install --system --target vendor -r requirements.txt` instead.
## Quick start
Point your browser to `http://localhost:8083` or `http://localhost:8083/opds` for the OPDS catalog
Set `Location of Calibre database` to the path of the folder where your Calibre library (metadata.db) lives, push "submit" button\
Optionally a Google Drive can be used to host the calibre library [-> Using Google Drive integration](https://github.com/janeczku/calibre-web/wiki/Configuration#using-google-drive-integration)
Go to Login page
**Default admin login:**\
#### Default admin login:
*Username:* admin\
*Password:* admin123
**Issues with Ubuntu:**
Please note that running the above install command can fail on some versions of Ubuntu, saying `"can't combine user with prefix"`. This is a [known bug](https://github.com/pypa/pip/issues/3826) and can be remedied by using the command `pip install --system --target vendor -r requirements.txt` instead.
## Requirements
@ -72,14 +75,7 @@ Optionally, to enable on-the-fly conversion from one ebook format to another whe
## Docker Images
Pre-built Docker images are available in these Docker Hub repositories:
Please report security issues to ozzie.fernandez.isaacs@googlemail.com
## Supported Versions
To receive fixes for security vulnerabilities it is required to always upgrade to the latest version of Calibre-Web. See https://github.com/janeczku/calibre-web/releases/latest for the latest release.
## History
| Fixed in | Description |CVE number |
| ---------- |---------|---------|
| 3rd July 2018 | Guest access acts as a backdoor||
| V 0.6.7 |Hardcoded secret key for sessions |CVE-2020-12627 |
| V 0.6.13|Calibre-Web Metadata cross site scripting |CVE-2021-25964|
| V 0.6.13|Name of Shelves are only visible to users who can access the corresponding shelf Thanks to @ibarrionuevo||
| V 0.6.13|JavaScript could get executed in the description field. Thanks to @ranjit-git and Hagai Wechsler (WhiteSource)||
| V 0.6.13|JavaScript could get executed in a custom column of type "comment" field ||
| V 0.6.13|JavaScript could get executed after converting a book to another format with a title containing javascript code||
| V 0.6.13|JavaScript could get executed after converting a book to another format with a username containing javascript code||
| V 0.6.13|JavaScript could get executed in the description series, categories or publishers title||
| V 0.6.13|JavaScript could get executed in the shelf title||
| V 0.6.13|Login with the old session cookie after logout. Thanks to @ibarrionuevo||
| V 0.6.14|CSRF was possible. Thanks to @mik317 and Hagai Wechsler (WhiteSource) ||
| V 0.6.14|Cross-Site Scripting vulnerability on typeahead inputs. Thanks to @notdodo||
<aid="new"data-toggle="tooltip"title="{{_('Sort according to book date, newest first')}}"class="btn btn-primary" href="{{url_for('web.books_list', data='author', book_id=id, sort_param='new')}}"><spanclass="glyphicon glyphicon-book"></span><spanclass="glyphicon glyphicon-calendar"></span><spanclass="glyphicon glyphicon-sort-by-order"></span></a>
<aid="old"data-toggle="tooltip"title="{{_('Sort according to book date, oldest first')}}"class="btn btn-primary" href="{{url_for('web.books_list', data='author', book_id=id, sort_param='old')}}"><spanclass="glyphicon glyphicon-book"></span><spanclass="glyphicon glyphicon-calendar"></span><spanclass="glyphicon glyphicon-sort-by-order-alt"></span></a>
<aid="asc"data-toggle="tooltip"title="{{_('Sort title in alphabetical order')}}"class="btn btn-primary" href="{{url_for('web.books_list', data='author', book_id=id, sort_param='abc')}}"><spanclass="glyphicon glyphicon-font"></span><spanclass="glyphicon glyphicon-sort-by-alphabet"></span></a>
<aid="desc"data-toggle="tooltip"title="{{_('Sort title in reverse alphabetical order')}}"class="btn btn-primary" href="{{url_for('web.books_list', data='author', book_id=id, sort_param='zyx')}}"><spanclass="glyphicon glyphicon-font"></span><spanclass="glyphicon glyphicon-sort-by-alphabet-alt"></span></a>
<aid="pub_new"data-toggle="tooltip"title="{{_('Sort according to publishing date, newest first')}}"class="btn btn-primary" href="{{url_for('web.books_list', data='author', book_id=id, sort_param='pubnew')}}"><spanclass="glyphicon glyphicon-calendar"></span><spanclass="glyphicon glyphicon-sort-by-order"></span></a>
<aid="pub_old"data-toggle="tooltip"title="{{_('Sort according to publishing date, oldest first')}}"class="btn btn-primary" href="{{url_for('web.books_list', data='author', book_id=id, sort_param='pubold')}}"><spanclass="glyphicon glyphicon-calendar"></span><spanclass="glyphicon glyphicon-sort-by-order-alt"></span></a>
<divclass="filterheader hidden-xs">
<aid="new"data-toggle="tooltip"title="{{_('Sort according to book date, newest first')}}"class="btn btn-primary{% if order == "new"%}active{%endif%}" href="{{url_for('web.books_list', data='author', book_id=id, sort_param='new')}}"><spanclass="glyphicon glyphicon-book"></span><spanclass="glyphicon glyphicon-calendar"></span><spanclass="glyphicon glyphicon-sort-by-order"></span></a>
<aid="old"data-toggle="tooltip"title="{{_('Sort according to book date, oldest first')}}"class="btn btn-primary{% if order == "old"%}active{%endif%}" href="{{url_for('web.books_list', data='author', book_id=id, sort_param='old')}}"><spanclass="glyphicon glyphicon-book"></span><spanclass="glyphicon glyphicon-calendar"></span><spanclass="glyphicon glyphicon-sort-by-order-alt"></span></a>
<aid="asc"data-toggle="tooltip"title="{{_('Sort title in alphabetical order')}}"class="btn btn-primary{% if order == "abc"%}active{%endif%}" href="{{url_for('web.books_list', data='author', book_id=id, sort_param='abc')}}"><spanclass="glyphicon glyphicon-font"></span><spanclass="glyphicon glyphicon-sort-by-alphabet"></span></a>
<aid="desc"data-toggle="tooltip"title="{{_('Sort title in reverse alphabetical order')}}"class="btn btn-primary{% if order == "zyx"%}active{%endif%}" href="{{url_for('web.books_list', data='author', book_id=id, sort_param='zyx')}}"><spanclass="glyphicon glyphicon-font"></span><spanclass="glyphicon glyphicon-sort-by-alphabet-alt"></span></a>
<aid="pub_new"data-toggle="tooltip"title="{{_('Sort according to publishing date, newest first')}}"class="btn btn-primary{% if order == "pubnew"%}active{%endif%}" href="{{url_for('web.books_list', data='author', book_id=id, sort_param='pubnew')}}"><spanclass="glyphicon glyphicon-calendar"></span><spanclass="glyphicon glyphicon-sort-by-order"></span></a>
<aid="pub_old"data-toggle="tooltip"title="{{_('Sort according to publishing date, oldest first')}}"class="btn btn-primary{% if order == "pubold"%}active{%endif%}" href="{{url_for('web.books_list', data='author', book_id=id, sort_param='pubold')}}"><spanclass="glyphicon glyphicon-calendar"></span><spanclass="glyphicon glyphicon-sort-by-order-alt"></span></a>
<divid="asc"data-id="series"data-order="{{ order }}"class="btn btn-primary{% if order == 1 %} active{% endif%}"><spanclass="glyphicon glyphicon-sort-by-alphabet"></span></div>
<divid="desc"data-id="series"class="btn btn-primary{% if order == 0 %} active{% endif%}"><spanclass="glyphicon glyphicon-sort-by-alphabet-alt"></span></div>
<adata-toggle="tooltip"title="{{_('Sort according to book date, newest first')}}"id="new"class="btn btn-primary"href="{{url_for('web.books_list', data=page, book_id=id, sort_param='new')}}"><spanclass="glyphicon glyphicon-book"></span><spanclass="glyphicon glyphicon-calendar"></span><spanclass="glyphicon glyphicon-sort-by-order"></span></a>
<adata-toggle="tooltip"title="{{_('Sort according to book date, oldest first')}}"id="old"class="btn btn-primary"href="{{url_for('web.books_list', data=page, book_id=id, sort_param='old')}}"><spanclass="glyphicon glyphicon-book"></span><spanclass="glyphicon glyphicon-calendar"></span><spanclass="glyphicon glyphicon-sort-by-order-alt"></span></a>
<adata-toggle="tooltip"title="{{_('Sort title in alphabetical order')}}"id="asc"class="btn btn-primary"href="{{url_for('web.books_list', data=page, book_id=id, sort_param='abc')}}"><spanclass="glyphicon glyphicon-font"></span><spanclass="glyphicon glyphicon-sort-by-alphabet"></span></a>
<adata-toggle="tooltip"title="{{_('Sort title in reverse alphabetical order')}}"id="desc"class="btn btn-primary"href="{{url_for('web.books_list', data=page, book_id=id, sort_param='zyx')}}"><spanclass="glyphicon glyphicon-font"></span><spanclass="glyphicon glyphicon-sort-by-alphabet-alt"></span></a>
<adata-toggle="tooltip"title="{{_('Sort according to publishing date, newest first')}}"id="pub_new"class="btn btn-primary"href="{{url_for('web.books_list', data=page, book_id=id, sort_param='pubnew')}}"><spanclass="glyphicon glyphicon-calendar"></span><spanclass="glyphicon glyphicon-sort-by-order"></span></a>
<adata-toggle="tooltip"title="{{_('Sort according to publishing date, oldest first')}}"id="pub_old"class="btn btn-primary"href="{{url_for('web.books_list', data=page, book_id=id, sort_param='pubold')}}"><spanclass="glyphicon glyphicon-calendar"></span><spanclass="glyphicon glyphicon-sort-by-order-alt"></span></a>
<divclass="filterheader hidden-xs">
{% if page == 'hot' %}
<adata-toggle="tooltip"title="{{_('Sort ascending according to download count')}}"id="hot_asc"class="btn btn-primary{% if order == "hotasc"%}active{%endif%}"href="{{url_for('web.books_list', data=page, book_id=id, sort_param='hotasc')}}"><spanclass="glyphicon glyphicon-sort-by-order"></span></a>
<adata-toggle="tooltip"title="{{_('Sort descending according to download count')}}"id="hot_desc"class="btn btn-primary{% if order == "hotdesc"%}active{%endif%}"href="{{url_for('web.books_list', data=page, book_id=id, sort_param='hotdesc')}}"><spanclass="glyphicon glyphicon-sort-by-order-alt"></span></a>
{% else %}
<adata-toggle="tooltip"title="{{_('Sort according to book date, newest first')}}"id="new"class="btn btn-primary{% if order == "new"%}active{%endif%}"href="{{url_for('web.books_list', data=page, book_id=id, sort_param='new')}}"><spanclass="glyphicon glyphicon-book"></span><spanclass="glyphicon glyphicon-calendar"></span><spanclass="glyphicon glyphicon-sort-by-order"></span></a>
<adata-toggle="tooltip"title="{{_('Sort according to book date, oldest first')}}"id="old"class="btn btn-primary{% if order == "old"%}active{%endif%}"href="{{url_for('web.books_list', data=page, book_id=id, sort_param='old')}}"><spanclass="glyphicon glyphicon-book"></span><spanclass="glyphicon glyphicon-calendar"></span><spanclass="glyphicon glyphicon-sort-by-order-alt"></span></a>
<adata-toggle="tooltip"title="{{_('Sort title in alphabetical order')}}"id="asc"class="btn btn-primary{% if order == "abc"%}active{%endif%}"href="{{url_for('web.books_list', data=page, book_id=id, sort_param='abc')}}"><spanclass="glyphicon glyphicon-font"></span><spanclass="glyphicon glyphicon-sort-by-alphabet"></span></a>
<adata-toggle="tooltip"title="{{_('Sort title in reverse alphabetical order')}}"id="desc"class="btn btn-primary{% if order == "zyx"%}active{%endif%}"href="{{url_for('web.books_list', data=page, book_id=id, sort_param='zyx')}}"><spanclass="glyphicon glyphicon-font"></span><spanclass="glyphicon glyphicon-sort-by-alphabet-alt"></span></a>
<adata-toggle="tooltip"title="{{_('Sort authors in alphabetical order')}}"id="auth_az"class="btn btn-primary{% if order == "authaz"%}active{%endif%}"href="{{url_for('web.books_list', data=page, book_id=id, sort_param='authaz')}}"><spanclass="glyphicon glyphicon-user"></span><spanclass="glyphicon glyphicon-sort-by-alphabet"></span></a>
<adata-toggle="tooltip"title="{{_('Sort authors in reverse alphabetical order')}}"id="auth_za"class="btn btn-primary{% if order == "authza"%}active{%endif%}"href="{{url_for('web.books_list', data=page, book_id=id, sort_param='authza')}}"><spanclass="glyphicon glyphicon-user"></span><spanclass="glyphicon glyphicon-sort-by-alphabet-alt"></span></a>
<adata-toggle="tooltip"title="{{_('Sort according to publishing date, newest first')}}"id="pub_new"class="btn btn-primary{% if order == "pubnew"%}active{%endif%}"href="{{url_for('web.books_list', data=page, book_id=id, sort_param='pubnew')}}"><spanclass="glyphicon glyphicon-calendar"></span><spanclass="glyphicon glyphicon-sort-by-order"></span></a>
<adata-toggle="tooltip"title="{{_('Sort according to publishing date, oldest first')}}"id="pub_old"class="btn btn-primary{% if order == "pubold"%}active{%endif%}"href="{{url_for('web.books_list', data=page, book_id=id, sort_param='pubold')}}"><spanclass="glyphicon glyphicon-calendar"></span><spanclass="glyphicon glyphicon-sort-by-order-alt"></span></a>
{% if page == 'series' %}
<adata-toggle="tooltip"title="{{_('Sort ascending according to series index')}}"id="series_asc"class="btn btn-primary"href="{{url_for('web.books_list', data=page, book_id=id, sort_param='seriesasc')}}"><spanclass="glyphicon glyphicon-sort-by-order"></span></a>
<adata-toggle="tooltip"title="{{_('Sort descending according to series index')}}"id="series_desc"class="btn btn-primary"href="{{url_for('web.books_list', data=page, book_id=id, sort_param='seriesdesc')}}"><spanclass="glyphicon glyphicon-sort-by-order-alt"></span></a>
<adata-toggle="tooltip"title="{{_('Sort ascending according to series index')}}"id="series_asc"class="btn btn-primary{% if order == "seriesasc"%}active{%endif%}" href="{{url_for('web.books_list', data=page, book_id=id, sort_param='seriesasc')}}"><spanclass="glyphicon glyphicon-sort-by-order"></span></a>
<adata-toggle="tooltip"title="{{_('Sort descending according to series index')}}"id="series_desc"class="btn btn-primary{% if order == "seriesdesc"%}active{%endif%}" href="{{url_for('web.books_list', data=page, book_id=id, sort_param='seriesdesc')}}"><spanclass="glyphicon glyphicon-sort-by-order-alt"></span></a>
<divid="sort_name"class="btn btn-primary"><b>B,A <-> A B</b></div>
{% endif %}
<buttonid="asc"data-order="{{ order }}"data-id="{{ data }}"class="btn btn-primary"><spanclass="glyphicon glyphicon-sort-by-alphabet"></span></button>
<buttonid="desc"data-id="{{ data }}"class="btn btn-primary"><spanclass="glyphicon glyphicon-sort-by-alphabet-alt"></span></button>
<divid="asc"data-order="{{ order }}"data-id="{{ data }}"class="btn btn-primary {% if order == 1 %} active{% endif%}"><spanclass="glyphicon glyphicon-sort-by-alphabet"></span></div>
<divid="desc"data-id="{{ data }}"class="btn btn-primary{% if order == 0 %} active{% endif%}"><spanclass="glyphicon glyphicon-sort-by-alphabet-alt"></span></div>
<divclass="filterheader hidden-xs hidden-sm"><!-- ToDo: Implement filter for search results -->
<aid="new"data-toggle="tooltip"title="{{_('Sort according to book date, newest first')}}"class="btn btn-primary" href="{{url_for('web.books_list', data=page, sort_param='new', query=query)}}"><spanclass="glyphicon glyphicon-sort-by-order"></span></a>
<aid="old"data-toggle="tooltip"title="{{_('Sort according to book date, oldest first')}}"class="btn btn-primary" href="{{url_for('web.books_list', data=page, sort_param='old', query=query)}}"><spanclass="glyphicon glyphicon-sort-by-order-alt"></span></a>
<aid="asc"data-toggle="tooltip"title="{{_('Sort title in alphabetical order')}}"class="btn btn-primary" href="{{url_for('web.books_list', data=page, sort_param='abc', query=query)}}"><spanclass="glyphicon glyphicon-font"></span><spanclass="glyphicon glyphicon-sort-by-alphabet"></span></a>
<aid="desc"data-toggle="tooltip"title="{{_('Sort title in reverse alphabetical order')}}"class="btn btn-primary" href="{{url_for('web.books_list', data=page, sort_param='zyx', query=query)}}"><spanclass="glyphicon glyphicon-font"></span><spanclass="glyphicon glyphicon-sort-by-alphabet-alt"></span></a>
<aid="pub_new"data-toggle="tooltip"title="{{_('Sort according to publishing date, newest first')}}"class="btn btn-primary" href="{{url_for('web.books_list', data=page, sort_param='pubnew', query=query)}}"><spanclass="glyphicon glyphicon-calendar"></span><spanclass="glyphicon glyphicon-sort-by-order"></span></a>
<aid="pub_old"data-toggle="tooltip"title="{{_('Sort according to publishing date, oldest first')}}"class="btn btn-primary" href="{{url_for('web.books_list', data=page, sort_param='pubold', query=query)}}"><spanclass="glyphicon glyphicon-calendar"></span><spanclass="glyphicon glyphicon-sort-by-order-alt"></span></a>
<divclass="filterheader hidden-xs"><!-- ToDo: Implement filter for search results -->
<aid="new"data-toggle="tooltip"title="{{_('Sort according to book date, newest first')}}"class="btn btn-primary{% if order == "new"%}active{%endif%}" href="{{url_for('web.books_list', data=page, sort_param='new', query=query)}}"><spanclass="glyphicon glyphicon-sort-by-order"></span></a>
<aid="old"data-toggle="tooltip"title="{{_('Sort according to book date, oldest first')}}"class="btn btn-primary{% if order == "old"%}active{%endif%}" href="{{url_for('web.books_list', data=page, sort_param='old', query=query)}}"><spanclass="glyphicon glyphicon-sort-by-order-alt"></span></a>
<aid="asc"data-toggle="tooltip"title="{{_('Sort title in alphabetical order')}}"class="btn btn-primary{% if order == "abc"%}active{%endif%}" href="{{url_for('web.books_list', data=page, sort_param='abc', query=query)}}"><spanclass="glyphicon glyphicon-font"></span><spanclass="glyphicon glyphicon-sort-by-alphabet"></span></a>
<aid="desc"data-toggle="tooltip"title="{{_('Sort title in reverse alphabetical order')}}"class="btn btn-primary{% if order == "zyx"%}active{%endif%}" href="{{url_for('web.books_list', data=page, sort_param='zyx', query=query)}}"><spanclass="glyphicon glyphicon-font"></span><spanclass="glyphicon glyphicon-sort-by-alphabet-alt"></span></a>
<aid="auth_az"data-toggle="tooltip"title="{{_('Sort authors in alphabetical order')}}"class="btn btn-primary{% if order == "authaz"%}active{%endif%}" href="{{url_for('web.books_list', data=page, sort_param='authaz', query=query)}}"><spanclass="glyphicon glyphicon-user"></span><spanclass="glyphicon glyphicon-sort-by-alphabet"></span></a>
<aid="auth_za"data-toggle="tooltip"title="{{_('Sort authors in reverse alphabetical order')}}"class="btn btn-primary{% if order == "authza"%}active{%endif%}" href="{{url_for('web.books_list', data=page, sort_param='authza', query=query)}}"><spanclass="glyphicon glyphicon-user"></span><spanclass="glyphicon glyphicon-sort-by-alphabet-alt"></span></a>
<aid="pub_new"data-toggle="tooltip"title="{{_('Sort according to publishing date, newest first')}}"class="btn btn-primary{% if order == "pubnew"%}active{%endif%}" href="{{url_for('web.books_list', data=page, sort_param='pubnew', query=query)}}"><spanclass="glyphicon glyphicon-calendar"></span><spanclass="glyphicon glyphicon-sort-by-order"></span></a>
<aid="pub_old"data-toggle="tooltip"title="{{_('Sort according to publishing date, oldest first')}}"class="btn btn-primary{% if order == "pubold"%}active{%endif%}" href="{{url_for('web.books_list', data=page, sort_param='pubold', query=query)}}"><spanclass="glyphicon glyphicon-calendar"></span><spanclass="glyphicon glyphicon-sort-by-order-alt"></span></a>
<adata-toggle="tooltip"title="{{_('Sort according to book date, newest first')}}"id="new"class="btn btn-primary disabled"href="{{url_for('shelf.show_shelf', shelf_id=shelf.id, sort_param='new')}}"><spanclass="glyphicon glyphicon-book"></span><spanclass="glyphicon glyphicon-calendar"></span><spanclass="glyphicon glyphicon-sort-by-order"></span></a>
<adata-toggle="tooltip"title="{{_('Sort according to book date, oldest first')}}"id="old"class="btn btn-primary disabled"href="{{url_for('shelf.show_shelf', shelf_id=shelf.id, sort_param='old')}}"><spanclass="glyphicon glyphicon-book"></span><spanclass="glyphicon glyphicon-calendar"></span><spanclass="glyphicon glyphicon-sort-by-order-alt"></span></a>
<adata-toggle="tooltip"title="{{_('Sort title in alphabetical order')}}"id="asc"class="btn btn-primary disabled"href="{{url_for('shelf.show_shelf', shelf_id=shelf.id, sort_param='abc')}}"><spanclass="glyphicon glyphicon-font"></span><spanclass="glyphicon glyphicon-sort-by-alphabet"></span></a>