Commit 8b8fe7a0 authored by Ozzieisaacs's avatar Ozzieisaacs

bugfix on create shelv via kobo sync protocol

bugfix for totally wrong json requests
prevent empty shelf names on kobo create shelf
Removed errorhandler 404
parent e29f17ac
...@@ -45,6 +45,7 @@ from flask_login import current_user, login_required ...@@ -45,6 +45,7 @@ from flask_login import current_user, login_required
from werkzeug.datastructures import Headers from werkzeug.datastructures import Headers
from sqlalchemy import func from sqlalchemy import func
from sqlalchemy.sql.expression import and_, or_ from sqlalchemy.sql.expression import and_, or_
from sqlalchemy.exc import StatementError
import requests import requests
from . import config, logger, kobo_auth, db, helper, shelf as shelf_lib, ub from . import config, logger, kobo_auth, db, helper, shelf as shelf_lib, ub
...@@ -222,10 +223,10 @@ def HandleSyncRequest(): ...@@ -222,10 +223,10 @@ def HandleSyncRequest():
sync_token.archive_last_modified = new_archived_last_modified sync_token.archive_last_modified = new_archived_last_modified
sync_token.reading_state_last_modified = new_reading_state_last_modified sync_token.reading_state_last_modified = new_reading_state_last_modified
return generate_sync_response(request, sync_token, sync_results) return generate_sync_response(sync_token, sync_results)
def generate_sync_response(request, sync_token, sync_results): def generate_sync_response(sync_token, sync_results):
extra_headers = {} extra_headers = {}
if config.config_kobo_proxy: if config.config_kobo_proxy:
# Merge in sync results from the official Kobo store. # Merge in sync results from the official Kobo store.
...@@ -394,26 +395,31 @@ def get_metadata(book): ...@@ -394,26 +395,31 @@ def get_metadata(book):
return metadata return metadata
@kobo.route("/v1/library/tags", methods=["POST"]) @kobo.route("/v1/library/tags", methods=["POST", "DELETE"])
@login_required @login_required
# Creates a Shelf with the given items, and returns the shelf's uuid. # Creates a Shelf with the given items, and returns the shelf's uuid.
def HandleTagCreate(): def HandleTagCreate():
shelf_request = request.json # catch delete requests, otherwise the are handeld in the book delete handler
if request.method == "DELETE":
abort(405)
name, items = None, None name, items = None, None
try: try:
shelf_request = request.json
name = shelf_request["Name"] name = shelf_request["Name"]
items = shelf_request["Items"] items = shelf_request["Items"]
except KeyError: if not name:
raise TypeError
except (KeyError, TypeError):
log.debug("Received malformed v1/library/tags request.") log.debug("Received malformed v1/library/tags request.")
abort(400, description="Malformed tags POST request. Data is missing 'Name' or 'Items' field") abort(400, description="Malformed tags POST request. Data has empty 'Name', missing 'Name' or 'Items' field")
shelf = ub.session.query(ub.Shelf).filter(ub.Shelf.name == name, ub.Shelf.user_id == shelf = ub.session.query(ub.Shelf).filter(ub.Shelf.name == name, ub.Shelf.user_id ==
current_user.id).one_or_none() current_user.id).one_or_none()
if shelf and not shelf_lib.check_shelf_edit_permissions(shelf): if shelf and not shelf_lib.check_shelf_edit_permissions(shelf):
abort(401, description="User is unauthaurized to edit shelf.") abort(401, description="User is unauthaurized to create shelf.")
if not shelf: if not shelf:
shelf = ub.Shelf(user_id=current_user.id, name=name, uuid=uuid.uuid4()) shelf = ub.Shelf(user_id=current_user.id, name=name, uuid=str(uuid.uuid4()))
ub.session.add(shelf) ub.session.add(shelf)
items_unknown_to_calibre = add_items_to_shelf(items, shelf) items_unknown_to_calibre = add_items_to_shelf(items, shelf)
...@@ -441,18 +447,17 @@ def HandleTagUpdate(tag_id): ...@@ -441,18 +447,17 @@ def HandleTagUpdate(tag_id):
if request.method == "DELETE": if request.method == "DELETE":
shelf_lib.delete_shelf_helper(shelf) shelf_lib.delete_shelf_helper(shelf)
else: else:
shelf_request = request.json
name = None name = None
try: try:
shelf_request = request.json
name = shelf_request["Name"] name = shelf_request["Name"]
except KeyError: except (KeyError, TypeError):
log.debug("Received malformed v1/library/tags rename request.") log.debug("Received malformed v1/library/tags rename request.")
abort(400, description="Malformed tags POST request. Data is missing 'Name' field") abort(400, description="Malformed tags POST request. Data is missing 'Name' field")
shelf.name = name shelf.name = name
ub.session.merge(shelf) ub.session.merge(shelf)
ub.session.commit() ub.session.commit()
return make_response(' ', 200) return make_response(' ', 200)
...@@ -461,29 +466,32 @@ def add_items_to_shelf(items, shelf): ...@@ -461,29 +466,32 @@ def add_items_to_shelf(items, shelf):
book_ids_already_in_shelf = set([book_shelf.book_id for book_shelf in shelf.books]) book_ids_already_in_shelf = set([book_shelf.book_id for book_shelf in shelf.books])
items_unknown_to_calibre = [] items_unknown_to_calibre = []
for item in items: for item in items:
if item["Type"] != "ProductRevisionTagItem": try:
items_unknown_to_calibre.append(item) if item["Type"] != "ProductRevisionTagItem":
continue items_unknown_to_calibre.append(item)
continue
book = db.session.query(db.Books).filter(db.Books.uuid == item["RevisionId"]).one_or_none()
if not book: book = db.session.query(db.Books).filter(db.Books.uuid == item["RevisionId"]).one_or_none()
if not book:
items_unknown_to_calibre.append(item)
continue
book_id = book.id
if book_id not in book_ids_already_in_shelf:
shelf.books.append(ub.BookShelf(book_id=book_id))
except KeyError:
items_unknown_to_calibre.append(item) items_unknown_to_calibre.append(item)
continue
book_id = book.id
if book_id not in book_ids_already_in_shelf:
shelf.books.append(ub.BookShelf(book_id=book_id))
return items_unknown_to_calibre return items_unknown_to_calibre
@kobo.route("/v1/library/tags/<tag_id>/items", methods=["POST"]) @kobo.route("/v1/library/tags/<tag_id>/items", methods=["POST"])
@login_required @login_required
def HandleTagAddItem(tag_id): def HandleTagAddItem(tag_id):
tag_request = request.json
items = None items = None
try: try:
tag_request = request.json
items = tag_request["Items"] items = tag_request["Items"]
except KeyError: except (KeyError, TypeError):
log.debug("Received malformed v1/library/tags/<tag_id>/items/delete request.") log.debug("Received malformed v1/library/tags/<tag_id>/items/delete request.")
abort(400, description="Malformed tags POST request. Data is missing 'Items' field") abort(400, description="Malformed tags POST request. Data is missing 'Items' field")
...@@ -509,15 +517,14 @@ def HandleTagAddItem(tag_id): ...@@ -509,15 +517,14 @@ def HandleTagAddItem(tag_id):
@kobo.route("/v1/library/tags/<tag_id>/items/delete", methods=["POST"]) @kobo.route("/v1/library/tags/<tag_id>/items/delete", methods=["POST"])
@login_required @login_required
def HandleTagRemoveItem(tag_id): def HandleTagRemoveItem(tag_id):
tag_request = request.json
items = None items = None
try: try:
tag_request = request.json
items = tag_request["Items"] items = tag_request["Items"]
except KeyError: except (KeyError, TypeError):
log.debug("Received malformed v1/library/tags/<tag_id>/items/delete request.") log.debug("Received malformed v1/library/tags/<tag_id>/items/delete request.")
abort(400, description="Malformed tags POST request. Data is missing 'Items' field") abort(400, description="Malformed tags POST request. Data is missing 'Items' field")
# insconsitent to above requests
shelf = ub.session.query(ub.Shelf).filter(ub.Shelf.uuid == tag_id, shelf = ub.session.query(ub.Shelf).filter(ub.Shelf.uuid == tag_id,
ub.Shelf.user_id == current_user.id).one_or_none() ub.Shelf.user_id == current_user.id).one_or_none()
if not shelf: if not shelf:
...@@ -530,16 +537,19 @@ def HandleTagRemoveItem(tag_id): ...@@ -530,16 +537,19 @@ def HandleTagRemoveItem(tag_id):
items_unknown_to_calibre = [] items_unknown_to_calibre = []
for item in items: for item in items:
if item["Type"] != "ProductRevisionTagItem": try:
items_unknown_to_calibre.append(item) if item["Type"] != "ProductRevisionTagItem":
continue items_unknown_to_calibre.append(item)
continue
book = db.session.query(db.Books).filter(db.Books.uuid == item["RevisionId"]).one_or_none() book = db.session.query(db.Books).filter(db.Books.uuid == item["RevisionId"]).one_or_none()
if not book: if not book:
items_unknown_to_calibre.append(item) items_unknown_to_calibre.append(item)
continue continue
shelf.books.filter(ub.BookShelf.book_id == book.id).delete() shelf.books.filter(ub.BookShelf.book_id == book.id).delete()
except KeyError:
items_unknown_to_calibre.append(item)
ub.session.commit() ub.session.commit()
if items_unknown_to_calibre: if items_unknown_to_calibre:
...@@ -628,39 +638,43 @@ def HandleStateRequest(book_uuid): ...@@ -628,39 +638,43 @@ def HandleStateRequest(book_uuid):
else: else:
update_results_response = {"EntitlementId": book_uuid} update_results_response = {"EntitlementId": book_uuid}
request_data = request.json try:
if "ReadingStates" not in request_data: request_data = request.json
request_reading_state = request_data["ReadingStates"][0]
request_bookmark = request_reading_state["CurrentBookmark"]
if request_bookmark:
current_bookmark = kobo_reading_state.current_bookmark
current_bookmark.progress_percent = request_bookmark["ProgressPercent"]
current_bookmark.content_source_progress_percent = request_bookmark["ContentSourceProgressPercent"]
location = request_bookmark["Location"]
if location:
current_bookmark.location_value = location["Value"]
current_bookmark.location_type = location["Type"]
current_bookmark.location_source = location["Source"]
update_results_response["CurrentBookmarkResult"] = {"Result": "Success"}
request_statistics = request_reading_state["Statistics"]
if request_statistics:
statistics = kobo_reading_state.statistics
statistics.spent_reading_minutes = int(request_statistics["SpentReadingMinutes"])
statistics.remaining_time_minutes = int(request_statistics["RemainingTimeMinutes"])
update_results_response["StatisticsResult"] = {"Result": "Success"}
request_status_info = request_reading_state["StatusInfo"]
if request_status_info:
book_read = kobo_reading_state.book_read_link
new_book_read_status = get_ub_read_status(request_status_info["Status"])
if new_book_read_status == ub.ReadBook.STATUS_IN_PROGRESS \
and new_book_read_status != book_read.read_status:
book_read.times_started_reading += 1
book_read.last_time_started_reading = datetime.datetime.utcnow()
book_read.read_status = new_book_read_status
update_results_response["StatusInfoResult"] = {"Result": "Success"}
except (KeyError, TypeError, ValueError, StatementError):
log.debug("Received malformed v1/library/<book_uuid>/state request.")
ub.session.rollback()
abort(400, description="Malformed request data is missing 'ReadingStates' key") abort(400, description="Malformed request data is missing 'ReadingStates' key")
request_reading_state = request_data["ReadingStates"][0]
request_bookmark = request_reading_state.get("CurrentBookmark")
if request_bookmark:
current_bookmark = kobo_reading_state.current_bookmark
current_bookmark.progress_percent = request_bookmark.get("ProgressPercent")
current_bookmark.content_source_progress_percent = request_bookmark.get("ContentSourceProgressPercent")
location = request_bookmark.get("Location")
if location:
current_bookmark.location_value = location.get("Value")
current_bookmark.location_type = location.get("Type")
current_bookmark.location_source = location.get("Source")
update_results_response["CurrentBookmarkResult"] = {"Result": "Success"}
request_statistics = request_reading_state.get("Statistics")
if request_statistics:
statistics = kobo_reading_state.statistics
statistics.spent_reading_minutes = request_statistics.get("SpentReadingMinutes")
statistics.remaining_time_minutes = request_statistics.get("RemainingTimeMinutes")
update_results_response["StatisticsResult"] = {"Result": "Success"}
request_status_info = request_reading_state.get("StatusInfo")
if request_status_info:
book_read = kobo_reading_state.book_read_link
new_book_read_status = get_ub_read_status(request_status_info.get("Status"))
if new_book_read_status == ub.ReadBook.STATUS_IN_PROGRESS and new_book_read_status != book_read.read_status:
book_read.times_started_reading += 1
book_read.last_time_started_reading = datetime.datetime.utcnow()
book_read.read_status = new_book_read_status
update_results_response["StatusInfoResult"] = {"Result": "Success"}
ub.session.merge(kobo_reading_state) ub.session.merge(kobo_reading_state)
ub.session.commit() ub.session.commit()
...@@ -691,8 +705,8 @@ def get_ub_read_status(kobo_read_status): ...@@ -691,8 +705,8 @@ def get_ub_read_status(kobo_read_status):
def get_or_create_reading_state(book_id): def get_or_create_reading_state(book_id):
book_read = ub.session.query(ub.ReadBook).filter(and_(ub.ReadBook.book_id == book_id, book_read = ub.session.query(ub.ReadBook).filter(ub.ReadBook.book_id == book_id,
ub.ReadBook.user_id == current_user.id)).one_or_none() ub.ReadBook.user_id == current_user.id).one_or_none()
if not book_read: if not book_read:
book_read = ub.ReadBook(user_id=current_user.id, book_id=book_id) book_read = ub.ReadBook(user_id=current_user.id, book_id=book_id)
if not book_read.kobo_reading_state: if not book_read.kobo_reading_state:
...@@ -840,12 +854,15 @@ def HandleProductsRequest(dummy=None): ...@@ -840,12 +854,15 @@ def HandleProductsRequest(dummy=None):
return redirect_or_proxy_request() return redirect_or_proxy_request()
@kobo.app_errorhandler(404) '''@kobo.errorhandler(404)
def handle_404(err): def handle_404(err):
# This handler acts as a catch-all for endpoints that we don't have an interest in # This handler acts as a catch-all for endpoints that we don't have an interest in
# implementing (e.g: v1/analytics/gettests, v1/user/recommendations, etc) # implementing (e.g: v1/analytics/gettests, v1/user/recommendations, etc)
if err:
print('404')
return jsonify(error=str(err)), 404
log.debug("Unknown Request received: %s, method: %s, data: %s", request.base_url, request.method, request.data) log.debug("Unknown Request received: %s, method: %s, data: %s", request.base_url, request.method, request.data)
return redirect_or_proxy_request() return redirect_or_proxy_request()'''
def make_calibre_web_auth_response(): def make_calibre_web_auth_response():
......
...@@ -24,7 +24,7 @@ import signal ...@@ -24,7 +24,7 @@ import signal
import socket import socket
try: try:
from gevent.pywtsgi import WSGIServer from gevent.pywsgi import WSGIServer
from gevent.pool import Pool from gevent.pool import Pool
from gevent import __version__ as _version from gevent import __version__ as _version
VERSION = 'Gevent ' + _version VERSION = 'Gevent ' + _version
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment