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