CURL: remove Encode() overload; Encode() return new value instead of modifying parameter,
[vuplus_xbmc] / xbmc / filesystem / MultiPathDirectory.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://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 "threads/SystemClock.h"
22 #include "MultiPathDirectory.h"
23 #include "Directory.h"
24 #include "Util.h"
25 #include "URL.h"
26 #include "guilib/GUIWindowManager.h"
27 #include "dialogs/GUIDialogProgress.h"
28 #include "FileItem.h"
29 #include "utils/StringUtils.h"
30 #include "utils/log.h"
31 #include "utils/TimeUtils.h"
32 #include "utils/URIUtils.h"
33
34 using namespace std;
35 using namespace XFILE;
36
37 //
38 // multipath://{path1}/{path2}/{path3}/.../{path-N}
39 //
40 // unlike the older virtualpath:// protocol, sub-folders are combined together into a new
41 // multipath:// style url.
42 //
43
44 CMultiPathDirectory::CMultiPathDirectory()
45 {}
46
47 CMultiPathDirectory::~CMultiPathDirectory()
48 {}
49
50 bool CMultiPathDirectory::GetDirectory(const CStdString& strPath, CFileItemList &items)
51 {
52   CLog::Log(LOGDEBUG,"CMultiPathDirectory::GetDirectory(%s)", strPath.c_str());
53
54   vector<CStdString> vecPaths;
55   if (!GetPaths(strPath, vecPaths))
56     return false;
57
58   XbmcThreads::EndTime progressTime(3000); // 3 seconds before showing progress bar
59   CGUIDialogProgress* dlgProgress = NULL;
60
61   unsigned int iFailures = 0;
62   for (unsigned int i = 0; i < vecPaths.size(); ++i)
63   {
64     // show the progress dialog if we have passed our time limit
65     if (progressTime.IsTimePast() && !dlgProgress)
66     {
67       dlgProgress = (CGUIDialogProgress *)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
68       if (dlgProgress)
69       {
70         dlgProgress->SetHeading(15310);
71         dlgProgress->SetLine(0, 15311);
72         dlgProgress->SetLine(1, "");
73         dlgProgress->SetLine(2, "");
74         dlgProgress->StartModal();
75         dlgProgress->ShowProgressBar(true);
76         dlgProgress->SetProgressMax((int)vecPaths.size()*2);
77         dlgProgress->Progress();
78       }
79     }
80     if (dlgProgress)
81     {
82       CURL url(vecPaths[i]);
83       dlgProgress->SetLine(1, url.GetWithoutUserDetails());
84       dlgProgress->SetProgressAdvance();
85       dlgProgress->Progress();
86     }
87
88     CFileItemList tempItems;
89     CLog::Log(LOGDEBUG,"Getting Directory (%s)", vecPaths[i].c_str());
90     if (CDirectory::GetDirectory(vecPaths[i], tempItems, m_strFileMask, m_flags))
91       items.Append(tempItems);
92     else
93     {
94       CLog::Log(LOGERROR,"Error Getting Directory (%s)", vecPaths[i].c_str());
95       iFailures++;
96     }
97
98     if (dlgProgress)
99     {
100       dlgProgress->SetProgressAdvance();
101       dlgProgress->Progress();
102     }
103   }
104
105   if (dlgProgress)
106     dlgProgress->Close();
107
108   if (iFailures == vecPaths.size())
109     return false;
110
111   // merge like-named folders into a sub multipath:// style url
112   MergeItems(items);
113
114   return true;
115 }
116
117 bool CMultiPathDirectory::Exists(const char* strPath)
118 {
119   CLog::Log(LOGDEBUG,"Testing Existence (%s)", strPath);
120
121   vector<CStdString> vecPaths;
122   if (!GetPaths(strPath, vecPaths))
123     return false;
124
125   for (unsigned int i = 0; i < vecPaths.size(); ++i)
126   {
127     CLog::Log(LOGDEBUG,"Testing Existence (%s)", vecPaths[i].c_str());
128     if (CDirectory::Exists(vecPaths[i]))
129       return true;
130   }
131   return false;
132 }
133
134 bool CMultiPathDirectory::Remove(const char* strPath)
135 {
136   vector<CStdString> vecPaths;
137   if (!GetPaths(strPath, vecPaths))
138     return false;
139
140   bool success = false;
141   for (unsigned int i = 0; i < vecPaths.size(); ++i)
142   {
143     if (CDirectory::Remove(vecPaths[i]))
144       success = true;
145   }
146   return success;
147 }
148
149 CStdString CMultiPathDirectory::GetFirstPath(const CStdString &strPath)
150 {
151   size_t pos = strPath.find("/", 12);
152   if (pos != std::string::npos)
153   {
154     CStdString firstPath = strPath.substr(12, pos - 12);
155     CURL::Decode(firstPath);
156     return firstPath;
157   }
158   return "";
159 }
160
161 bool CMultiPathDirectory::GetPaths(const CStdString& strPath, vector<CStdString>& vecPaths)
162 {
163   vecPaths.clear();
164   CStdString strPath1 = strPath;
165
166   // remove multipath:// from path and any trailing / (so that the last path doesn't get any more than it originally had)
167   strPath1 = strPath1.substr(12);
168   URIUtils::RemoveSlashAtEnd(strPath1);
169
170   // split on "/"
171   vector<CStdString> vecTemp;
172   StringUtils::SplitString(strPath1, "/", vecTemp);
173   if (vecTemp.size() == 0)
174     return false;
175
176   // check each item
177   for (unsigned int i = 0; i < vecTemp.size(); i++)
178   {
179     CStdString tempPath = vecTemp[i];
180     CURL::Decode(tempPath);
181     vecPaths.push_back(tempPath);
182   }
183   return true;
184 }
185
186 bool CMultiPathDirectory::HasPath(const CStdString& strPath, const CStdString& strPathToFind)
187 {
188   // remove multipath:// from path and any trailing / (so that the last path doesn't get any more than it originally had)
189   CStdString strPath1 = strPath.substr(12);
190   URIUtils::RemoveSlashAtEnd(strPath1);
191
192   // split on "/"
193   vector<CStdString> vecTemp;
194   StringUtils::SplitString(strPath1, "/", vecTemp);
195   if (vecTemp.size() == 0)
196     return false;
197
198   // check each item
199   for (unsigned int i = 0; i < vecTemp.size(); i++)
200   {
201     CStdString tempPath = vecTemp[i];
202     CURL::Decode(tempPath);
203     if(tempPath == strPathToFind)
204       return true;
205   }
206   return false;
207 }
208
209 CStdString CMultiPathDirectory::ConstructMultiPath(const CFileItemList& items, const vector<int> &stack)
210 {
211   // we replace all instances of comma's with double comma's, then separate
212   // the paths using " , "
213   //CLog::Log(LOGDEBUG, "Building multipath");
214   CStdString newPath = "multipath://";
215   //CLog::Log(LOGDEBUG, "-- adding path: %s", strPath.c_str());
216   for (unsigned int i = 0; i < stack.size(); ++i)
217     AddToMultiPath(newPath, items[stack[i]]->GetPath());
218
219   //CLog::Log(LOGDEBUG, "Final path: %s", newPath.c_str());
220   return newPath;
221 }
222
223 void CMultiPathDirectory::AddToMultiPath(CStdString& strMultiPath, const CStdString& strPath)
224 {
225   CStdString strPath1 = strPath;
226   URIUtils::AddSlashAtEnd(strMultiPath);
227   //CLog::Log(LOGDEBUG, "-- adding path: %s", strPath.c_str());
228   strPath1 = CURL::Encode(strPath1);
229   strMultiPath += strPath1;
230   strMultiPath += "/";
231 }
232
233 CStdString CMultiPathDirectory::ConstructMultiPath(const vector<CStdString> &vecPaths)
234 {
235   // we replace all instances of comma's with double comma's, then separate
236   // the paths using " , "
237   //CLog::Log(LOGDEBUG, "Building multipath");
238   CStdString newPath = "multipath://";
239   //CLog::Log(LOGDEBUG, "-- adding path: %s", strPath.c_str());
240   for (unsigned int i = 0; i < vecPaths.size(); ++i)
241     AddToMultiPath(newPath, vecPaths[i]);
242   //CLog::Log(LOGDEBUG, "Final path: %s", newPath.c_str());
243   return newPath;
244 }
245
246 CStdString CMultiPathDirectory::ConstructMultiPath(const std::set<CStdString> &setPaths)
247 {
248   CStdString newPath = "multipath://";
249   for (std::set<CStdString>::const_iterator path = setPaths.begin(); path != setPaths.end(); ++path)
250     AddToMultiPath(newPath, *path);
251
252   return newPath;
253 }
254
255 void CMultiPathDirectory::MergeItems(CFileItemList &items)
256 {
257   CLog::Log(LOGDEBUG, "CMultiPathDirectory::MergeItems, items = %i", (int)items.Size());
258   unsigned int time = XbmcThreads::SystemClockMillis();
259   if (items.Size() == 0)
260     return;
261   // sort items by label
262   // folders are before files in this sort method
263   items.Sort(SortByLabel, SortOrderAscending);
264   int i = 0;
265
266   // if first item in the sorted list is a file, just abort
267   if (!items.Get(i)->m_bIsFolder)
268     return;
269
270   while (i + 1 < items.Size())
271   {
272     // there are no more folders left, so exit the loop
273     CFileItemPtr pItem1 = items.Get(i);
274     if (!pItem1->m_bIsFolder)
275       break;
276
277     vector<int> stack;
278     stack.push_back(i);
279     CLog::Log(LOGDEBUG,"Testing path: [%03i] %s", i, pItem1->GetPath().c_str());
280
281     int j = i + 1;
282     do
283     {
284       CFileItemPtr pItem2 = items.Get(j);
285       if (!pItem2->GetLabel().Equals(pItem1->GetLabel()))
286         break;
287
288       // ignore any filefolders which may coincidently have
289       // the same label as a true folder
290       if (!pItem2->IsFileFolder())
291       {
292         stack.push_back(j);
293         CLog::Log(LOGDEBUG,"  Adding path: [%03i] %s", j, pItem2->GetPath().c_str());
294       }
295       j++;
296     }
297     while (j < items.Size());
298
299     // do we have anything to combine?
300     if (stack.size() > 1)
301     {
302       // we have a multipath so remove the items and add the new item
303       CStdString newPath = ConstructMultiPath(items, stack);
304       for (unsigned int k = stack.size() - 1; k > 0; --k)
305         items.Remove(stack[k]);
306       pItem1->SetPath(newPath);
307       CLog::Log(LOGDEBUG,"  New path: %s", pItem1->GetPath().c_str());
308     }
309
310     i++;
311   }
312
313   CLog::Log(LOGDEBUG,
314             "CMultiPathDirectory::MergeItems, items = %i,  took %d ms",
315             items.Size(), XbmcThreads::SystemClockMillis() - time);
316 }
317
318 bool CMultiPathDirectory::SupportsWriteFileOperations(const CStdString &strPath)
319 {
320   vector<CStdString> paths;
321   GetPaths(strPath, paths);
322   for (unsigned int i = 0; i < paths.size(); ++i)
323     if (CUtil::SupportsWriteFileOperations(paths[i]))
324       return true;
325   return false;
326 }