add Basic Authentication for OPDS catalog

use coroutine to support ad-hoc callback in login button so that when
the username/password is incorrect it will retry automatically
currently tested with COPS on apache

This should fix #1001.
pull/1637/head
chrox 9 years ago
parent 384d5eca72
commit b0df6d53e9

@ -291,7 +291,11 @@ function MenuItem:onTapSelect(arg, ges)
UIManager:scheduleIn(0.1, function()
self[1].invert = false
UIManager:setDirty(self.show_parent, refreshfunc)
self.menu:onMenuSelect(self.table, pos)
DEBUG("creating coroutine for menu select")
local co = coroutine.create(function()
self.menu:onMenuSelect(self.table, pos)
end)
coroutine.resume(co)
end)
return true
end

@ -1,6 +1,7 @@
local MultiInputDialog = require("ui/widget/multiinputdialog")
local ButtonDialog = require("ui/widget/buttondialog")
local InfoMessage = require("ui/widget/infomessage")
local LoginDialog = require("ui/widget/logindialog")
local lfs = require("libs/libkoreader-lfs")
local OPDSParser = require("ui/opdsparser")
local NetworkMgr = require("ui/networkmgr")
@ -20,6 +21,7 @@ local socket = require('socket')
local http = require('socket.http')
local https = require('ssl.https')
local ltn12 = require('ltn12')
local mime = require('mime')
local CatalogCacheItem = CacheItem:new{
size = 1024, -- fixed size for catalog item
@ -214,12 +216,37 @@ function OPDSBrowser:genItemTableFromRoot()
return item_table
end
function OPDSBrowser:getBasicAuthentication(host)
local authentications = G_reader_settings:readSetting("www-auth") or {}
return authentications[host]
end
function OPDSBrowser:setBasicAuthentication(host, username, password)
local authentications = G_reader_settings:readSetting("www-auth") or {}
authentications[host] = {
username = username,
password = password,
}
G_reader_settings:saveSetting("www-auth", authentications)
end
function OPDSBrowser:getAuthorizationHeader(host)
local auth = self:getBasicAuthentication(host)
if auth then
local authorization = auth.username .. ':' .. auth.password
return {
Authorization = "Basic " .. mime.b64(authorization),
}
end
end
function OPDSBrowser:fetchFeed(feed_url)
local headers, request, sink = {}, {}, {}
local parsed = url.parse(feed_url)
request['url'] = feed_url
request['method'] = 'GET'
request['sink'] = ltn12.sink.table(sink)
request['headers'] = self:getAuthorizationHeader(parsed.host)
DEBUG("request", request)
http.TIMEOUT, https.TIMEOUT = 10, 10
local httpRequest = parsed.scheme == 'http' and http.request or https.request
@ -230,13 +257,72 @@ function OPDSBrowser:fetchFeed(feed_url)
error(code)
end
local xml = table.concat(sink)
if xml ~= "" then
--DEBUG("xml", xml)
return xml
--DEBUG("response", code, headers, status)
if code == 401 and status and status:find("Unauthorized") then
self._coroutine = coroutine.running() or self._coroutine
self:fetchWithLogin(parsed.host, function()
return self:fetchFeed(feed_url)
end)
if coroutine.running() then
local result = coroutine.yield()
return result
end
else
local xml = table.concat(sink)
if xml ~= "" then
--DEBUG("xml", xml)
return xml
end
end
end
function OPDSBrowser:fetchWithLogin(host, callback)
self.login_dialog = LoginDialog:new{
title = _("Login to OPDS server"),
username = "",
buttons = {
{
{
text = _("Cancel"),
enabled = true,
callback = function()
self:closeDialog()
end,
},
{
text = _("Login"),
enabled = true,
callback = function()
local username, password = self:getCredential()
self:setBasicAuthentication(host, username, password)
self:closeDialog()
UIManager:scheduleIn(0.5, function()
local res = callback()
if res then
coroutine.resume(self._coroutine, res)
end
end)
end,
},
},
},
width = Screen:getWidth() * 0.8,
height = Screen:getHeight() * 0.4,
}
self.login_dialog:onShowKeyboard()
UIManager:show(self.login_dialog)
end
function OPDSBrowser:closeDialog()
self.login_dialog:onClose()
UIManager:close(self.login_dialog)
end
function OPDSBrowser:getCredential()
return self.login_dialog:getCredential()
end
function OPDSBrowser:parseFeed(feed_url)
local feed = nil
local hash = "opds|catalog|" .. feed_url
@ -388,6 +474,7 @@ function OPDSBrowser:downloadFile(title, format, remote_url)
local httpRequest = parsed.scheme == 'http' and http.request or https.request
local r, c, h = httpRequest{
url = remote_url,
headers = self:getAuthorizationHeader(parsed.host),
sink = ltn12.sink.file(io.open(local_path, "w")),
}

Loading…
Cancel
Save