1 /*
2 * This File is part of Davix, The IO library for HTTP based protocols
3 * Copyright (C) CERN 2013
4 * Author: Adrien Devresse <adrien.devresse@cern.ch>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 *
20 */
21
22 #include <davix_internal.hpp>
23 #include "davpropxmlparser.hpp"
24
25 #include <utils/davix_logger_internal.hpp>
26 #include <status/davixstatusrequest.hpp>
27 #include <datetime/datetime_utils.hpp>
28 #include <string_utils/stringutils.hpp>
29
30 using namespace StrUtil;
31
32 namespace Davix {
33
34 const Xml::XmlPTree prop_node(Xml::ElementStart, "propstat");
35 const Xml::XmlPTree prop_collection(Xml::ElementStart, "collection");
36 static std::unique_ptr<Xml::XmlPTree> webDavTree;
37
38 static std::once_flag _l_init;
39
40 struct DavPropXMLParser::DavxPropXmlIntern{
DavxPropXmlInternDavix::DavPropXMLParser::DavxPropXmlIntern41 DavxPropXmlIntern() : _stack(),
42 _props(), _current_props(), _last_response_status(500), _last_filename(){
43 _stack.reserve(10);
44 char_buffer.reserve(1024);
45 }
46
47 // node stack
48 std::vector<Xml::XmlPTree> _stack;
49
50 // props
51 std::deque<FileProperties> _props;
52 FileProperties _current_props;
53 int _last_response_status;
54 std::string _last_filename;
55
56 // buffer
57 std::string char_buffer;
58
appendCharsDavix::DavPropXMLParser::DavxPropXmlIntern59 inline void appendChars(const char *buff, size_t len){
60 char_buffer.append(std::string(buff, len));
61 }
62
clearDavix::DavPropXMLParser::DavxPropXmlIntern63 inline void clear(){
64 char_buffer.clear();
65 }
66
67
add_new_elemDavix::DavPropXMLParser::DavxPropXmlIntern68 inline void add_new_elem(){
69 DAVIX_SLOG(DAVIX_LOG_DEBUG, DAVIX_LOG_XML, " properties detected ");
70 _current_props.clear();
71 _current_props.filename = _last_filename; // setup the current filename
72 _current_props.info.mode = 0777 | S_IFREG; // default : fake access to everything
73 }
74
store_new_elemDavix::DavPropXMLParser::DavxPropXmlIntern75 inline void store_new_elem(){
76 DAVIX_SLOG(DAVIX_LOG_DEBUG, DAVIX_LOG_XML, " end of properties... ");
77 if( _last_response_status > 100
78 && _last_response_status < 400){
79 _props.push_back(_current_props);
80 }else{
81 DAVIX_SLOG(DAVIX_LOG_DEBUG, DAVIX_LOG_XML, "Bad status code ! properties dropped");
82 }
83 }
84
85 };
86
87
88 typedef void (*properties_cb)(DavPropXMLParser::DavxPropXmlIntern & par, const std::string & name);
89
90
check_last_modified(DavPropXMLParser::DavxPropXmlIntern & par,const std::string & name)91 static void check_last_modified(DavPropXMLParser::DavxPropXmlIntern & par, const std::string & name){
92 DAVIX_SLOG(DAVIX_LOG_DEBUG, DAVIX_LOG_XML, " getlastmodified found -> parse it ");
93 time_t t = parse_standard_date(name.c_str());
94 if(t == -1){
95 DAVIX_SLOG(DAVIX_LOG_VERBOSE, DAVIX_LOG_XML, " getlastmodified parsing error : corrupted value ... ignored");
96 t = 0;
97 }
98 DAVIX_SLOG(DAVIX_LOG_DEBUG, DAVIX_LOG_XML, " getlastmodified found -> value {} ", t);
99 par._current_props.info.mtime = t;
100 }
101
102
check_creation_date(DavPropXMLParser::DavxPropXmlIntern & par,const std::string & name)103 static void check_creation_date(DavPropXMLParser::DavxPropXmlIntern & par, const std::string & name){
104 DAVIX_SLOG(DAVIX_LOG_DEBUG, DAVIX_LOG_XML, "creationdate found -> parse it");
105 time_t t = parse_standard_date(name.c_str());
106 if(t == -1){
107 DAVIX_SLOG(DAVIX_LOG_VERBOSE, DAVIX_LOG_XML, " creationdate parsing error : corrupted value ... ignored");
108 t = 0;
109 }
110 DAVIX_SLOG(DAVIX_LOG_DEBUG, DAVIX_LOG_XML, " creationdate found -> value {} ", t);
111 par._current_props.info.ctime = t;
112 }
113
check_is_directory(DavPropXMLParser::DavxPropXmlIntern & par,const std::string & name)114 static void check_is_directory(DavPropXMLParser::DavxPropXmlIntern & par, const std::string & name){
115 (void) name;
116 DAVIX_SLOG(DAVIX_LOG_DEBUG, DAVIX_LOG_XML, " directory pattern found -> set flag IS_DIR");
117 par._current_props.info.mode |= S_IFDIR;
118 par._current_props.info.mode &= ~(S_IFREG);
119 }
120
121
check_content_length(DavPropXMLParser::DavxPropXmlIntern & par,const std::string & name)122 static void check_content_length(DavPropXMLParser::DavxPropXmlIntern & par, const std::string & name){
123 DAVIX_SLOG(DAVIX_LOG_DEBUG, DAVIX_LOG_XML, " content length found -> parse it");
124 try{
125 const unsigned long mysize = toType<unsigned long, std::string>()(name);
126 DAVIX_SLOG(DAVIX_LOG_DEBUG, DAVIX_LOG_XML, " content length found -> {}", mysize);
127 par._current_props.info.size = static_cast<off_t>(mysize);
128 }catch(...){
129 DAVIX_SLOG(DAVIX_LOG_VERBOSE, DAVIX_LOG_XML, " Invalid content length value in dav response");
130 }
131 }
132
check_quota_used_bytes(DavPropXMLParser::DavxPropXmlIntern & par,const std::string & name)133 static void check_quota_used_bytes(DavPropXMLParser::DavxPropXmlIntern & par, const std::string & name){
134 DAVIX_SLOG(DAVIX_LOG_DEBUG, DAVIX_LOG_XML, " quota used bytes found -> parse it");
135 try{
136 const unsigned long mysize = toType<unsigned long, std::string>()(name);
137 DAVIX_SLOG(DAVIX_LOG_DEBUG, DAVIX_LOG_XML, " quota used bytes found -> {}", mysize);
138 par._current_props.info.size = static_cast<off_t>(mysize);
139 par._current_props.quota.used_bytes = static_cast<off_t>(mysize);
140 }catch(...){
141 DAVIX_SLOG(DAVIX_LOG_VERBOSE, DAVIX_LOG_XML, " Invalid quota used bytes in dav response");
142 }
143 }
144
check_quota_free_space(DavPropXMLParser::DavxPropXmlIntern & par,const std::string & name)145 static void check_quota_free_space(DavPropXMLParser::DavxPropXmlIntern & par, const std::string & name){
146 DAVIX_SLOG(DAVIX_LOG_DEBUG, DAVIX_LOG_XML, " quota free space found -> parse it");
147 try{
148 const unsigned long mysize = toType<unsigned long, std::string>()(name);
149 DAVIX_SLOG(DAVIX_LOG_DEBUG, DAVIX_LOG_XML, " quota free space found -> {}", mysize);
150 par._current_props.quota.free_space = static_cast<off_t>(mysize);
151 }catch(...){
152 DAVIX_SLOG(DAVIX_LOG_VERBOSE, DAVIX_LOG_XML, " Invalid quota free space in dav response");
153 }
154 }
155
check_mode_ext(DavPropXMLParser::DavxPropXmlIntern & par,const std::string & name)156 static void check_mode_ext(DavPropXMLParser::DavxPropXmlIntern & par, const std::string & name){
157 DAVIX_SLOG(DAVIX_LOG_DEBUG, DAVIX_LOG_XML, "mode_t extension for LCGDM found -> parse it");
158 const unsigned long mymode = strtoul(name.c_str(), NULL, 8);
159 if(mymode == ULONG_MAX){
160 DAVIX_SLOG(DAVIX_LOG_VERBOSE, DAVIX_LOG_XML, "Invalid mode_t value for the LCGDM extension");
161 errno =0;
162 return;
163 }
164 DAVIX_SLOG(DAVIX_LOG_DEBUG, DAVIX_LOG_XML, fmt::sprintf(" mode_t extension found -> 0%o", (mode_t) mymode).c_str());
165 par._current_props.info.mode = (mode_t) mymode;
166 }
167
startswith(const std::string & str,const std::string & prefix)168 static bool startswith(const std::string &str, const std::string &prefix) {
169 if(prefix.size() > str.size()) return false;
170
171 for(size_t i = 0; i < prefix.size(); i++) {
172 if(str[i] != prefix[i]) return false;
173 }
174 return true;
175 }
176
check_href(DavPropXMLParser::DavxPropXmlIntern & par,const std::string & name)177 static void check_href(DavPropXMLParser::DavxPropXmlIntern & par, const std::string & name){
178 std::string _href(name);
179 rtrim(_href, isSlash()); // remove trailing slash
180 std::string::reverse_iterator it = std::find(_href.rbegin(), _href.rend(), '/');
181 if( it == _href.rend()){
182 par._last_filename.assign(_href);
183 }else{
184 par._last_filename.assign(it.base(), _href.end());
185
186 if(startswith(name, "https://") || startswith(name, "http://") || startswith(name, "://") || startswith(name, "dav://") || startswith(name, "davs://")) {
187 par._last_filename = Uri::unescapeString(par._last_filename);
188 }
189 }
190 DAVIX_SLOG(DAVIX_LOG_DEBUG, DAVIX_LOG_XML, " href/filename parsed -> {} ", par._last_filename.c_str() );
191 }
192
check_status(DavPropXMLParser::DavxPropXmlIntern & par,const std::string & name)193 static void check_status(DavPropXMLParser::DavxPropXmlIntern & par, const std::string & name){
194 DAVIX_SLOG(DAVIX_LOG_DEBUG, DAVIX_LOG_XML, " status found -> parse it");
195 std::string str_status(name);
196 ltrim(str_status, StrUtil::isSpace());
197 std::string::iterator it1, it2;
198 it1 = std::find(str_status.begin(), str_status.end(), ' ');
199 if( it1 != str_status.end()){
200 it2 = std::find(it1+1, str_status.end(), ' ');
201 std::string str_status_parsed(it1+1, it2);
202 unsigned long res = strtoul(str_status_parsed.c_str(), NULL, 10);
203 if(res != ULONG_MAX){
204 DAVIX_SLOG(DAVIX_LOG_DEBUG, DAVIX_LOG_XML, " status value : {}", res);
205 par._last_response_status = res;
206 return;
207 }
208 }
209 DAVIX_SLOG(DAVIX_LOG_VERBOSE, DAVIX_LOG_XML, "Invalid dav status field value");
210 errno =0;
211 }
212
check_owner_uid(DavPropXMLParser::DavxPropXmlIntern & par,const std::string & value)213 static void check_owner_uid(DavPropXMLParser::DavxPropXmlIntern & par, const std::string & value){
214 DAVIX_SLOG(DAVIX_LOG_DEBUG, DAVIX_LOG_XML, " owner found -> parse it");
215 std::string str_owner(value);
216 ltrim(str_owner, StrUtil::isSpace());
217 unsigned long res = strtoul(str_owner.c_str(), NULL, 10);
218 if(res != ULONG_MAX){
219 DAVIX_SLOG(DAVIX_LOG_DEBUG, DAVIX_LOG_XML, " owner value : {}", res);
220 par._current_props.info.owner = res;
221 return;
222 }
223 DAVIX_SLOG(DAVIX_LOG_VERBOSE, DAVIX_LOG_XML, "Invalid owner field value");
224 }
225
check_group_gid(DavPropXMLParser::DavxPropXmlIntern & par,const std::string & value)226 static void check_group_gid(DavPropXMLParser::DavxPropXmlIntern & par, const std::string & value){
227 DAVIX_SLOG(DAVIX_LOG_DEBUG, DAVIX_LOG_XML, " group found -> parse it");
228 std::string str_group(value);
229 ltrim(str_group, StrUtil::isSpace());
230 unsigned long res = strtoul(str_group.c_str(), NULL, 10);
231 if(res != ULONG_MAX){
232 DAVIX_SLOG(DAVIX_LOG_DEBUG, DAVIX_LOG_XML, " group value : {}", res);
233 par._current_props.info.group = res;
234 return;
235 }
236 DAVIX_SLOG(DAVIX_LOG_VERBOSE, DAVIX_LOG_XML, "Invalid group field value");
237 }
238
init_webdavTree()239 void init_webdavTree(){
240
241 // Nodes list
242 webDavTree.reset(new Xml::XmlPTree(Xml::ElementStart, "multistatus"));
243 webDavTree->addChild(Xml::XmlPTree(Xml::ElementStart, "response"));
244 Xml::XmlPTree::iterator it = webDavTree->beginChildren();
245 it->addChild(Xml::XmlPTree(Xml::ElementStart, "href", Xml::XmlPTree::ChildrenList(), (void*) &check_href));
246 it->addChild(Xml::XmlPTree(Xml::ElementStart, "propstat"));
247 it = (--it->endChildren());
248 it->addChild(Xml::XmlPTree(Xml::ElementStart, "status", Xml::XmlPTree::ChildrenList(), (void*) &check_status));
249 it->addChild(Xml::XmlPTree(Xml::ElementStart, "prop"));
250 it = (--it->endChildren());
251
252 it->addChild(Xml::XmlPTree(Xml::ElementStart, "getlastmodified", Xml::XmlPTree::ChildrenList(), (void*) &check_last_modified));
253 it->addChild(Xml::XmlPTree(Xml::ElementStart, "creationdate", Xml::XmlPTree::ChildrenList(), (void*) &check_creation_date));
254 it->addChild(Xml::XmlPTree(Xml::ElementStart, "quota-used-bytes", Xml::XmlPTree::ChildrenList(), (void*) &check_quota_used_bytes));
255 it->addChild(Xml::XmlPTree(Xml::ElementStart, "quota-available-bytes", Xml::XmlPTree::ChildrenList(), (void*) &check_quota_free_space));
256 it->addChild(Xml::XmlPTree(Xml::ElementStart, "getcontentlength", Xml::XmlPTree::ChildrenList(), (void*) &check_content_length));
257
258 it->addChild(Xml::XmlPTree(Xml::ElementStart, "owner", Xml::XmlPTree::ChildrenList(), (void*) &check_owner_uid));
259 it->addChild(Xml::XmlPTree(Xml::ElementStart, "group", Xml::XmlPTree::ChildrenList(), (void*) &check_group_gid));
260 it->addChild(Xml::XmlPTree(Xml::ElementStart, "mode", Xml::XmlPTree::ChildrenList(), (void*) &check_mode_ext));
261 it->addChild(Xml::XmlPTree(Xml::ElementStart, "resourcetype"));
262
263 it = (--it->endChildren());
264 it->addChild(Xml::XmlPTree(Xml::ElementStart, "collection", Xml::XmlPTree::ChildrenList(), (void*) &check_is_directory));
265 }
266
DavPropXMLParser()267 DavPropXMLParser::DavPropXMLParser() :
268 d_ptr(new DavxPropXmlIntern())
269 {
270 std::call_once(_l_init, init_webdavTree);
271 }
272
~DavPropXMLParser()273 DavPropXMLParser::~DavPropXMLParser(){
274 delete d_ptr;
275 }
276
277
getProperties()278 std::deque<FileProperties> & DavPropXMLParser::getProperties(){
279 return d_ptr->_props;
280 }
281
282
parserStartElemCb(int parent,const char * nspace,const char * name,const char ** atts)283 int DavPropXMLParser::parserStartElemCb(int parent, const char *nspace, const char *name, const char **atts){
284 (void) parent;
285 (void) name;
286 (void) nspace;
287 (void) atts;
288 // add elem to stack
289 Xml::XmlPTree node(Xml::ElementStart, name);
290 d_ptr->_stack.push_back(node);
291
292 // if beginning of prop, add new element
293 if(node.compareNode(prop_node)){
294 d_ptr->add_new_elem();
295 }
296 return 1;
297 }
298
299
parserCdataCb(int state,const char * cdata,size_t len)300 int DavPropXMLParser::parserCdataCb(int state, const char *cdata, size_t len){
301 (void) state;
302 d_ptr->appendChars(cdata, len);
303 return 0;
304 }
305
306
parserEndElemCb(int state,const char * nspace,const char * name)307 int DavPropXMLParser::parserEndElemCb(int state, const char *nspace, const char *name){
308 (void) state;
309 (void) nspace;
310 Xml::XmlPTree node(Xml::ElementStart, name);
311
312 // find potential interesting data
313 std::vector<Xml::XmlPTree::ptr_type> chain;
314 if(d_ptr->char_buffer.size() != 0 || node.compareNode(prop_collection)){
315 chain = webDavTree->findChain(d_ptr->_stack);
316 if(chain.size() > 0){
317 properties_cb cb = ((properties_cb) chain.at(chain.size()-1)->getMeta());
318 if(cb){
319 StrUtil::trim(d_ptr->char_buffer);
320 cb(*d_ptr, d_ptr->char_buffer);
321 }
322 }
323 }
324
325 // push props
326 if(node.compareNode(prop_node)){
327 d_ptr->store_new_elem();
328 }
329
330 // cleaning work
331 if(d_ptr->_stack.size() == 0)
332 throw DavixException(davix_scope_xml_parser(),StatusCode::ParsingError, "Corrupted Parser Stack, Invalid XML");
333 d_ptr->_stack.pop_back();
334 d_ptr->clear();
335 return 0;
336 }
337
338 } // namespace Davix
339