@ -537,15 +537,19 @@ namespace NzbDrone.Core.Books.Calibre
return response . Resource ;
return response . Resource ;
}
}
private bool CalibreLoginEnabled ( CalibreSettings settings )
private bool HasWriteAccess ( CalibreSettings settings )
{
{
var builder = GetBuilder ( $"/book-get-last-read-position/{settings.Library}/1" , settings ) ;
var request = GetBuilder ( $"cdb/cmd/saved_searches" , settings )
builder . SuppressHttpError = true ;
. Post ( )
. SetHeader ( "Content-Type" , "application/json" )
. Build ( ) ;
request . SuppressHttpError = true ;
request . SetContent ( "[\"list\"]" ) ;
var request = builder . Build ( ) ;
var response = _httpClient . Get ( request ) ;
var response = _httpClient . Get ( request ) ;
return response . StatusCode ! = HttpStatusCode . NotFound ;
return response . StatusCode ! = HttpStatusCode . Forbidden ;
}
}
private HttpRequestBuilder GetBuilder ( string relativePath , CalibreSettings settings )
private HttpRequestBuilder GetBuilder ( string relativePath , CalibreSettings settings )
@ -604,16 +608,6 @@ namespace NzbDrone.Core.Books.Calibre
private ValidationFailure TestCalibre ( CalibreSettings settings )
private ValidationFailure TestCalibre ( CalibreSettings settings )
{
{
var authRequired = settings . Host ! = "127.0.0.1" & & settings . Host ! = "::1" & & settings . Host ! = "localhost" ;
if ( authRequired & & settings . Username . IsNullOrWhiteSpace ( ) )
{
return new NzbDroneValidationFailure ( "Username" , "Username required" )
{
DetailedDescription = "A username/password is required for non-local Calibre servers to allow write access"
} ;
}
var builder = GetBuilder ( "" , settings ) ;
var builder = GetBuilder ( "" , settings ) ;
builder . Accept ( HttpAccept . Html ) ;
builder . Accept ( HttpAccept . Html ) ;
builder . SuppressHttpError = true ;
builder . SuppressHttpError = true ;
@ -661,38 +655,18 @@ namespace NzbDrone.Core.Books.Calibre
return new ValidationFailure ( "Port" , "Not a valid Calibre content server. See https://manual.calibre-ebook.com/server.html" ) ;
return new ValidationFailure ( "Port" , "Not a valid Calibre content server. See https://manual.calibre-ebook.com/server.html" ) ;
}
}
CalibreLibraryInfo libraryInfo ;
if ( ! HasWriteAccess ( settings ) )
try
{
{
libraryInfo = GetLibraryInfo ( settings ) ;
return new ValidationFailure ( "Username" , "Readarr needs write access. Configure a user or trusted IP in calibre. See See https://manual.calibre-ebook.com/server.html" ) ;
}
catch ( HttpException e )
{
if ( e . Response . StatusCode = = HttpStatusCode . Unauthorized )
{
return new NzbDroneValidationFailure ( "Username" , "Authentication failure" )
{
DetailedDescription = "Please verify your username and password."
} ;
}
else
{
return new NzbDroneValidationFailure ( string . Empty , "Unknown exception: " + e . Message ) ;
}
}
}
var libraryInfo = GetLibraryInfo ( settings ) ;
if ( settings . Library . IsNullOrWhiteSpace ( ) )
if ( settings . Library . IsNullOrWhiteSpace ( ) )
{
{
settings . Library = libraryInfo . DefaultLibrary ;
settings . Library = libraryInfo . DefaultLibrary ;
}
}
// now that we have library info, double check if auth is actually enabled calibre side. If not, we'll get a 404 back.
// https://github.com/kovidgoyal/calibre/blob/bf53bbf07a6ced728bf6a87d097fb6eb8c67e4e0/src/calibre/srv/books.py#L196
if ( authRequired & & ! CalibreLoginEnabled ( settings ) )
{
return new ValidationFailure ( "Host" , "Remote calibre server must have authentication enabled to allow Readarr write access" ) ;
}
if ( ! libraryInfo . LibraryMap . ContainsKey ( settings . Library ) )
if ( ! libraryInfo . LibraryMap . ContainsKey ( settings . Library ) )
{
{
return new ValidationFailure ( "Library" , "Not a valid library in calibre" ) ;
return new ValidationFailure ( "Library" , "Not a valid library in calibre" ) ;