mirror of https://github.com/kiwix/libkiwix.git
333 lines
10 KiB
C++
333 lines
10 KiB
C++
/*
|
|
* Copyright 2011 Emmanuel Engelhart <kelson@kiwix.org>
|
|
*
|
|
* 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 "manager.h"
|
|
#include "downloader.h"
|
|
|
|
namespace kiwix
|
|
{
|
|
/* Constructor */
|
|
Manager::Manager(Library* library):
|
|
writableLibraryPath(""),
|
|
library(library)
|
|
{
|
|
}
|
|
/* Destructor */
|
|
Manager::~Manager()
|
|
{
|
|
}
|
|
bool Manager::parseXmlDom(const pugi::xml_document& doc,
|
|
const bool readOnly,
|
|
const string libraryPath)
|
|
{
|
|
pugi::xml_node libraryNode = doc.child("library");
|
|
|
|
string libraryVersion = libraryNode.attribute("version").value();
|
|
|
|
for (pugi::xml_node bookNode = libraryNode.child("book"); bookNode;
|
|
bookNode = bookNode.next_sibling("book")) {
|
|
bool ok = true;
|
|
kiwix::Book book;
|
|
|
|
book.setReadOnly(readOnly);
|
|
book.setId(bookNode.attribute("id").value());
|
|
std::string path = bookNode.attribute("path").value();
|
|
if (isRelativePath(path)) {
|
|
path = computeAbsolutePath(
|
|
removeLastPathElement(libraryPath, true, false), path);
|
|
}
|
|
book.setPath(path);
|
|
book.setLast(std::string(bookNode.attribute("last").value()) != "undefined"
|
|
? bookNode.attribute("last").value()
|
|
: "");
|
|
std::string indexPath = bookNode.attribute("indexPath").value();
|
|
if (isRelativePath(indexPath)) {
|
|
indexPath = computeAbsolutePath(
|
|
removeLastPathElement(libraryPath, true, false), indexPath);
|
|
}
|
|
book.setIndexPath(indexPath);
|
|
book.setIndexType(XAPIAN);
|
|
book.setTitle(bookNode.attribute("title").value());
|
|
book.setName(bookNode.attribute("name").value());
|
|
book.setTags(bookNode.attribute("tags").value());
|
|
book.setDescription(bookNode.attribute("description").value());
|
|
book.setLanguage(bookNode.attribute("language").value());
|
|
book.setDate(bookNode.attribute("date").value());
|
|
book.setCreator(bookNode.attribute("creator").value());
|
|
book.setPublisher(bookNode.attribute("publisher").value());
|
|
book.setUrl(bookNode.attribute("url").value());
|
|
book.setOrigId(bookNode.attribute("origId").value());
|
|
book.setArticleCount(strtoull(bookNode.attribute("articleCount").value(), 0, 0));
|
|
book.setMediaCount(strtoull(bookNode.attribute("mediaCount").value(), 0, 0));
|
|
book.setSize(strtoull(bookNode.attribute("size").value(), 0, 0));
|
|
book.setFavicon(bookNode.attribute("favicon").value());
|
|
book.setFaviconMimeType(bookNode.attribute("faviconMimeType").value());
|
|
|
|
/* Update the book properties with the new importer */
|
|
if (libraryVersion.empty()
|
|
|| atoi(libraryVersion.c_str()) <= atoi(KIWIX_LIBRARY_VERSION)) {
|
|
ok = false;
|
|
if (!book.path().empty()) {
|
|
ok = this->readBookFromPath(book.path());
|
|
}
|
|
}
|
|
|
|
if (ok) {
|
|
library->addBook(book);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Manager::readXml(const string& xml,
|
|
const bool readOnly,
|
|
const string libraryPath)
|
|
{
|
|
pugi::xml_document doc;
|
|
pugi::xml_parse_result result
|
|
= doc.load_buffer_inplace((void*)xml.data(), xml.size());
|
|
|
|
if (result) {
|
|
this->parseXmlDom(doc, readOnly, libraryPath);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
bool Manager::parseOpdsDom(const pugi::xml_document& doc, const std::string& urlHost)
|
|
{
|
|
pugi::xml_node libraryNode = doc.child("feed");
|
|
|
|
for (pugi::xml_node entryNode = libraryNode.child("entry"); entryNode;
|
|
entryNode = entryNode.next_sibling("entry")) {
|
|
kiwix::Book book;
|
|
|
|
book.setReadOnly(false);
|
|
book.setId(entryNode.child("id").child_value());
|
|
book.setTitle(entryNode.child("title").child_value());
|
|
book.setDescription(entryNode.child("summary").child_value());
|
|
book.setLanguage(entryNode.child("language").child_value());
|
|
book.setDate(entryNode.child("updated").child_value());
|
|
book.setCreator(entryNode.child("author").child("name").child_value());
|
|
for(pugi::xml_node linkNode = entryNode.child("link"); linkNode;
|
|
linkNode = linkNode.next_sibling("link")) {
|
|
std::string rel = linkNode.attribute("rel").value();
|
|
|
|
if (rel == "http://opds-spec.org/image/thumbnail") {
|
|
auto faviconUrl = urlHost + linkNode.attribute("href").value();
|
|
Downloader downloader;
|
|
auto fileHandle = downloader.download(faviconUrl);
|
|
if (fileHandle.success) {
|
|
auto content = getFileContent(fileHandle.path);
|
|
book.setFavicon(base64_encode((const unsigned char*)content.data(), content.size()));
|
|
book.setFaviconMimeType(linkNode.attribute("type").value());
|
|
} else {
|
|
std::cerr << "Cannot get favicon content from " << faviconUrl << std::endl;
|
|
}
|
|
|
|
} else if (rel == "http://opds-spec.org/acquisition/open-access") {
|
|
book.setUrl(linkNode.attribute("href").value());
|
|
}
|
|
}
|
|
|
|
/* Update the book properties with the new importer */
|
|
library->addBook(book);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
bool Manager::readOpds(const string& content, const std::string& urlHost)
|
|
{
|
|
pugi::xml_document doc;
|
|
pugi::xml_parse_result result
|
|
= doc.load_buffer_inplace((void*)content.data(), content.size());
|
|
|
|
if (result) {
|
|
this->parseOpdsDom(doc, urlHost);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool Manager::readFile(const string path, const bool readOnly)
|
|
{
|
|
return this->readFile(path, path, readOnly);
|
|
}
|
|
|
|
bool Manager::readFile(const string nativePath,
|
|
const string UTF8Path,
|
|
const bool readOnly)
|
|
{
|
|
bool retVal = true;
|
|
pugi::xml_document doc;
|
|
pugi::xml_parse_result result = doc.load_file(nativePath.c_str());
|
|
|
|
if (result) {
|
|
this->parseXmlDom(doc, readOnly, UTF8Path);
|
|
} else {
|
|
retVal = false;
|
|
}
|
|
|
|
/* This has to be set (although if the file does not exists) to be
|
|
* able to know where to save the library if new content are
|
|
* available */
|
|
if (!readOnly) {
|
|
this->writableLibraryPath = UTF8Path;
|
|
}
|
|
|
|
return retVal;
|
|
}
|
|
|
|
|
|
/* Add a book to the library. Return empty string if failed, book id otherwise
|
|
*/
|
|
string Manager::addBookFromPathAndGetId(const string pathToOpen,
|
|
const string pathToSave,
|
|
const string url,
|
|
const bool checkMetaData)
|
|
{
|
|
kiwix::Book book;
|
|
|
|
if (this->readBookFromPath(pathToOpen, &book)) {
|
|
if (pathToSave != pathToOpen) {
|
|
book.setPath(isRelativePath(pathToSave)
|
|
? computeAbsolutePath(
|
|
removeLastPathElement(writableLibraryPath, true, false),
|
|
pathToSave)
|
|
: pathToSave);
|
|
}
|
|
|
|
if (!checkMetaData
|
|
|| (checkMetaData && !book.title().empty() && !book.language().empty()
|
|
&& !book.date().empty())) {
|
|
book.setUrl(url);
|
|
library->addBook(book);
|
|
return book.id();
|
|
}
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
/* Wrapper over Manager::addBookFromPath which return a bool instead of a string
|
|
*/
|
|
bool Manager::addBookFromPath(const string pathToOpen,
|
|
const string pathToSave,
|
|
const string url,
|
|
const bool checkMetaData)
|
|
{
|
|
return !(
|
|
this->addBookFromPathAndGetId(pathToOpen, pathToSave, url, checkMetaData)
|
|
.empty());
|
|
}
|
|
|
|
bool Manager::readBookFromPath(const string path, kiwix::Book* book)
|
|
{
|
|
try {
|
|
kiwix::Reader* reader = new kiwix::Reader(path);
|
|
|
|
if (book != NULL) {
|
|
book->setPath(path);
|
|
book->setId(reader->getId());
|
|
book->setDescription(reader->getDescription());
|
|
book->setLanguage(reader->getLanguage());
|
|
book->setDate(reader->getDate());
|
|
book->setCreator(reader->getCreator());
|
|
book->setPublisher(reader->getPublisher());
|
|
book->setTitle(reader->getTitle());
|
|
book->setName(reader->getName());
|
|
book->setTags(reader->getTags());
|
|
book->setOrigId(reader->getOrigId());
|
|
book->setArticleCount(reader->getArticleCount());
|
|
book->setMediaCount(reader->getMediaCount());
|
|
book->setSize(reader->getFileSize());
|
|
|
|
string favicon;
|
|
string faviconMimeType;
|
|
if (reader->getFavicon(favicon, faviconMimeType)) {
|
|
book->setFavicon(base64_encode(
|
|
reinterpret_cast<const unsigned char*>(favicon.c_str()),
|
|
favicon.length()));
|
|
book->setFaviconMimeType(faviconMimeType);
|
|
}
|
|
}
|
|
|
|
delete reader;
|
|
} catch (const std::exception& e) {
|
|
std::cerr << e.what() << std::endl;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Manager::removeBookById(const string id)
|
|
{
|
|
return library.removeBookById(id);
|
|
}
|
|
|
|
bool Manager::updateBookLastOpenDateById(const string id)
|
|
try {
|
|
auto book = library.getBookById(id);
|
|
char unixdate[12];
|
|
sprintf(unixdate, "%d", (int)time(NULL));
|
|
book.last = unixdate;
|
|
return true;
|
|
} catch(...) {
|
|
return false;
|
|
}
|
|
|
|
bool Manager::setBookIndex(const string id,
|
|
const string path,
|
|
const supportedIndexType type)
|
|
try {
|
|
auto book = library->getBookById(id);
|
|
book.setIndexPath(isRelativePath(path)
|
|
? computeAbsolutePath(
|
|
removeLastPathElement(writableLibraryPath, true, false),
|
|
path)
|
|
: path);
|
|
book.setIndexType(type);
|
|
return true;
|
|
} catch (...) {
|
|
return false;
|
|
}
|
|
|
|
bool Manager::setBookPath(const string id, const string path)
|
|
try {
|
|
auto book = library->getBookById(id);
|
|
book.setPath(isRelativePath(path)
|
|
? computeAbsolutePath(
|
|
removeLastPathElement(writableLibraryPath, true, false),
|
|
path)
|
|
: path);
|
|
return true;
|
|
} catch(...) {
|
|
return false;
|
|
}
|
|
|
|
}
|