diff --git a/frontend/socketutil.lua b/frontend/socketutil.lua index 7ad9ca132..e11745ae1 100644 --- a/frontend/socketutil.lua +++ b/frontend/socketutil.lua @@ -136,4 +136,40 @@ function socketutil.file_sink(handle, io_err) end end +function socketutil.redact_headers(headers) + local sensitive_headers = { + ["authorization"] = true, + ["cookie"] = true, + ["proxy-authorization"] = true, + ["set-cookie"] = true, + } + local safe_headers = {} + for key, value in pairs(headers) do + if sensitive_headers[key] then + safe_headers[key] = "REDACTED" + else + safe_headers[key] = value + end + end + return safe_headers +end + +function socketutil.redact_request(request) + local sensitive_props = { + ["password"] = true, + ["user"] = true, + } + local safe_request = {} + for key, value in pairs(request) do + if sensitive_props[key] then + safe_request[key] = "REDACTED" + elseif key == "headers" then + safe_request[key] = socketutil.redact_headers(value) + else + safe_request[key] = value + end + end + return safe_request +end + return socketutil diff --git a/frontend/ui/widget/inputtext.lua b/frontend/ui/widget/inputtext.lua index 6c75a0bd1..032916934 100644 --- a/frontend/ui/widget/inputtext.lua +++ b/frontend/ui/widget/inputtext.lua @@ -464,7 +464,7 @@ function InputText:initTextBox(text, char_added) padding = self.padding, padding_top = 0, padding_bottom = 0, - margin = self.margin, + margin = self.margin + self.bordersize, self._check_button, } else diff --git a/plugins/opds.koplugin/opdsbrowser.lua b/plugins/opds.koplugin/opdsbrowser.lua index d8b5cded3..9a061acb0 100644 --- a/plugins/opds.koplugin/opdsbrowser.lua +++ b/plugins/opds.koplugin/opdsbrowser.lua @@ -1,6 +1,7 @@ local BD = require("ui/bidi") local ButtonDialog = require("ui/widget/buttondialog") local Cache = require("cache") +local CheckButton = require("ui/widget/checkbutton") local ConfirmBox = require("ui/widget/confirmbox") local DocumentRegistry = require("document/documentregistry") local InfoMessage = require("ui/widget/infomessage") @@ -98,6 +99,7 @@ function OPDSBrowser:genItemTableFromRoot() url = server.url, username = server.username, password = server.password, + raw_names = server.raw_names, -- use server raw filenames for download searchable = server.url:match("%%s") and true or false, }) end @@ -132,7 +134,7 @@ function OPDSBrowser:addEditCatalog(item) title = _("Add OPDS catalog") end - local dialog + local dialog, check_button_raw_names dialog = MultiInputDialog:new{ title = title, fields = fields, @@ -148,13 +150,21 @@ function OPDSBrowser:addEditCatalog(item) { text = _("Save"), callback = function() - self:editCatalogFromInput(dialog:getFields(), item) + local new_fields = dialog:getFields() + new_fields[5] = check_button_raw_names.checked or nil + self:editCatalogFromInput(new_fields, item) UIManager:close(dialog) end, }, }, }, } + check_button_raw_names = CheckButton:new{ + text = _("Use server filenames"), + checked = item and item.raw_names, + parent = dialog, + } + dialog:addWidget(check_button_raw_names) UIManager:show(dialog) dialog:onShowKeyboard() end @@ -181,7 +191,8 @@ function OPDSBrowser:addSubCatalog(item_url) local name = dialog:getInputText() if name ~= "" then UIManager:close(dialog) - local fields = {name, item_url, self.root_catalog_username, self.root_catalog_password} + local fields = {name, item_url, + self.root_catalog_username, self.root_catalog_password, self.root_catalog_raw_names} self:editCatalogFromInput(fields, false, true) -- no init, stay in the subcatalog end end, @@ -206,10 +217,11 @@ function OPDSBrowser:editCatalogFromInput(fields, item, no_init) else -- add new new_server = {} end - new_server.title = fields[1] - new_server.url = fields[2]:match("^%a+://") and fields[2] or "http://" .. fields[2] - new_server.username = fields[3] ~= "" and fields[3] or nil - new_server.password = fields[4] + new_server.title = fields[1] + new_server.url = fields[2]:match("^%a+://") and fields[2] or "http://" .. fields[2] + new_server.username = fields[3] ~= "" and fields[3] or nil + new_server.password = fields[4] + new_server.raw_names = fields[5] if not item then table.insert(self.opds_servers, new_server) end @@ -245,12 +257,12 @@ function OPDSBrowser:fetchFeed(item_url, headers_only) user = self.root_catalog_username, password = self.root_catalog_password, } - logger.dbg("Request:", request) + logger.dbg("Request:", socketutil.redact_request(request)) local code, headers, status = socket.skip(1, http.request(request)) socketutil:reset_timeout() if headers_only then - return headers and headers["last-modified"] + return headers end if code == 200 then local xml = table.concat(sink) @@ -284,7 +296,8 @@ end -- Parses feed to catalog function OPDSBrowser:parseFeed(item_url) - local feed_last_modified = self:fetchFeed(item_url, true) -- headers only + local headers = self:fetchFeed(item_url, true) + local feed_last_modified = headers and headers["last-modified"] local feed if feed_last_modified then local hash = "opds|catalog|" .. item_url .. "|" .. feed_last_modified @@ -307,6 +320,21 @@ function OPDSBrowser:parseFeed(item_url) end end +function OPDSBrowser:getServerFileName(item_url) + local headers = self:fetchFeed(item_url, true) + if headers then + logger.dbg("OPDSBrowser: server file headers", socketutil.redact_headers(headers)) + local header = headers["content-disposition"] + if header then + return header:match('filename="*([^"]+)"*') + end + header = headers["location"] + if header then + return header:gsub(".*/", "") + end + end +end + -- Generates link to search in catalog function OPDSBrowser:getSearchTemplate(osd_url) -- parse search descriptor @@ -563,10 +591,13 @@ function OPDSBrowser:showDownloads(item) filename = item.author .. " - " .. filename end local filename_orig = filename + if self.root_catalog_raw_names then + filename = nil + end local function createTitle(path, file) -- title for ButtonDialog return T(_("Download folder:\n%1\n\nDownload filename:\n%2\n\nDownload file type:"), - BD.dirpath(path), file) + BD.dirpath(path), file or _("")) end local buttons = {} -- buttons for ButtonDialog @@ -611,7 +642,9 @@ function OPDSBrowser:showDownloads(item) table.insert(download_buttons, { text = text .. "\u{2B07}", -- append DOWNWARDS BLACK ARROW callback = function() - self:downloadFile(filename .. "." .. string.lower(filetype), acquisition.href) + local file = filename and filename .. "." .. string.lower(filetype) + or self:getServerFileName(acquisition.href) + self:downloadFile(file, acquisition.href) UIManager:close(self.download_dialog) end, }) @@ -656,7 +689,7 @@ function OPDSBrowser:showDownloads(item) local dialog dialog = InputDialog:new{ title = _("Enter filename"), - input = filename, + input = filename or filename_orig, input_hint = filename_orig, buttons = { { @@ -802,9 +835,10 @@ function OPDSBrowser:onMenuSelect(item) self:showDownloads(item) else -- catalog or Search item if #self.paths == 0 then -- root list - self.root_catalog_title = item.text - self.root_catalog_username = item.username - self.root_catalog_password = item.password + self.root_catalog_title = item.text + self.root_catalog_username = item.username + self.root_catalog_password = item.password + self.root_catalog_raw_names = item.raw_names end local connect_callback if item.searchable then @@ -831,13 +865,6 @@ function OPDSBrowser:onMenuHold(item) title_align = "center", buttons = { { - { - text = _("Edit"), - callback = function() - UIManager:close(dialog) - self:addEditCatalog(item) - end, - }, { text = _("Delete"), callback = function() @@ -851,6 +878,13 @@ function OPDSBrowser:onMenuHold(item) }) end, }, + { + text = _("Edit"), + callback = function() + UIManager:close(dialog) + self:addEditCatalog(item) + end, + }, }, }, }