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.
11 #include "DirectoryCache.h"
12 #include "DirectoryFactory.h"
13 #include "FileDirectoryFactory.h"
15 #include "PasswordManager.h"
16 #include "ServiceBroker.h"
18 #include "commons/Exception.h"
19 #include "dialogs/GUIDialogBusy.h"
20 #include "guilib/GUIWindowManager.h"
21 #include "messaging/ApplicationMessenger.h"
22 #include "settings/Settings.h"
23 #include "settings/SettingsComponent.h"
24 #include "utils/Job.h"
25 #include "utils/JobManager.h"
26 #include "utils/URIUtils.h"
27 #include "utils/log.h"
29 using namespace XFILE
;
30 using namespace std::chrono_literals
;
32 #define TIME_TO_BUSY_DIALOG 500
40 CResult(const CURL
& dir
, const CURL
& listDir
) : m_event(true), m_dir(dir
), m_listDir(listDir
), m_result(false) {}
51 CGetJob(std::shared_ptr
<IDirectory
>& imp
52 , std::shared_ptr
<CResult
>& result
)
57 bool DoWork() override
59 m_result
->m_list
.SetURL(m_result
->m_listDir
);
60 m_result
->m_result
= m_imp
->GetDirectory(m_result
->m_dir
, m_result
->m_list
);
61 m_result
->m_event
.Set();
62 return m_result
->m_result
;
65 std::shared_ptr
<CResult
> m_result
;
66 std::shared_ptr
<IDirectory
> m_imp
;
71 CGetDirectory(std::shared_ptr
<IDirectory
>& imp
, const CURL
& dir
, const CURL
& listDir
)
72 : m_result(new CResult(dir
, listDir
))
74 m_id
= CServiceBroker::GetJobManager()->AddJob(new CGetJob(imp
, m_result
), nullptr,
78 CGetJob
job(imp
, m_result
);
82 ~CGetDirectory() { CServiceBroker::GetJobManager()->CancelJob(m_id
); }
86 return m_result
->m_event
;
89 bool Wait(unsigned int timeout
)
91 return m_result
->m_event
.Wait(std::chrono::milliseconds(timeout
));
94 bool GetDirectory(CFileItemList
& list
)
96 /* if it was not finished or failed, return failure */
97 if (!m_result
->m_event
.Wait(0ms
) || !m_result
->m_result
)
103 list
.Copy(m_result
->m_list
);
106 std::shared_ptr
<CResult
> m_result
;
111 CDirectory::CDirectory() = default;
113 CDirectory::~CDirectory() = default;
115 bool CDirectory::GetDirectory(const std::string
& strPath
, CFileItemList
&items
, const std::string
&strMask
, int flags
)
119 hints
.mask
= strMask
;
120 const CURL
pathToUrl(strPath
);
121 return GetDirectory(pathToUrl
, items
, hints
);
124 bool CDirectory::GetDirectory(const std::string
& strPath
,
125 const std::shared_ptr
<IDirectory
>& pDirectory
,
126 CFileItemList
& items
,
127 const std::string
& strMask
,
132 hints
.mask
= strMask
;
133 const CURL
pathToUrl(strPath
);
134 return GetDirectory(pathToUrl
, pDirectory
, items
, hints
);
137 bool CDirectory::GetDirectory(const std::string
& strPath
, CFileItemList
&items
, const CHints
&hints
)
139 const CURL
pathToUrl(strPath
);
140 return GetDirectory(pathToUrl
, items
, hints
);
143 bool CDirectory::GetDirectory(const CURL
& url
, CFileItemList
&items
, const std::string
&strMask
, int flags
)
147 hints
.mask
= strMask
;
148 return GetDirectory(url
, items
, hints
);
151 bool CDirectory::GetDirectory(const CURL
& url
, CFileItemList
&items
, const CHints
&hints
)
153 CURL realURL
= URIUtils::SubstitutePath(url
);
154 std::shared_ptr
<IDirectory
> pDirectory(CDirectoryFactory::Create(realURL
));
155 return CDirectory::GetDirectory(url
, pDirectory
, items
, hints
);
158 bool CDirectory::GetDirectory(const CURL
& url
,
159 const std::shared_ptr
<IDirectory
>& pDirectory
,
160 CFileItemList
& items
,
165 CURL realURL
= URIUtils::SubstitutePath(url
);
169 // check our cache for this path
170 if (g_directoryCache
.GetDirectory(realURL
.Get(), items
, (hints
.flags
& DIR_FLAG_READ_CACHE
) == DIR_FLAG_READ_CACHE
))
174 // need to clear the cache (in case the directory fetch fails)
175 // and (re)fetch the folder
176 if (!(hints
.flags
& DIR_FLAG_BYPASS_CACHE
))
177 g_directoryCache
.ClearDirectory(realURL
.Get());
179 pDirectory
->SetFlags(hints
.flags
);
182 CURL authUrl
= realURL
;
186 const std::string
pathToUrl(url
.Get());
188 // don't change auth if it's set explicitly
189 if (CPasswordManager::GetInstance().IsURLSupported(authUrl
) && authUrl
.GetUserName().empty())
190 CPasswordManager::GetInstance().AuthenticateURL(authUrl
);
193 result
= pDirectory
->GetDirectory(authUrl
, items
);
197 // @TODO ProcessRequirements() can bring up the keyboard input dialog
198 // filesystem must not depend on GUI
199 if (CServiceBroker::GetAppMessenger()->IsProcessThread() &&
200 pDirectory
->ProcessRequirements())
202 authUrl
.SetDomain("");
203 authUrl
.SetUserName("");
204 authUrl
.SetPassword("");
208 CLog::Log(LOGERROR
, "{} - Error getting {}", __FUNCTION__
, url
.GetRedacted());
213 // hide credentials if necessary
214 if (CPasswordManager::GetInstance().IsURLSupported(realURL
))
217 // for explicitly credentials
218 if (!realURL
.GetUserName().empty())
220 // credentials was changed i.e. were stored in the password
221 // manager, in this case we can hide them from an item URL,
222 // otherwise we have to keep credentials in an item URL
223 if ( realURL
.GetUserName() != authUrl
.GetUserName()
224 || realURL
.GetPassWord() != authUrl
.GetPassWord()
225 || realURL
.GetDomain() != authUrl
.GetDomain())
232 // hide credentials in any other cases
238 for (int i
= 0; i
< items
.Size(); ++i
)
240 CFileItemPtr item
= items
[i
];
241 CURL itemUrl
= item
->GetURL();
242 itemUrl
.SetDomain("");
243 itemUrl
.SetUserName("");
244 itemUrl
.SetPassword("");
245 item
->SetPath(itemUrl
.Get());
250 // cache the directory, if necessary
251 if (!(hints
.flags
& DIR_FLAG_BYPASS_CACHE
))
252 g_directoryCache
.SetDirectory(realURL
.Get(), items
, pDirectory
->GetCacheType(url
));
255 // now filter for allowed files
256 if (!pDirectory
->AllowAll())
258 pDirectory
->SetMask(hints
.mask
);
259 for (int i
= 0; i
< items
.Size(); ++i
)
261 CFileItemPtr item
= items
[i
];
262 if (!item
->m_bIsFolder
&& !pDirectory
->IsAllowed(item
->GetURL()))
265 i
--; // don't confuse loop
269 // filter hidden files
270 //! @todo we shouldn't be checking the gui setting here, callers should use getHidden instead
271 if (!CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_FILELISTS_SHOWHIDDEN
) && !(hints
.flags
& DIR_FLAG_GET_HIDDEN
))
273 for (int i
= 0; i
< items
.Size(); ++i
)
275 if (items
[i
]->GetProperty("file:hidden").asBoolean())
278 i
--; // don't confuse loop
283 // Should any of the files we read be treated as a directory?
284 // Disable for database folders, as they already contain the extracted items
285 if (!(hints
.flags
& DIR_FLAG_NO_FILE_DIRS
) && !items
.IsMusicDb() && !items
.IsVideoDb() && !items
.IsSmartPlayList())
286 FilterFileDirectories(items
, hints
.mask
);
288 // Correct items for path substitution
289 const std::string
pathToUrl(url
.Get());
290 const std::string
pathToUrl2(realURL
.Get());
291 if (pathToUrl
!= pathToUrl2
)
293 for (int i
= 0; i
< items
.Size(); ++i
)
295 CFileItemPtr item
= items
[i
];
296 item
->SetPath(URIUtils::SubstitutePath(item
->GetPath(), true));
302 XBMCCOMMONS_HANDLE_UNCHECKED
303 catch (...) { CLog::Log(LOGERROR
, "{} - Unhandled exception", __FUNCTION__
); }
304 CLog::Log(LOGERROR
, "{} - Error getting {}", __FUNCTION__
, url
.GetRedacted());
308 bool CDirectory::Create(const std::string
& strPath
)
310 const CURL
pathToUrl(strPath
);
311 return Create(pathToUrl
);
314 bool CDirectory::Create(const CURL
& url
)
318 CURL realURL
= URIUtils::SubstitutePath(url
);
320 if (CPasswordManager::GetInstance().IsURLSupported(realURL
) && realURL
.GetUserName().empty())
321 CPasswordManager::GetInstance().AuthenticateURL(realURL
);
323 std::unique_ptr
<IDirectory
> pDirectory(CDirectoryFactory::Create(realURL
));
325 if(pDirectory
->Create(realURL
))
328 XBMCCOMMONS_HANDLE_UNCHECKED
329 catch (...) { CLog::Log(LOGERROR
, "{} - Unhandled exception", __FUNCTION__
); }
330 CLog::Log(LOGERROR
, "{} - Error creating {}", __FUNCTION__
, url
.GetRedacted());
334 bool CDirectory::Exists(const std::string
& strPath
, bool bUseCache
/* = true */)
336 const CURL
pathToUrl(strPath
);
337 return Exists(pathToUrl
, bUseCache
);
340 bool CDirectory::Exists(const CURL
& url
, bool bUseCache
/* = true */)
344 CURL realURL
= URIUtils::SubstitutePath(url
);
348 std::string
realPath(realURL
.Get());
349 URIUtils::AddSlashAtEnd(realPath
);
350 if (g_directoryCache
.FileExists(realPath
, bPathInCache
))
356 if (CPasswordManager::GetInstance().IsURLSupported(realURL
) && realURL
.GetUserName().empty())
357 CPasswordManager::GetInstance().AuthenticateURL(realURL
);
359 std::unique_ptr
<IDirectory
> pDirectory(CDirectoryFactory::Create(realURL
));
361 return pDirectory
->Exists(realURL
);
363 XBMCCOMMONS_HANDLE_UNCHECKED
364 catch (...) { CLog::Log(LOGERROR
, "{} - Unhandled exception", __FUNCTION__
); }
365 CLog::Log(LOGERROR
, "{} - Error checking for {}", __FUNCTION__
, url
.GetRedacted());
369 bool CDirectory::Remove(const std::string
& strPath
)
371 const CURL
pathToUrl(strPath
);
372 return Remove(pathToUrl
);
375 bool CDirectory::RemoveRecursive(const std::string
& strPath
)
377 return RemoveRecursive(CURL
{ strPath
});
380 bool CDirectory::Remove(const CURL
& url
)
384 CURL realURL
= URIUtils::SubstitutePath(url
);
385 CURL authUrl
= realURL
;
386 if (CPasswordManager::GetInstance().IsURLSupported(authUrl
) && authUrl
.GetUserName().empty())
387 CPasswordManager::GetInstance().AuthenticateURL(authUrl
);
389 std::unique_ptr
<IDirectory
> pDirectory(CDirectoryFactory::Create(realURL
));
391 if(pDirectory
->Remove(authUrl
))
393 g_directoryCache
.ClearFile(realURL
.Get());
397 XBMCCOMMONS_HANDLE_UNCHECKED
398 catch (...) { CLog::Log(LOGERROR
, "{} - Unhandled exception", __FUNCTION__
); }
399 CLog::Log(LOGERROR
, "{} - Error removing {}", __FUNCTION__
, url
.GetRedacted());
403 bool CDirectory::RemoveRecursive(const CURL
& url
)
407 CURL realURL
= URIUtils::SubstitutePath(url
);
408 CURL authUrl
= realURL
;
409 if (CPasswordManager::GetInstance().IsURLSupported(authUrl
) && authUrl
.GetUserName().empty())
410 CPasswordManager::GetInstance().AuthenticateURL(authUrl
);
412 std::unique_ptr
<IDirectory
> pDirectory(CDirectoryFactory::Create(realURL
));
414 if(pDirectory
->RemoveRecursive(authUrl
))
416 g_directoryCache
.ClearFile(realURL
.Get());
420 XBMCCOMMONS_HANDLE_UNCHECKED
421 catch (...) { CLog::Log(LOGERROR
, "{} - Unhandled exception", __FUNCTION__
); }
422 CLog::Log(LOGERROR
, "{} - Error removing {}", __FUNCTION__
, url
.GetRedacted());
426 void CDirectory::FilterFileDirectories(CFileItemList
&items
, const std::string
&mask
,
429 for (int i
=0; i
< items
.Size(); ++i
)
431 CFileItemPtr pItem
=items
[i
];
432 auto mode
= expandImages
&& pItem
->IsDiscImage() ? EFILEFOLDER_TYPE_ONBROWSE
: EFILEFOLDER_TYPE_ALWAYS
;
433 if (!pItem
->m_bIsFolder
&& pItem
->IsFileFolder(mode
))
435 std::unique_ptr
<IFileDirectory
> pDirectory(CFileDirectoryFactory::Create(pItem
->GetURL(),pItem
.get(),mask
));
437 pItem
->m_bIsFolder
= true;
439 if (pItem
->m_bIsFolder
)
442 i
--; // don't confuse loop