11"""Handlers for borrowing books"""
22
3+ import contextlib
34import copy
45import hashlib
56import hmac
@@ -105,7 +106,7 @@ class borrow(delegate.page):
105106 def GET (self , key ):
106107 return self .POST (key )
107108
108- def POST (self , key ): # noqa: PLR0915
109+ def POST (self , key ): # noqa: PLR0912, PLR0915
109110 """Called when the user wants to borrow the edition"""
110111
111112 i = web .input (
@@ -161,7 +162,16 @@ def POST(self, key): # noqa: PLR0915
161162 raise web .seeother (archive_url )
162163
163164 error_redirect = archive_url
164- edition_redirect = urllib .parse .quote (i .redirect or edition .url ())
165+
166+ # Strip scheme/host to prevent open-redirect attacks.
167+ if i .redirect :
168+ parsed = urllib .parse .urlsplit (i .redirect )
169+ edition_redirect = urllib .parse .urlunsplit (
170+ ('' , '' , parsed .path , parsed .query , parsed .fragment )
171+ )
172+ else :
173+ edition_redirect = edition .url ()
174+
165175 user = accounts .get_current_user ()
166176
167177 if user :
@@ -173,18 +183,33 @@ def POST(self, key): # noqa: PLR0915
173183 ) # invalidate cache for user loans
174184 if not user or not ia_itemname or not s3_keys :
175185 web .setcookie (config .login_cookie_name , "" , expires = - 1 )
186+ return_path = f"{ edition_redirect } /borrow?action={ action } "
176187 redirect_url = (
177- f"/account/login?redirect={ edition_redirect } /borrow?action= { action } "
188+ f"/account/login?redirect={ urllib . parse . quote ( return_path , safe = '' ) } "
178189 )
179190 if i ._autoReadAloud is not None :
180191 redirect_url += '&_autoReadAloud=' + i ._autoReadAloud
181192 raise web .seeother (redirect_url )
182193
183194 if action == 'return' :
184- lending .s3_loan_api (s3_keys , ocaid = edition .ocaid , action = 'return_loan' )
185- stats .increment ('ol.loans.return' )
195+ with contextlib .suppress (lending .PatronAccessException ):
196+ lending .s3_loan_api (s3_keys , ocaid = edition .ocaid , action = 'return_loan' )
197+
186198 edition .update_loan_status ()
187199 user .update_loan_status ()
200+ title = edition .title or _ ('this book' )
201+
202+ if user .has_borrowed (edition ):
203+ add_flash_message (
204+ 'error' ,
205+ _ (
206+ 'Unable to return %s. Please try again later or contact info@archive.org.'
207+ )
208+ % title ,
209+ )
210+ else :
211+ stats .increment ('ol.loans.return' )
212+ add_flash_message ('success' , _ ('%s has been returned.' ) % title )
188213 raise web .seeother (edition_redirect )
189214 elif action == 'join-waitinglist' :
190215 lending .get_cached_user_waiting_loans .memcache_delete (
0 commit comments