1 /*
2  * Copyright 2011 Emmanuel Engelhart <kelson@kiwix.org>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU  General Public License as published by
6  * the Free Software Foundation; either version 3 of the License, or
7  * any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
17  * MA 02110-1301, USA.
18  */
19 
20 #include "book.h"
21 #include "reader.h"
22 
23 #include "tools/base64.h"
24 #include "tools/regexTools.h"
25 #include "tools/networkTools.h"
26 #include "tools/otherTools.h"
27 
28 #include <pugixml.hpp>
29 
30 namespace kiwix
31 {
32 /* Constructor */
Book()33 Book::Book() :
34   m_pathValid(false),
35   m_readOnly(false)
36 {
37 }
38 /* Destructor */
~Book()39 Book::~Book()
40 {
41 }
42 
update(const kiwix::Book & other)43 bool Book::update(const kiwix::Book& other)
44 {
45   if (m_readOnly)
46     return false;
47 
48   if (m_id != other.m_id)
49     return false;
50 
51   m_readOnly = other.m_readOnly;
52   m_path = other.m_path;
53   m_pathValid = other.m_pathValid;
54   m_title = other.m_title;
55   m_description = other.m_description;
56   m_language = other.m_language;
57   m_creator = other.m_creator;
58   m_publisher = other.m_publisher;
59   m_date = other.m_date;
60   m_url = other.m_url;
61   m_name = other.m_name;
62   m_flavour = other.m_flavour;
63   m_tags = other.m_tags;
64   m_origId = other.m_origId;
65   m_articleCount = other.m_articleCount;
66   m_mediaCount = other.m_mediaCount;
67   m_size = other.m_size;
68   m_favicon = other.m_favicon;
69   m_faviconMimeType = other.m_faviconMimeType;
70   m_faviconUrl = other.m_faviconUrl;
71 
72   m_downloadId = other.m_downloadId;
73 
74   return true;
75 }
76 
update(const kiwix::Reader & reader)77 void Book::update(const kiwix::Reader& reader)
78 {
79   m_path = reader.getZimFilePath();
80   m_pathValid = true;
81   m_id = reader.getId();
82   m_title = reader.getTitle();
83   m_description = reader.getDescription();
84   m_language = reader.getLanguage();
85   m_creator = reader.getCreator();
86   m_publisher = reader.getPublisher();
87   m_date = reader.getDate();
88   m_name = reader.getName();
89   m_flavour = reader.getFlavour();
90   m_tags = reader.getTags();
91   m_origId = reader.getOrigId();
92   m_articleCount = reader.getArticleCount();
93   m_mediaCount = reader.getMediaCount();
94   m_size = static_cast<uint64_t>(reader.getFileSize()) << 10;
95   m_pathValid = true;
96 
97   reader.getFavicon(m_favicon, m_faviconMimeType);
98 }
99 
100 #define ATTR(name) node.attribute(name).value()
updateFromXml(const pugi::xml_node & node,const std::string & baseDir)101 void Book::updateFromXml(const pugi::xml_node& node, const std::string& baseDir)
102 {
103   m_id = ATTR("id");
104   std::string path = ATTR("path");
105   if (isRelativePath(path)) {
106     path = computeAbsolutePath(baseDir, path);
107   }
108   m_path = path;
109   m_pathValid = fileExists(path);
110   m_title = ATTR("title");
111   m_description = ATTR("description");
112   m_language = ATTR("language");
113   m_creator = ATTR("creator");
114   m_publisher = ATTR("publisher");
115   m_date = ATTR("date");
116   m_url = ATTR("url");
117   m_name = ATTR("name");
118   m_flavour = ATTR("flavour");
119   m_tags = ATTR("tags");
120   m_origId = ATTR("origId");
121   m_articleCount = strtoull(ATTR("articleCount"), 0, 0);
122   m_mediaCount = strtoull(ATTR("mediaCount"), 0, 0);
123   m_size = strtoull(ATTR("size"), 0, 0) << 10;
124   m_favicon = base64_decode(ATTR("favicon"));
125   m_faviconMimeType = ATTR("faviconMimeType");
126   m_faviconUrl = ATTR("faviconUrl");
127   try {
128     m_downloadId = ATTR("downloadId");
129   } catch(...) {}
130 }
131 #undef ATTR
132 
133 
fromOpdsDate(const std::string & date)134 static std::string fromOpdsDate(const std::string& date)
135 {
136   //The opds date use the standard <YYYY>-<MM>-<DD>T<HH>:<mm>:<SS>Z
137   //and we want <YYYY>-<MM>-<DD>. That's easy, let's take the first 10 char
138   return date.substr(0, 10);
139 }
140 
141 
142 #define VALUE(name) node.child(name).child_value()
updateFromOpds(const pugi::xml_node & node,const std::string & urlHost)143 void Book::updateFromOpds(const pugi::xml_node& node, const std::string& urlHost)
144 {
145   m_id = VALUE("id");
146   if (!m_id.compare(0, 9, "urn:uuid:")) {
147     m_id.erase(0, 9);
148   }
149   // No path on opds.
150   m_title = VALUE("title");
151   m_description = VALUE("summary");
152   m_language = VALUE("language");
153   m_creator = node.child("author").child("name").child_value();
154   m_publisher = node.child("publisher").child("name").child_value();
155   m_date = fromOpdsDate(VALUE("updated"));
156   m_name = VALUE("name");
157   m_flavour = VALUE("flavour");
158   m_tags = VALUE("tags");
159   m_articleCount = strtoull(VALUE("articleCount"), 0, 0);
160   m_mediaCount = strtoull(VALUE("mediaCount"), 0, 0);
161   for(auto linkNode = node.child("link"); linkNode;
162            linkNode = linkNode.next_sibling("link")) {
163     std::string rel = linkNode.attribute("rel").value();
164 
165     if (rel == "http://opds-spec.org/acquisition/open-access") {
166       m_url = linkNode.attribute("href").value();
167       m_size = strtoull(linkNode.attribute("length").value(), 0, 0);
168     }
169     if (rel == "http://opds-spec.org/image/thumbnail") {
170       m_faviconUrl = urlHost + linkNode.attribute("href").value();
171       m_faviconMimeType = linkNode.attribute("type").value();
172     }
173  }
174 
175 }
176 #undef VALUE
177 
getHumanReadableIdFromPath() const178 std::string Book::getHumanReadableIdFromPath() const
179 {
180   std::string id = m_path;
181   if (!id.empty()) {
182     kiwix::removeAccents(id);
183 
184 #ifdef _WIN32
185     id = replaceRegex(id, "", "^.*\\\\");
186 #else
187     id = replaceRegex(id, "", "^.*/");
188 #endif
189 
190     id = replaceRegex(id, "", "\\.zim[a-z]*$");
191     id = replaceRegex(id, "_", " ");
192     id = replaceRegex(id, "plus", "\\+");
193   }
194   return id;
195 }
196 
setPath(const std::string & path)197 void Book::setPath(const std::string& path)
198 {
199  m_path = isRelativePath(path)
200    ? computeAbsolutePath(getCurrentDirectory(), path)
201    : path;
202 }
203 
getFavicon() const204 const std::string& Book::getFavicon() const {
205   if (m_favicon.empty() && !m_faviconUrl.empty()) {
206     try {
207       m_favicon = download(m_faviconUrl);
208     } catch(...) {
209       std::cerr << "Cannot download favicon from " << m_faviconUrl;
210     }
211   }
212   return m_favicon;
213 }
214 
getTagStr(const std::string & tagName) const215 std::string Book::getTagStr(const std::string& tagName) const {
216   return getTagValueFromTagList(convertTags(m_tags), tagName);
217 }
218 
getTagBool(const std::string & tagName) const219 bool Book::getTagBool(const std::string& tagName) const {
220   return convertStrToBool(getTagStr(tagName));
221 }
222 
223 }
224