diff --git a/SECURITY.md b/SECURITY.md index a7113785..bbaad7c4 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -27,3 +27,6 @@ To receive fixes for security vulnerabilities it is required to always upgrade t | V 0.6.14|Cross-Site Scripting vulnerability on typeahead inputs. Thanks to @notdodo|| +## Staement regarding Log4j (CVE-2021-44228 and related) + +Calibre-web is not affected by bugs related to Log4j. Calibre-Web is a python program, therefore not using Java, and not using the Java logging feature log4j. diff --git a/cps/admin.py b/cps/admin.py index 04d9138f..1c228e49 100644 --- a/cps/admin.py +++ b/cps/admin.py @@ -756,7 +756,12 @@ def prepare_tags(user, action, tags_name, id_list): return ",".join(saved_tags_list) -@admi.route("/ajax/addrestriction/", defaults={"user_id": 0}, methods=['POST']) +@admi.route("/ajax/addrestriction/", methods=['POST']) +@login_required +@admin_required +def add_user_0_restriction(res_type): + return add_restriction(res_type, 0) + @admi.route("/ajax/addrestriction//", methods=['POST']) @login_required @admin_required @@ -803,7 +808,13 @@ def add_restriction(res_type, user_id): return "" -@admi.route("/ajax/deleterestriction/", defaults={"user_id": 0}, methods=['POST']) +@admi.route("/ajax/deleterestriction/", methods=['POST']) +@login_required +@admin_required +def delete_user_0_restriction(res_type): + return delete_restriction(res_type, 0) + + @admi.route("/ajax/deleterestriction//", methods=['POST']) @login_required @admin_required diff --git a/cps/db.py b/cps/db.py index 4b0a7ac7..9c5b5657 100644 --- a/cps/db.py +++ b/cps/db.py @@ -796,23 +796,24 @@ class CalibreDB(): def speaking_language(self, languages=None, return_all_languages=False, with_count=False, reverse_order=False): from . import get_locale - if not languages: - if with_count: + if with_count: + if not languages: languages = self.session.query(Languages, func.count('books_languages_link.book'))\ .join(books_languages_link).join(Books)\ .filter(self.common_filters(return_all_languages=return_all_languages)) \ .group_by(text('books_languages_link.lang_code')).all() - for lang in languages: - lang[0].name = isoLanguages.get_language_name(get_locale(), lang[0].lang_code) - return sorted(languages, key=lambda x: x[0].name, reverse=reverse_order) - else: + for lang in languages: + lang[0].name = isoLanguages.get_language_name(get_locale(), lang[0].lang_code) + return sorted(languages, key=lambda x: x[0].name, reverse=reverse_order) + else: + if not languages: languages = self.session.query(Languages) \ .join(books_languages_link) \ .join(Books) \ .filter(self.common_filters(return_all_languages=return_all_languages)) \ .group_by(text('books_languages_link.lang_code')).all() - for lang in languages: - lang.name = isoLanguages.get_language_name(get_locale(), lang.lang_code) + for lang in languages: + lang.name = isoLanguages.get_language_name(get_locale(), lang.lang_code) return sorted(languages, key=lambda x: x.name, reverse=reverse_order) diff --git a/cps/gdrive.py b/cps/gdrive.py index 6ca73ca9..e782cb9e 100644 --- a/cps/gdrive.py +++ b/cps/gdrive.py @@ -109,7 +109,7 @@ def revoke_watch_gdrive(): try: gdriveutils.stopChannel(gdriveutils.Gdrive.Instance().drive, last_watch_response['id'], last_watch_response['resourceId']) - except HttpError: + except (HttpError, AttributeError): pass config.config_google_drive_watch_changes_response = {} config.save() diff --git a/cps/gdriveutils.py b/cps/gdriveutils.py index 878c1f9f..c4445944 100644 --- a/cps/gdriveutils.py +++ b/cps/gdriveutils.py @@ -56,11 +56,13 @@ try: from pydrive2.auth import GoogleAuth from pydrive2.drive import GoogleDrive from pydrive2.auth import RefreshError + from pydrive2.files import ApiRequestError except ImportError as err: try: from pydrive.auth import GoogleAuth from pydrive.drive import GoogleDrive from pydrive.auth import RefreshError + from pydrive.files import ApiRequestError except ImportError as err: importError = err gdrive_support = False @@ -322,6 +324,11 @@ def getFolderId(path, drive): log.error("gdrive.db DB is not Writeable") log.debug('Database error: %s', ex) session.rollback() + except ApiRequestError as ex: + log.error('{} {}'.format(ex.error['message'], path)) + session.rollback() + except RefreshError as ex: + log.error(ex) return currentFolderId diff --git a/cps/helper.py b/cps/helper.py index 2c2c3cad..b8499a12 100644 --- a/cps/helper.py +++ b/cps/helper.py @@ -233,7 +233,7 @@ def get_valid_filename(value, replace_whitespace=True): value = value[:-1]+u'_' value = value.replace("/", "_").replace(":", "_").strip('\0') if use_unidecode: - if not config.config_unicode_filename: + if config.config_unicode_filename: value = (unidecode.unidecode(value)) else: value = value.replace(u'§', u'SS') @@ -673,9 +673,9 @@ def save_cover(img, book_path): def do_download_file(book, book_format, client, data, headers): if config.config_use_google_drive: - startTime = time.time() + #startTime = time.time() df = gd.getFileFromEbooksFolder(book.path, data.name + "." + book_format) - log.debug('%s', time.time() - startTime) + #log.debug('%s', time.time() - startTime) if df: return gd.do_gdrive_download(df, headers) else: diff --git a/cps/iso_language_names.py b/cps/iso_language_names.py index 697b7e22..11e5e67b 100644 --- a/cps/iso_language_names.py +++ b/cps/iso_language_names.py @@ -51,7 +51,6 @@ LANGUAGE_NAMES = { "bel": "běloruština", "bem": "bemba (Zambie)", "ben": "bengálština", - "berinomo": "bit", "bho": "bhódžpurština", "bik": "bikolština", "bin": "bini", @@ -474,7 +473,6 @@ LANGUAGE_NAMES = { "bel": "Weißrussisch", "bem": "Bemba (Sambia)", "ben": "Bengalisch", - "berinomo": "Bit", "bho": "Bhojpuri", "bik": "Bikol", "bin": "Bini", @@ -901,7 +899,6 @@ LANGUAGE_NAMES = { "byn": "Bilin", "bin": "Bini", "bis": "Bislama", - "berinomo": "Bit", "zbl": "Blissymbols", "bos": "Βοσνιακά", "bra": "Braj", @@ -1275,7 +1272,6 @@ LANGUAGE_NAMES = { "bel": "Bielorruso", "bem": "Bemba (Zambia)", "ben": "Bengalí", - "berinomo": "Bit", "bho": "Bopurí", "bik": "Bicolano", "bin": "Bini", @@ -1698,7 +1694,6 @@ LANGUAGE_NAMES = { "bel": "valkovenäjä", "bem": "Bemba (Zambia)", "ben": "bengali", - "berinomo": "Bit", "bho": "bhojpuri", "bik": "bikol", "bin": "bini", @@ -2121,7 +2116,6 @@ LANGUAGE_NAMES = { "bel": "biélorusse", "bem": "bemba (Zambie)", "ben": "bengali", - "berinomo": "bit", "bho": "bhojpuri", "bik": "bikol", "bin": "bini", @@ -2544,7 +2538,6 @@ LANGUAGE_NAMES = { "bel": "belarusz", "bem": "Bemba (Zambia)", "ben": "bengáli", - "berinomo": "Bit", "bho": "bhodzspuri", "bik": "bikol", "bin": "bini", @@ -2967,7 +2960,6 @@ LANGUAGE_NAMES = { "bel": "Bielorusso", "bem": "Bemba (Zambia)", "ben": "Bengalese", - "berinomo": "Bit", "bho": "Bhojpuri", "bik": "bicol", "bin": "Bini", @@ -3390,7 +3382,6 @@ LANGUAGE_NAMES = { "bel": "白ロシア語", "bem": "Bemba (Zambia)", "ben": "ベンガル語", - "berinomo": "Bit", "bho": "ボージプリー語", "bik": "ビコル語", "bin": "ビニ語", @@ -3813,7 +3804,6 @@ LANGUAGE_NAMES = { "bel": "Belarusian", "bem": "Bemba (Zambia)", "ben": "Bengali", - "berinomo": "Bit", "bho": "Bhojpuri", "bik": "Bikol", "bin": "Bini", @@ -4236,7 +4226,6 @@ LANGUAGE_NAMES = { "bel": "Wit-Russisch; Belarussisch", "bem": "Bemba (Zambia)", "ben": "Bengaals", - "berinomo": "Bit", "bho": "Bhojpuri", "bik": "Bikol", "bin": "Bini; Edo", @@ -4659,7 +4648,6 @@ LANGUAGE_NAMES = { "bel": "białoruski", "bem": "bemba (Zambia)", "ben": "bengalski", - "berinomo": "Bit", "bho": "bhodźpuri", "bik": "bikol", "bin": "edo", @@ -5086,7 +5074,6 @@ LANGUAGE_NAMES = { "byn": "Bilin", "bin": "Bini", "bis": "Bislama", - "berinomo": "Bit", "zbl": "Blissymbols", "bos": "Bosnian", "bra": "Braj", @@ -5458,7 +5445,6 @@ LANGUAGE_NAMES = { "bel": "Белорусский", "bem": "Бемба (Замбия)", "ben": "Бенгальский", - "berinomo": "Bit", "bho": "Бходжпури", "bik": "Бикольский", "bin": "Бини", @@ -5881,7 +5867,6 @@ LANGUAGE_NAMES = { "bel": "Vitryska", "bem": "Bemba (Zambia)", "ben": "Bengaliska", - "berinomo": "Bit", "bho": "Bhojpuri", "bik": "Bikol", "bin": "Edo (bini)", @@ -6308,7 +6293,6 @@ LANGUAGE_NAMES = { "byn": "Bilin", "bin": "Bini (Afrika)", "bis": "Bislama (Vanuatu; Kuzey Pasifik)", - "berinomo": "Bit", "zbl": "Blis Sembolleri", "bos": "Boşnakça", "bra": "Braj (Hindistan)", @@ -6680,7 +6664,6 @@ LANGUAGE_NAMES = { "bel": "білоруська", "bem": "бемба (Замбія)", "ben": "бенгальська", - "berinomo": "біт", "bho": "бходжпурі", "bik": "бікольська", "bin": "біні", @@ -7103,7 +7086,6 @@ LANGUAGE_NAMES = { "bel": "白俄罗斯语", "bem": "本巴语(赞比亚)", "ben": "孟加拉语", - "berinomo": "布兴话", "bho": "博杰普尔语", "bik": "比科尔语", "bin": "比尼语", @@ -7530,7 +7512,6 @@ LANGUAGE_NAMES = { "byn": "Bilin", "bin": "Bini", "bis": "Bislama", - "berinomo": "Bit", "zbl": "布利斯符號", "bos": "Bosnian", "bra": "Braj", @@ -7906,7 +7887,6 @@ LANGUAGE_NAMES = { "bik": "Bikol", "bin": "Bini", "bis": "Bislama", - "bit": "Berinomo", "bla": "Siksika", "bod": "Tibetan", "bos": "Bosnian", @@ -7941,7 +7921,6 @@ LANGUAGE_NAMES = { "cre": "Cree", "crh": "Turkish; Crimean", "csb": "Kashubian", - "csl": "Chinese Sign Language", "cym": "Welsh", "dak": "Dakota", "dan": "Danish", diff --git a/cps/kobo.py b/cps/kobo.py index c5af44fc..e0395855 100644 --- a/cps/kobo.py +++ b/cps/kobo.py @@ -146,9 +146,16 @@ def HandleSyncRequest(): if not current_app.wsgi_app.is_proxied: log.debug('Kobo: Received unproxied request, changed request port to external server port') - new_books_last_modified = sync_token.books_last_modified - new_books_last_created = sync_token.books_last_created + # if no books synced don't respect sync_token + if not ub.session.query(ub.KoboSyncedBooks).filter(ub.KoboSyncedBooks.user_id == current_user.id).count(): + sync_token.books_last_modified = datetime.datetime.min + sync_token.books_last_created = datetime.datetime.min + sync_token.reading_state_last_modified = datetime.datetime.min + + new_books_last_modified = sync_token.books_last_modified # needed for sync selected shelfs only + new_books_last_created = sync_token.books_last_created # needed to distinguish between new and changed entitlement new_reading_state_last_modified = sync_token.reading_state_last_modified + new_archived_last_modified = datetime.datetime.min sync_results = [] @@ -306,11 +313,12 @@ def HandleSyncRequest(): sync_shelves(sync_token, sync_results, only_kobo_shelves) - sync_token.books_last_created = new_books_last_created + # update last created timestamp to distinguish between new and changed entitlements + if not cont_sync: + sync_token.books_last_created = new_books_last_created sync_token.books_last_modified = new_books_last_modified sync_token.archive_last_modified = new_archived_last_modified sync_token.reading_state_last_modified = new_reading_state_last_modified - # sync_token.books_last_id = books_last_id return generate_sync_response(sync_token, sync_results, cont_sync) diff --git a/cps/static/css/style.css b/cps/static/css/style.css index bf2257e6..6e6b0eae 100644 --- a/cps/static/css/style.css +++ b/cps/static/css/style.css @@ -429,7 +429,7 @@ div.log { } #detailcover { cursor:zoom-in; } -#detailcover:-webkit-full-screen { cursor:zoom-out; } -#detailcover:-moz-full-screen { cursor:zoom-out; } -#detailcover:-ms-fullscreen { cursor:zoom-out; } -#detailcover:fullscreen { cursor:zoom-out; } +#detailcover:-webkit-full-screen { cursor:zoom-out; border: 0; } +#detailcover:-moz-full-screen { cursor:zoom-out; border: 0; } +#detailcover:-ms-fullscreen { cursor:zoom-out; border: 0; } +#detailcover:fullscreen { cursor:zoom-out; border: 0; } diff --git a/cps/static/js/libs/tinymce/langs/zh_Hans_CN.js b/cps/static/js/libs/tinymce/langs/zh_Hans_CN.js index 2a784f57..981c9090 100644 --- a/cps/static/js/libs/tinymce/langs/zh_Hans_CN.js +++ b/cps/static/js/libs/tinymce/langs/zh_Hans_CN.js @@ -1,4 +1,4 @@ -tinymce.addI18n('zh_CN',{ +tinymce.addI18n('zh_Hans_CN',{ "Redo": "\u91cd\u505a", "Undo": "\u64a4\u9500", "Cut": "\u526a\u5207", diff --git a/cps/static/js/libs/tinymce/langs/zh_Hant_TW.js b/cps/static/js/libs/tinymce/langs/zh_Hant_TW.js index 1987486c..f6e7e248 100644 --- a/cps/static/js/libs/tinymce/langs/zh_Hant_TW.js +++ b/cps/static/js/libs/tinymce/langs/zh_Hant_TW.js @@ -1,4 +1,4 @@ -tinymce.addI18n('zh_TW',{ +tinymce.addI18n('zh_Hant_TW',{ "Redo": "\u91cd\u505a", "Undo": "\u64a4\u92b7", "Cut": "\u526a\u4e0b", @@ -416,4 +416,4 @@ tinymce.addI18n('zh_TW',{ "Spellcheck": "\u62fc\u5b57\u6aa2\u67e5", "Caption": "\u8868\u683c\u6a19\u984c", "Insert template": "\u63d2\u5165\u6a23\u7248" -}); \ No newline at end of file +}); diff --git a/cps/static/js/reading/epub.js b/cps/static/js/reading/epub.js index edafa82c..7942bfbd 100644 --- a/cps/static/js/reading/epub.js +++ b/cps/static/js/reading/epub.js @@ -61,11 +61,14 @@ var reader; this.removeBookmark(bookmark); }.bind(this)); } + + var csrftoken = $("input[name='csrf_token']").val(); // Save to database $.ajax(calibre.bookmarkUrl, { method: "post", - data: { bookmark: location || "" } + data: { bookmark: location || "" }, + headers: { "X-CSRFToken": csrftoken } }).fail(function (xhr, status, error) { alert(error); }); diff --git a/cps/tasks/convert.py b/cps/tasks/convert.py index ada53005..59ad6909 100644 --- a/cps/tasks/convert.py +++ b/cps/tasks/convert.py @@ -217,13 +217,16 @@ class TaskConvert(CalibreTask): quotes.append(quotes_index) quotes_index += 1 - p = process_open(command, quotes) + p = process_open(command, quotes, newlines=False) except OSError as e: return 1, _(u"Ebook-converter failed: %(error)s", error=e) while p.poll() is None: nextline = p.stdout.readline() - log.debug(nextline.strip('\r\n')) + if isinstance(nextline, bytes): + nextline = nextline.decode('utf-8', errors="ignore").strip('\r\n') + if nextline: + log.debug(nextline) # parse progress string from calibre-converter progress = re.search(r"(\d+)%\s.*", nextline) if progress: @@ -236,11 +239,15 @@ class TaskConvert(CalibreTask): calibre_traceback = p.stderr.readlines() error_message = "" for ele in calibre_traceback: - log.debug(ele.strip('\n')) + ele = ele.decode('utf-8', errors="ignore").strip('\n') + log.debug(ele) if not ele.startswith('Traceback') and not ele.startswith(' File'): - error_message = _("Calibre failed with error: %(error)s", error=ele.strip('\n')) + error_message = _("Calibre failed with error: %(error)s", error=ele) return check, error_message @property def name(self): return "Convert" + + def __str__(self): + return "Convert {} {}".format(self.bookid, self.kindle_mail) diff --git a/cps/tasks/mail.py b/cps/tasks/mail.py index 2e95ee98..05b2175f 100644 --- a/cps/tasks/mail.py +++ b/cps/tasks/mail.py @@ -267,4 +267,4 @@ class TaskEmail(CalibreTask): return "E-mail" def __str__(self): - return "{}, {}".format(self.name, self.subject) + return "E-mail {}, {}".format(self.name, self.subject) diff --git a/cps/tasks/upload.py b/cps/tasks/upload.py index 6a341cdd..2a667c28 100644 --- a/cps/tasks/upload.py +++ b/cps/tasks/upload.py @@ -32,3 +32,6 @@ class TaskUpload(CalibreTask): @property def name(self): return "Upload" + + def __str__(self): + return "Upload {}".format(self.message) diff --git a/cps/templates/read.html b/cps/templates/read.html index 3d2566e0..1766eb1b 100644 --- a/cps/templates/read.html +++ b/cps/templates/read.html @@ -17,6 +17,7 @@