1 /*
2  * Copyright (C) 2006 Tommi Maekitalo
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
11  * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
12  * NON-INFRINGEMENT.  See the 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 St, Fifth Floor, Boston, MA  02110-1301 USA
17  *
18  */
19 
20 #include <zim/article.h>
21 #include "template.h"
22 #include "_dirent.h"
23 #include "cluster.h"
24 #include <zim/fileheader.h>
25 #include "fileimpl.h"
26 #include "file_part.h"
27 #include <sstream>
28 #include <iostream>
29 #include <stdexcept>
30 #include <limits>
31 #include "log.h"
32 
33 log_define("zim.article")
34 
35 namespace zim
36 {
getArticleSize() const37   size_type Article::getArticleSize() const
38   {
39     auto dirent = getDirent();
40     return size_type(file->getCluster(dirent->getClusterNumber())
41                          ->getBlobSize(dirent->getBlobNumber()));
42   }
43 
44   namespace
45   {
46     class Ev : public TemplateParser::Event
47     {
48         std::ostream& out;
49         Article& article;
50         std::shared_ptr<FileImpl> file;
51         unsigned maxRecurse;
52 
53       public:
Ev(std::ostream & out_,Article & article_,std::shared_ptr<FileImpl> file_,unsigned maxRecurse_)54         Ev(std::ostream& out_, Article& article_, std::shared_ptr<FileImpl> file_, unsigned maxRecurse_)
55           : out(out_),
56             article(article_),
57             file(file_),
58             maxRecurse(maxRecurse_)
59           { }
60         void onData(const std::string& data);
61         void onToken(const std::string& token);
62         void onLink(char ns, const std::string& title);
63     };
64 
onData(const std::string & data)65     void Ev::onData(const std::string& data)
66     {
67       out << data;
68     }
69 
onToken(const std::string & token)70     void Ev::onToken(const std::string& token)
71     {
72       log_trace("onToken(\"" << token << "\")");
73 
74       if (token == "title")
75         out << article.getTitle();
76       else if (token == "url")
77         out << article.getUrl();
78       else if (token == "namespace")
79         out << article.getNamespace();
80       else if (token == "content")
81       {
82         if (maxRecurse <= 0)
83           throw std::runtime_error("maximum recursive limit is reached");
84         article.getPage(out, false, maxRecurse - 1);
85       }
86       else
87       {
88         log_warn("unknown token \"" << token  << "\" found in template");
89         out << "<%" << token << "%>";
90       }
91     }
92 
onLink(char ns,const std::string & url)93     void Ev::onLink(char ns, const std::string& url)
94     {
95       if (maxRecurse <= 0)
96         throw std::runtime_error("maximum recursive limit is reached");
97       std::pair<bool, article_index_t> r = file->findx(ns, url);
98       if (r.first) {
99           Article(file, article_index_type(r.second)).getPage(out, false, maxRecurse - 1);
100       } else {
101           throw std::runtime_error(std::string("impossible to find article ") + std::string(1, ns) + std::string("/") + url);
102       }
103     }
104 
105   }
106 
getDirent() const107   std::shared_ptr<const Dirent> Article::getDirent() const
108   {
109     return file->getDirent(article_index_t(idx));
110   }
111 
getParameter() const112   std::string Article::getParameter() const
113   {
114     return getDirent()->getParameter();
115   }
116 
getTitle() const117   std::string Article::getTitle() const
118   {
119     return getDirent()->getTitle();
120   }
121 
getUrl() const122   std::string Article::getUrl() const
123   {
124     return getDirent()->getUrl();
125   }
126 
getLongUrl() const127   std::string Article::getLongUrl() const
128   {
129     return getDirent()->getLongUrl();
130   }
131 
getLibraryMimeType() const132   uint16_t Article::getLibraryMimeType() const
133   {
134     return getDirent()->getMimeType();
135   }
136 
getMimeType() const137   const std::string& Article::getMimeType() const
138   {
139     return file->getMimeType(getLibraryMimeType());
140   }
141 
isRedirect() const142   bool Article::isRedirect() const
143   {
144     return getDirent()->isRedirect();
145   }
146 
isLinktarget() const147   bool Article::isLinktarget() const
148   {
149     return getDirent()->isLinktarget();
150   }
151 
isDeleted() const152   bool Article::isDeleted() const
153   {
154     return getDirent()->isDeleted();
155   }
156 
getNamespace() const157   char Article::getNamespace() const
158   {
159     return getDirent()->getNamespace();
160   }
161 
getRedirectIndex() const162   article_index_type Article::getRedirectIndex() const
163   {
164     return article_index_type(getDirent()->getRedirectIndex());
165   }
166 
getRedirectArticle() const167   Article Article::getRedirectArticle() const
168   {
169     return Article(file, getRedirectIndex());
170   }
171 
getCluster() const172   std::shared_ptr<const Cluster> Article::getCluster() const
173   {
174     auto dirent = getDirent();
175     if ( !dirent->isArticle() ) {
176       return std::shared_ptr<const Cluster>();
177     }
178     return file->getCluster(dirent->getClusterNumber());
179   }
getClusterNumber() const180   cluster_index_type Article::getClusterNumber() const {
181     auto dirent= getDirent();
182     if ( !dirent->isArticle() ) {
183       return std::numeric_limits<cluster_index_type>::max();
184     }
185     return dirent->getClusterNumber().v;
186 }
187 
getData(offset_type offset) const188   Blob Article::getData(offset_type offset) const
189   {
190     auto size = getArticleSize()-offset;
191     return getData(offset, size);
192   }
193 
getData(offset_type offset,size_type size) const194   Blob Article::getData(offset_type offset, size_type size) const
195   {
196     std::shared_ptr<const Cluster> cluster = getCluster();
197     if (!cluster) {
198       return Blob();
199     }
200     return cluster->getBlob(getDirent()->getBlobNumber(), offset_t(offset), zsize_t(size));
201   }
202 
getOffset() const203   offset_type Article::getOffset() const
204   {
205     auto dirent = getDirent();
206     if ( !dirent->isArticle() )
207         return 0;
208     return offset_type(file->getBlobOffset(dirent->getClusterNumber(), dirent->getBlobNumber()));
209   }
210 
getDirectAccessInformation() const211   std::pair<std::string, offset_type> Article::getDirectAccessInformation() const
212   {
213     auto dirent = getDirent();
214     if ( !dirent->isArticle() ) {
215         return std::make_pair("", 0);
216     }
217 
218     auto full_offset = file->getBlobOffset(dirent->getClusterNumber(),
219                                            dirent->getBlobNumber());
220 
221     if (!full_offset) {
222       // cluster is compressed
223       return std::make_pair("", 0);
224     }
225     auto part_its = file->getFileParts(full_offset, zsize_t(getArticleSize()));
226     auto range = part_its.first->first;
227     auto part = part_its.first->second;
228     if (++part_its.first != part_its.second) {
229       return std::make_pair("", 0);
230     }
231     auto local_offset = full_offset - range.min;
232     return std::make_pair(part->filename(), offset_type(local_offset));
233   }
234 
getPage(bool layout,unsigned maxRecurse)235   std::string Article::getPage(bool layout, unsigned maxRecurse)
236   {
237     std::ostringstream s;
238     getPage(s, layout, maxRecurse);
239     return s.str();
240   }
241 
getPage(std::ostream & out,bool layout,unsigned maxRecurse)242   void Article::getPage(std::ostream& out, bool layout, unsigned maxRecurse)
243   {
244     log_trace("Article::getPage(" << layout << ", " << maxRecurse << ')');
245 
246     if (getMimeType().compare(0, 9, "text/html") == 0 || getMimeType() == MimeHtmlTemplate)
247     {
248       if (layout && file->getFileheader().hasLayoutPage())
249       {
250         Article layoutPage(file, file->getFileheader().getLayoutPage());
251         Blob data = layoutPage.getData();
252 
253         Ev ev(out, *this, file, maxRecurse);
254         log_debug("call template parser");
255         TemplateParser parser(&ev);
256         for (const char* p = data.data(); p != data.end(); ++p)
257           parser.parse(*p);
258         parser.flush();
259 
260         return;
261       }
262       else if (getMimeType() == MimeHtmlTemplate)
263       {
264         Blob data = getData();
265 
266         Ev ev(out, *this, file, maxRecurse);
267         TemplateParser parser(&ev);
268         for (const char* p = data.data(); p != data.end(); ++p)
269           parser.parse(*p);
270         parser.flush();
271 
272         return;
273       }
274     }
275 
276     // default case - template cases has return above
277     out << getData();
278   }
279 
280 }
281