Merge pull request #25808 from CastagnaIT/fix_url_parse
[xbmc.git] / xbmc / filesystem / DAVDirectory.cpp
blob08905fca31545ca68e0f2400f06310cf69500cb7
1 /*
2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
9 #include "DAVDirectory.h"
11 #include "CurlFile.h"
12 #include "DAVCommon.h"
13 #include "DAVFile.h"
14 #include "FileItem.h"
15 #include "FileItemList.h"
16 #include "URL.h"
17 #include "utils/StringUtils.h"
18 #include "utils/URIUtils.h"
19 #include "utils/XBMCTinyXML2.h"
20 #include "utils/log.h"
22 using namespace XFILE;
24 CDAVDirectory::CDAVDirectory(void) = default;
25 CDAVDirectory::~CDAVDirectory(void) = default;
28 * Parses a <response>
30 * <!ELEMENT response (href, ((href*, status)|(propstat+)), responsedescription?) >
31 * <!ELEMENT propstat (prop, status, responsedescription?) >
34 void CDAVDirectory::ParseResponse(const tinyxml2::XMLElement* element, CFileItem& item)
36 /* Iterate response children elements */
37 for (auto* responseChild = element->FirstChildElement(); responseChild;
38 responseChild = responseChild->NextSiblingElement())
40 if (CDAVCommon::ValueWithoutNamespace(responseChild, "href") && !responseChild->NoChildren())
42 std::string path(responseChild->FirstChild()->Value());
43 URIUtils::RemoveSlashAtEnd(path);
44 item.SetPath(path);
46 else if (CDAVCommon::ValueWithoutNamespace(responseChild, "propstat"))
48 if (CDAVCommon::GetStatusTag(responseChild->ToElement()).find("200 OK") != std::string::npos)
50 /* Iterate propstat children elements */
51 for (auto* propstatChild = responseChild->FirstChild(); propstatChild;
52 propstatChild = propstatChild->NextSibling())
54 if (CDAVCommon::ValueWithoutNamespace(propstatChild, "prop"))
56 /* Iterate all properties available */
57 for (auto* propChild = propstatChild->FirstChildElement(); propChild;
58 propChild = propChild->NextSiblingElement())
60 if (CDAVCommon::ValueWithoutNamespace(propChild, "getcontentlength") &&
61 !propChild->NoChildren())
63 item.m_dwSize = strtoll(propChild->FirstChild()->Value(), NULL, 10);
65 else if (CDAVCommon::ValueWithoutNamespace(propChild, "getlastmodified") &&
66 !propChild->NoChildren())
68 struct tm timeDate = {};
69 strptime(propChild->FirstChild()->Value(), "%a, %d %b %Y %T", &timeDate);
70 item.m_dateTime = mktime(&timeDate);
72 else if (CDAVCommon::ValueWithoutNamespace(propChild, "displayname") &&
73 !propChild->NoChildren())
75 item.SetLabel(CURL::Decode(propChild->FirstChild()->Value()));
77 else if (!item.m_dateTime.IsValid() &&
78 CDAVCommon::ValueWithoutNamespace(propChild, "creationdate") &&
79 !propChild->NoChildren())
81 struct tm timeDate = {};
82 strptime(propChild->FirstChild()->Value(), "%Y-%m-%dT%T", &timeDate);
83 item.m_dateTime = mktime(&timeDate);
85 else if (CDAVCommon::ValueWithoutNamespace(propChild, "resourcetype"))
87 if (CDAVCommon::ValueWithoutNamespace(propChild->FirstChild(), "collection"))
89 item.m_bIsFolder = true;
100 bool CDAVDirectory::GetDirectory(const CURL& url, CFileItemList &items)
102 CCurlFile dav;
103 std::string strRequest = "PROPFIND";
105 dav.SetCustomRequest(strRequest);
106 dav.SetMimeType("text/xml; charset=\"utf-8\"");
107 dav.SetRequestHeader("depth", 1);
108 dav.SetPostData(
109 "<?xml version=\"1.0\" encoding=\"utf-8\" ?>"
110 " <D:propfind xmlns:D=\"DAV:\">"
111 " <D:prop>"
112 " <D:resourcetype/>"
113 " <D:getcontentlength/>"
114 " <D:getlastmodified/>"
115 " <D:creationdate/>"
116 " <D:displayname/>"
117 " </D:prop>"
118 " </D:propfind>");
120 if (!dav.Open(url))
122 CLog::LogF(LOGERROR, "Unable to get dav directory ({})", url.GetRedacted());
123 return false;
126 std::string strResponse;
127 dav.ReadData(strResponse);
129 std::string fileCharset(dav.GetProperty(XFILE::FILE_PROPERTY_CONTENT_CHARSET));
130 CXBMCTinyXML2 davResponse;
131 davResponse.Parse(strResponse);
133 if (!davResponse.Parse(strResponse))
135 CLog::LogF(LOGERROR, "Unable to process dav directory ({})", url.GetRedacted());
136 dav.Close();
137 return false;
140 // Iterate over all responses
141 for (auto* child = davResponse.RootElement()->FirstChild(); child; child = child->NextSibling())
143 if (CDAVCommon::ValueWithoutNamespace(child, "response"))
145 CFileItem item;
146 ParseResponse(child->ToElement(), item);
147 const CURL& url2(url);
148 CURL url3(item.GetPath());
150 std::string itemPath(URIUtils::AddFileToFolder(url2.GetWithoutFilename(), url3.GetFileName()));
152 if (item.GetLabel().empty())
154 std::string name(itemPath);
155 URIUtils::RemoveSlashAtEnd(name);
156 item.SetLabel(CURL::Decode(URIUtils::GetFileName(name)));
159 if (item.m_bIsFolder)
160 URIUtils::AddSlashAtEnd(itemPath);
162 // Add back protocol options
163 if (!url2.GetProtocolOptions().empty())
164 itemPath += "|" + url2.GetProtocolOptions();
165 item.SetPath(itemPath);
167 if (!item.IsURL(url))
169 CFileItemPtr pItem(new CFileItem(item));
170 items.Add(pItem);
175 dav.Close();
177 return true;
180 bool CDAVDirectory::Create(const CURL& url)
182 CDAVFile dav;
183 std::string strRequest = "MKCOL";
185 dav.SetCustomRequest(strRequest);
187 if (!dav.Execute(url))
189 CLog::Log(LOGERROR, "{} - Unable to create dav directory ({}) - {}", __FUNCTION__,
190 url.GetRedacted(), dav.GetLastResponseCode());
191 return false;
194 dav.Close();
196 return true;
199 bool CDAVDirectory::Exists(const CURL& url)
201 CCurlFile dav;
203 // Set the PROPFIND custom request else we may not find folders, depending
204 // on the server's configuration
205 std::string strRequest = "PROPFIND";
206 dav.SetCustomRequest(strRequest);
207 dav.SetRequestHeader("depth", 0);
209 return dav.Exists(url);
212 bool CDAVDirectory::Remove(const CURL& url)
214 CDAVFile dav;
215 std::string strRequest = "DELETE";
217 dav.SetCustomRequest(strRequest);
219 if (!dav.Execute(url))
221 CLog::Log(LOGERROR, "{} - Unable to delete dav directory ({}) - {}", __FUNCTION__,
222 url.GetRedacted(), dav.GetLastResponseCode());
223 return false;
226 dav.Close();
228 return true;