[cosmetics] update date in GPL header
[vuplus_xbmc] / xbmc / filesystem / DAVDirectory.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://www.xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, see
17  *  <http://www.gnu.org/licenses/>.
18  *
19  */
20
21 #include "DAVDirectory.h"
22 #include "URL.h"
23 #include "CurlFile.h"
24 #include "FileItem.h"
25 #include "utils/RegExp.h"
26 #include "utils/StringUtils.h"
27 #include "utils/CharsetConverter.h"
28 #include "utils/log.h"
29 #include "utils/URIUtils.h"
30
31 using namespace XFILE;
32
33 CDAVDirectory::CDAVDirectory(void) {}
34 CDAVDirectory::~CDAVDirectory(void) {}
35
36 /*
37  * Return true if pElement value is equal value without namespace.
38  *
39  * if pElement is <DAV:foo> and value is foo then ValueWithoutNamespace is true
40  */
41 bool CDAVDirectory::ValueWithoutNamespace(const TiXmlNode *pNode, CStdString value)
42 {
43   CStdStringArray result;
44   const TiXmlElement *pElement;
45
46   if (!pNode)
47   {
48     return false;
49   }
50
51   pElement = pNode->ToElement();
52
53   if (!pElement)
54   {
55     return false;
56   }
57
58   StringUtils::SplitString(pElement->Value(), ":", result, 2);
59
60   if (result.size() == 1 && result[0] == value)
61   {
62     return true;
63   }
64   else if (result.size() == 2 && result[1] == value)
65   {
66     return true;
67   }
68   else if (result.size() > 2)
69   {
70     CLog::Log(LOGERROR, "%s - Splitting %s failed, size(): %lu, value: %s", __FUNCTION__, pElement->Value(), (unsigned long int)result.size(), value.c_str());
71   }
72
73   return false;
74 }
75
76 /*
77  * Search for <status> and return its content
78  */
79 CStdString CDAVDirectory::GetStatusTag(const TiXmlElement *pElement)
80 {
81   const TiXmlElement *pChild;
82
83   for (pChild = pElement->FirstChild()->ToElement(); pChild != 0; pChild = pChild->NextSibling()->ToElement())
84   {
85     if (ValueWithoutNamespace(pChild, "status"))
86     {
87       return CStdString(pChild->GetText());
88     }
89   }
90
91   return CStdString("");
92 }
93
94 /*
95  * Parses a <response>
96  *
97  * <!ELEMENT response (href, ((href*, status)|(propstat+)), responsedescription?) >
98  * <!ELEMENT propstat (prop, status, responsedescription?) >
99  *
100  */
101 void CDAVDirectory::ParseResponse(const TiXmlElement *pElement, CFileItem &item)
102 {
103   const TiXmlNode *pResponseChild;
104   const TiXmlNode *pPropstatChild;
105   const TiXmlNode *pPropChild;
106
107   /* Iterate response children elements */
108   for (pResponseChild = pElement->FirstChild(); pResponseChild != 0; pResponseChild = pResponseChild->NextSibling())
109   {
110     if (ValueWithoutNamespace(pResponseChild, "href"))
111     {
112       CStdString path(pResponseChild->ToElement()->GetText());
113       URIUtils::RemoveSlashAtEnd(path);
114       item.SetPath(path);
115     }
116     else 
117     if (ValueWithoutNamespace(pResponseChild, "propstat"))
118     {
119       if (GetStatusTag(pResponseChild->ToElement()) == "HTTP/1.1 200 OK")
120       {
121         /* Iterate propstat children elements */
122         for (pPropstatChild = pResponseChild->FirstChild(); pPropstatChild != 0; pPropstatChild = pPropstatChild->NextSibling())
123         {
124           if (ValueWithoutNamespace(pPropstatChild, "prop"))
125           {
126             /* Iterate all properties available */
127             for (pPropChild = pPropstatChild->FirstChild(); pPropChild != 0; pPropChild = pPropChild->NextSibling())
128             {
129               if (ValueWithoutNamespace(pPropChild, "getcontentlength"))
130               {
131                 item.m_dwSize = strtoll(pPropChild->ToElement()->GetText(), NULL, 10);
132               }
133               else
134               if (ValueWithoutNamespace(pPropChild, "getlastmodified"))
135               {
136                 struct tm timeDate = {0};
137                 strptime(pPropChild->ToElement()->GetText(), "%a, %d %b %Y %T", &timeDate);
138                 item.m_dateTime = mktime(&timeDate);
139               }
140               else
141               if (ValueWithoutNamespace(pPropChild, "displayname"))
142               {
143                 item.SetLabel(pPropChild->ToElement()->GetText());
144               }
145               else
146               if (!item.m_dateTime.IsValid() && ValueWithoutNamespace(pPropChild, "creationdate"))
147               {
148                 struct tm timeDate = {0};
149                 strptime(pPropChild->ToElement()->GetText(), "%Y-%m-%dT%T", &timeDate);
150                 item.m_dateTime = mktime(&timeDate);
151               }
152               else 
153               if (ValueWithoutNamespace(pPropChild, "resourcetype"))
154               {
155                 if (ValueWithoutNamespace(pPropChild->FirstChild(), "collection"))
156                 {
157                   item.m_bIsFolder = true;
158                 }
159               }
160             }
161           }
162         }
163       }
164     }
165   }
166 }
167
168 bool CDAVDirectory::GetDirectory(const CStdString& strPath, CFileItemList &items)
169 {
170   CCurlFile dav;
171   CURL url(strPath);
172   CStdString strRequest = "PROPFIND";
173
174   dav.SetCustomRequest(strRequest);
175   dav.SetMimeType("text/xml; charset=\"utf-8\"");
176   dav.SetRequestHeader("depth", 1);
177   dav.SetPostData(
178     "<?xml version=\"1.0\" encoding=\"utf-8\" ?>"
179     " <D:propfind xmlns:D=\"DAV:\">"
180     "   <D:prop>"
181     "     <D:resourcetype/>"
182     "     <D:getcontentlength/>"
183     "     <D:getlastmodified/>"
184     "     <D:creationdate/>"
185     "     <D:displayname/>"
186     "    </D:prop>"
187     "  </D:propfind>");
188
189   if (!dav.Open(url))
190   {
191     CLog::Log(LOGERROR, "%s - Unable to get dav directory (%s)", __FUNCTION__, strPath.c_str());
192     return false;
193   }
194
195   CStdString strResponse;
196   dav.ReadData(strResponse);
197
198   CXBMCTinyXML davResponse;
199   davResponse.Parse(strResponse.c_str());
200
201   if (!davResponse.Parse(strResponse))
202   {
203     CLog::Log(LOGERROR, "%s - Unable to process dav directory (%s)", __FUNCTION__, strPath.c_str());
204     dav.Close();
205     return false;
206   }
207
208   TiXmlNode *pChild;
209   // Iterate over all responses
210   for (pChild = davResponse.RootElement()->FirstChild(); pChild != 0; pChild = pChild->NextSibling())
211   {
212     if (ValueWithoutNamespace(pChild, "response"))
213     {
214       CFileItem item;
215       ParseResponse(pChild->ToElement(), item);
216       CURL url2(strPath);
217       CURL url3(item.GetPath());
218
219       CStdString itemPath(URIUtils::AddFileToFolder(url2.GetWithoutFilename(), url3.GetFileName()));
220
221       if (item.GetLabel().IsEmpty())
222       {
223         CStdString name(itemPath);
224         URIUtils::RemoveSlashAtEnd(name);
225         CURL::Decode(name);
226         item.SetLabel(URIUtils::GetFileName(name));
227       }
228
229       if (item.m_bIsFolder)
230         URIUtils::AddSlashAtEnd(itemPath);
231
232       // Add back protocol options
233       if (!url2.GetProtocolOptions().IsEmpty())
234         itemPath += "|" + url2.GetProtocolOptions();
235       item.SetPath(itemPath);
236
237       if (!item.GetPath().Equals(strPath))
238       {
239         CFileItemPtr pItem(new CFileItem(item));
240         items.Add(pItem);
241       }
242     }
243   }
244
245   dav.Close();
246
247   return true;
248 }
249
250 bool CDAVDirectory::Exists(const char* strPath)
251 {
252   CCurlFile dav;
253
254   // Set the PROPFIND custom request else we may not find folders, depending
255   // on the server's configuration
256   CStdString strRequest = "PROPFIND";
257   dav.SetCustomRequest(strRequest);
258
259   CURL url(strPath);
260   return dav.Exists(url);
261 }