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.
9 #include "DAVDirectory.h"
12 #include "DAVCommon.h"
15 #include "FileItemList.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;
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
);
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
)
103 std::string strRequest
= "PROPFIND";
105 dav
.SetCustomRequest(strRequest
);
106 dav
.SetMimeType("text/xml; charset=\"utf-8\"");
107 dav
.SetRequestHeader("depth", 1);
109 "<?xml version=\"1.0\" encoding=\"utf-8\" ?>"
110 " <D:propfind xmlns:D=\"DAV:\">"
113 " <D:getcontentlength/>"
114 " <D:getlastmodified/>"
122 CLog::LogF(LOGERROR
, "Unable to get dav directory ({})", url
.GetRedacted());
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());
140 // Iterate over all responses
141 for (auto* child
= davResponse
.RootElement()->FirstChild(); child
; child
= child
->NextSibling())
143 if (CDAVCommon::ValueWithoutNamespace(child
, "response"))
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
));
180 bool CDAVDirectory::Create(const CURL
& url
)
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());
199 bool CDAVDirectory::Exists(const CURL
& url
)
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
)
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());