/* * Copyright 2011 Emmanuel Engelhart * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. */ #include "book.h" #include "tools.h" #include "tools/base64.h" #include "tools/regexTools.h" #include "tools/networkTools.h" #include "tools/otherTools.h" #include "tools/stringTools.h" #include "tools/pathTools.h" #include "tools/archiveTools.h" #include #include #include namespace kiwix { /* Constructor */ Book::Book() : m_pathValid(false), m_readOnly(false) { } /* Destructor */ Book::~Book() { } Book::Illustrations Book::getIllustrations() const { return m_illustrations; } bool Book::update(const kiwix::Book& other) { if (m_readOnly) return false; if (m_id != other.m_id) return false; *this = other; return true; } void Book::update(const zim::Archive& archive) { m_path = archive.getFilename(); m_pathValid = true; m_id = std::string(archive.getUuid()); m_title = getArchiveTitle(archive); m_description = getMetaDescription(archive); m_language = getMetaLanguage(archive); m_creator = getMetaCreator(archive); m_publisher = getMetaPublisher(archive); m_date = getMetaDate(archive); m_name = getMetaName(archive); m_flavour = getMetaFlavour(archive); m_tags = getMetaTags(archive); m_category = getCategoryFromTags(); m_articleCount = getArchiveArticleCount(archive); m_mediaCount = getArchiveMediaCount(archive); m_size = static_cast(getArchiveFileSize(archive)) << 10; m_illustrations.clear(); for ( const auto illustrationSize : archive.getIllustrationSizes() ) { const auto illustration = std::make_shared(); const zim::Item illustrationItem = archive.getIllustrationItem(illustrationSize); illustration->width = illustration->height = illustrationSize; illustration->mimeType = illustrationItem.getMimetype(); illustration->data = illustrationItem.getData(); // NOTE: illustration->url is left uninitialized m_illustrations.push_back(illustration); } } #define ATTR(name) node.attribute(name).value() void Book::updateFromXml(const pugi::xml_node& node, const std::string& baseDir) { m_id = ATTR("id"); std::string path = ATTR("path"); if (isRelativePath(path)) { path = computeAbsolutePath(baseDir, path); } m_path = path; m_pathValid = fileReadable(path); m_title = ATTR("title"); m_description = ATTR("description"); m_language = ATTR("language"); m_creator = ATTR("creator"); m_publisher = ATTR("publisher"); m_date = ATTR("date"); m_url = ATTR("url"); m_name = ATTR("name"); m_flavour = ATTR("flavour"); m_tags = ATTR("tags"); m_origId = ATTR("origId"); m_articleCount = strtoull(ATTR("articleCount"), 0, 0); m_mediaCount = strtoull(ATTR("mediaCount"), 0, 0); m_size = strtoull(ATTR("size"), 0, 0) << 10; std::string favicon_mimetype = ATTR("faviconMimeType"); if (! favicon_mimetype.empty()) { const auto favicon = std::make_shared(); favicon->data = base64_decode(ATTR("favicon")); favicon->mimeType = favicon_mimetype; favicon->url = ATTR("faviconUrl"); m_illustrations.assign(1, favicon); } try { m_downloadId = ATTR("downloadId"); } catch(...) {} const auto catattr = node.attribute("category"); m_category = catattr.empty() ? getCategoryFromTags() : catattr.value(); } #undef ATTR static std::string fromOpdsDate(const std::string& date) { //The opds date use the standard --
T::Z //and we want --
. That's easy, let's take the first 10 char return date.substr(0, 10); } #define VALUE(name) node.child(name).child_value() void Book::updateFromOpds(const pugi::xml_node& node, const std::string& urlHost) { m_id = VALUE("id"); if (!m_id.compare(0, 9, "urn:uuid:")) { m_id.erase(0, 9); } // No path on opds. m_title = VALUE("title"); m_description = VALUE("summary"); m_language = VALUE("language"); m_creator = node.child("author").child("name").child_value(); m_publisher = node.child("publisher").child("name").child_value(); const std::string dcIssuedDate = VALUE("dc:issued"); m_date = dcIssuedDate.empty() ? VALUE("updated") : dcIssuedDate; m_date = fromOpdsDate(m_date); m_name = VALUE("name"); m_flavour = VALUE("flavour"); m_tags = VALUE("tags"); const auto catnode = node.child("category"); m_category = catnode.empty() ? getCategoryFromTags() : catnode.child_value(); m_articleCount = strtoull(VALUE("articleCount"), 0, 0); m_mediaCount = strtoull(VALUE("mediaCount"), 0, 0); for(auto linkNode = node.child("link"); linkNode; linkNode = linkNode.next_sibling("link")) { std::string rel = linkNode.attribute("rel").value(); if (rel == "http://opds-spec.org/acquisition/open-access") { m_url = linkNode.attribute("href").value(); m_size = strtoull(linkNode.attribute("length").value(), 0, 0); } if (rel == "http://opds-spec.org/image/thumbnail") { const auto favicon = std::make_shared(); favicon->data.clear(); favicon->url = urlHost + linkNode.attribute("href").value(); favicon->mimeType = linkNode.attribute("type").value(); m_illustrations.assign(1, favicon); } } } #undef VALUE std::string Book::getHumanReadableIdFromPath() const { std::string id = m_path; if (!id.empty()) { id = kiwix::removeAccents(id); #ifdef _WIN32 id = replaceRegex(id, "", "^.*\\\\"); #else id = replaceRegex(id, "", "^.*/"); #endif id = replaceRegex(id, "", "\\.zim[a-z]*$"); id = replaceRegex(id, "_", " "); id = replaceRegex(id, "plus", "\\+"); } return id; } void Book::setPath(const std::string& path) { m_path = isRelativePath(path) ? computeAbsolutePath(getCurrentDirectory(), path) : path; } const Book::Illustration Book::missingDefaultIllustration; std::shared_ptr Book::getIllustration(unsigned int size) const { for ( const auto& ilPtr : m_illustrations ) { if (ilPtr->width == size && ilPtr->height == size) { return ilPtr; } } throw std::runtime_error("Cannot find illustration"); } const Book::Illustration& Book::getDefaultIllustration() const { try { return *getIllustration(48); } catch (...) { return missingDefaultIllustration; } } const std::string& Book::Illustration::getData() const { if (data.empty() && !url.empty()) { const std::lock_guard l(mutex); if ( data.empty() ) { try { data = download(url); } catch(...) { std::cerr << "Cannot download favicon from " << url; } } } return data; } const std::string& Book::getFavicon() const { return getDefaultIllustration().getData(); } const std::string& Book::getFaviconUrl() const { return getDefaultIllustration().url; } const std::string& Book::getFaviconMimeType() const { return getDefaultIllustration().mimeType; } std::string Book::getTagStr(const std::string& tagName) const { return getTagValueFromTagList(convertTags(m_tags), tagName); } bool Book::getTagBool(const std::string& tagName) const { return convertStrToBool(getTagStr(tagName)); } std::string Book::getCategory() const { return m_category; } std::string Book::getCategoryFromTags() const { try { return getTagStr("category"); } catch ( const std::out_of_range& ) { return ""; } } }