summaryrefslogtreecommitdiffstats
path: root/xbmc/filesystem/Directory.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'xbmc/filesystem/Directory.cpp')
-rw-r--r--xbmc/filesystem/Directory.cpp446
1 files changed, 446 insertions, 0 deletions
diff --git a/xbmc/filesystem/Directory.cpp b/xbmc/filesystem/Directory.cpp
new file mode 100644
index 0000000..5436fd9
--- /dev/null
+++ b/xbmc/filesystem/Directory.cpp
@@ -0,0 +1,446 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "Directory.h"
+
+#include "DirectoryCache.h"
+#include "DirectoryFactory.h"
+#include "FileDirectoryFactory.h"
+#include "FileItem.h"
+#include "PasswordManager.h"
+#include "ServiceBroker.h"
+#include "URL.h"
+#include "commons/Exception.h"
+#include "dialogs/GUIDialogBusy.h"
+#include "guilib/GUIWindowManager.h"
+#include "messaging/ApplicationMessenger.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
+#include "utils/Job.h"
+#include "utils/JobManager.h"
+#include "utils/URIUtils.h"
+#include "utils/log.h"
+
+using namespace XFILE;
+using namespace std::chrono_literals;
+
+#define TIME_TO_BUSY_DIALOG 500
+
+class CGetDirectory
+{
+private:
+
+ struct CResult
+ {
+ CResult(const CURL& dir, const CURL& listDir) : m_event(true), m_dir(dir), m_listDir(listDir), m_result(false) {}
+ CEvent m_event;
+ CFileItemList m_list;
+ CURL m_dir;
+ CURL m_listDir;
+ bool m_result;
+ };
+
+ struct CGetJob
+ : CJob
+ {
+ CGetJob(std::shared_ptr<IDirectory>& imp
+ , std::shared_ptr<CResult>& result)
+ : m_result(result)
+ , m_imp(imp)
+ {}
+ public:
+ bool DoWork() override
+ {
+ m_result->m_list.SetURL(m_result->m_listDir);
+ m_result->m_result = m_imp->GetDirectory(m_result->m_dir, m_result->m_list);
+ m_result->m_event.Set();
+ return m_result->m_result;
+ }
+
+ std::shared_ptr<CResult> m_result;
+ std::shared_ptr<IDirectory> m_imp;
+ };
+
+public:
+
+ CGetDirectory(std::shared_ptr<IDirectory>& imp, const CURL& dir, const CURL& listDir)
+ : m_result(new CResult(dir, listDir))
+ {
+ m_id = CServiceBroker::GetJobManager()->AddJob(new CGetJob(imp, m_result), nullptr,
+ CJob::PRIORITY_HIGH);
+ if (m_id == 0)
+ {
+ CGetJob job(imp, m_result);
+ job.DoWork();
+ }
+ }
+ ~CGetDirectory() { CServiceBroker::GetJobManager()->CancelJob(m_id); }
+
+ CEvent& GetEvent()
+ {
+ return m_result->m_event;
+ }
+
+ bool Wait(unsigned int timeout)
+ {
+ return m_result->m_event.Wait(std::chrono::milliseconds(timeout));
+ }
+
+ bool GetDirectory(CFileItemList& list)
+ {
+ /* if it was not finished or failed, return failure */
+ if (!m_result->m_event.Wait(0ms) || !m_result->m_result)
+ {
+ list.Clear();
+ return false;
+ }
+
+ list.Copy(m_result->m_list);
+ return true;
+ }
+ std::shared_ptr<CResult> m_result;
+ unsigned int m_id;
+};
+
+
+CDirectory::CDirectory() = default;
+
+CDirectory::~CDirectory() = default;
+
+bool CDirectory::GetDirectory(const std::string& strPath, CFileItemList &items, const std::string &strMask, int flags)
+{
+ CHints hints;
+ hints.flags = flags;
+ hints.mask = strMask;
+ const CURL pathToUrl(strPath);
+ return GetDirectory(pathToUrl, items, hints);
+}
+
+bool CDirectory::GetDirectory(const std::string& strPath,
+ const std::shared_ptr<IDirectory>& pDirectory,
+ CFileItemList& items,
+ const std::string& strMask,
+ int flags)
+{
+ CHints hints;
+ hints.flags = flags;
+ hints.mask = strMask;
+ const CURL pathToUrl(strPath);
+ return GetDirectory(pathToUrl, pDirectory, items, hints);
+}
+
+bool CDirectory::GetDirectory(const std::string& strPath, CFileItemList &items, const CHints &hints)
+{
+ const CURL pathToUrl(strPath);
+ return GetDirectory(pathToUrl, items, hints);
+}
+
+bool CDirectory::GetDirectory(const CURL& url, CFileItemList &items, const std::string &strMask, int flags)
+{
+ CHints hints;
+ hints.flags = flags;
+ hints.mask = strMask;
+ return GetDirectory(url, items, hints);
+}
+
+bool CDirectory::GetDirectory(const CURL& url, CFileItemList &items, const CHints &hints)
+{
+ CURL realURL = URIUtils::SubstitutePath(url);
+ std::shared_ptr<IDirectory> pDirectory(CDirectoryFactory::Create(realURL));
+ return CDirectory::GetDirectory(url, pDirectory, items, hints);
+}
+
+bool CDirectory::GetDirectory(const CURL& url,
+ const std::shared_ptr<IDirectory>& pDirectory,
+ CFileItemList& items,
+ const CHints& hints)
+{
+ try
+ {
+ CURL realURL = URIUtils::SubstitutePath(url);
+ if (!pDirectory)
+ return false;
+
+ // check our cache for this path
+ if (g_directoryCache.GetDirectory(realURL.Get(), items, (hints.flags & DIR_FLAG_READ_CACHE) == DIR_FLAG_READ_CACHE))
+ items.SetURL(url);
+ else
+ {
+ // need to clear the cache (in case the directory fetch fails)
+ // and (re)fetch the folder
+ if (!(hints.flags & DIR_FLAG_BYPASS_CACHE))
+ g_directoryCache.ClearDirectory(realURL.Get());
+
+ pDirectory->SetFlags(hints.flags);
+
+ bool result = false;
+ CURL authUrl = realURL;
+
+ while (!result)
+ {
+ const std::string pathToUrl(url.Get());
+
+ // don't change auth if it's set explicitly
+ if (CPasswordManager::GetInstance().IsURLSupported(authUrl) && authUrl.GetUserName().empty())
+ CPasswordManager::GetInstance().AuthenticateURL(authUrl);
+
+ items.SetURL(url);
+ result = pDirectory->GetDirectory(authUrl, items);
+
+ if (!result)
+ {
+ // @TODO ProcessRequirements() can bring up the keyboard input dialog
+ // filesystem must not depend on GUI
+ if (CServiceBroker::GetAppMessenger()->IsProcessThread() &&
+ pDirectory->ProcessRequirements())
+ {
+ authUrl.SetDomain("");
+ authUrl.SetUserName("");
+ authUrl.SetPassword("");
+ continue;
+ }
+
+ CLog::Log(LOGERROR, "{} - Error getting {}", __FUNCTION__, url.GetRedacted());
+ return false;
+ }
+ }
+
+ // hide credentials if necessary
+ if (CPasswordManager::GetInstance().IsURLSupported(realURL))
+ {
+ bool hide = false;
+ // for explicitly credentials
+ if (!realURL.GetUserName().empty())
+ {
+ // credentials was changed i.e. were stored in the password
+ // manager, in this case we can hide them from an item URL,
+ // otherwise we have to keep credentials in an item URL
+ if ( realURL.GetUserName() != authUrl.GetUserName()
+ || realURL.GetPassWord() != authUrl.GetPassWord()
+ || realURL.GetDomain() != authUrl.GetDomain())
+ {
+ hide = true;
+ }
+ }
+ else
+ {
+ // hide credentials in any other cases
+ hide = true;
+ }
+
+ if (hide)
+ {
+ for (int i = 0; i < items.Size(); ++i)
+ {
+ CFileItemPtr item = items[i];
+ CURL itemUrl = item->GetURL();
+ itemUrl.SetDomain("");
+ itemUrl.SetUserName("");
+ itemUrl.SetPassword("");
+ item->SetPath(itemUrl.Get());
+ }
+ }
+ }
+
+ // cache the directory, if necessary
+ if (!(hints.flags & DIR_FLAG_BYPASS_CACHE))
+ g_directoryCache.SetDirectory(realURL.Get(), items, pDirectory->GetCacheType(url));
+ }
+
+ // now filter for allowed files
+ if (!pDirectory->AllowAll())
+ {
+ pDirectory->SetMask(hints.mask);
+ for (int i = 0; i < items.Size(); ++i)
+ {
+ CFileItemPtr item = items[i];
+ if (!item->m_bIsFolder && !pDirectory->IsAllowed(item->GetURL()))
+ {
+ items.Remove(i);
+ i--; // don't confuse loop
+ }
+ }
+ }
+ // filter hidden files
+ //! @todo we shouldn't be checking the gui setting here, callers should use getHidden instead
+ if (!CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_FILELISTS_SHOWHIDDEN) && !(hints.flags & DIR_FLAG_GET_HIDDEN))
+ {
+ for (int i = 0; i < items.Size(); ++i)
+ {
+ if (items[i]->GetProperty("file:hidden").asBoolean())
+ {
+ items.Remove(i);
+ i--; // don't confuse loop
+ }
+ }
+ }
+
+ // Should any of the files we read be treated as a directory?
+ // Disable for database folders, as they already contain the extracted items
+ if (!(hints.flags & DIR_FLAG_NO_FILE_DIRS) && !items.IsMusicDb() && !items.IsVideoDb() && !items.IsSmartPlayList())
+ FilterFileDirectories(items, hints.mask);
+
+ // Correct items for path substitution
+ const std::string pathToUrl(url.Get());
+ const std::string pathToUrl2(realURL.Get());
+ if (pathToUrl != pathToUrl2)
+ {
+ for (int i = 0; i < items.Size(); ++i)
+ {
+ CFileItemPtr item = items[i];
+ item->SetPath(URIUtils::SubstitutePath(item->GetPath(), true));
+ }
+ }
+
+ return true;
+ }
+ XBMCCOMMONS_HANDLE_UNCHECKED
+ catch (...) { CLog::Log(LOGERROR, "{} - Unhandled exception", __FUNCTION__); }
+ CLog::Log(LOGERROR, "{} - Error getting {}", __FUNCTION__, url.GetRedacted());
+ return false;
+}
+
+bool CDirectory::Create(const std::string& strPath)
+{
+ const CURL pathToUrl(strPath);
+ return Create(pathToUrl);
+}
+
+bool CDirectory::Create(const CURL& url)
+{
+ try
+ {
+ CURL realURL = URIUtils::SubstitutePath(url);
+
+ if (CPasswordManager::GetInstance().IsURLSupported(realURL) && realURL.GetUserName().empty())
+ CPasswordManager::GetInstance().AuthenticateURL(realURL);
+
+ std::unique_ptr<IDirectory> pDirectory(CDirectoryFactory::Create(realURL));
+ if (pDirectory)
+ if(pDirectory->Create(realURL))
+ return true;
+ }
+ XBMCCOMMONS_HANDLE_UNCHECKED
+ catch (...) { CLog::Log(LOGERROR, "{} - Unhandled exception", __FUNCTION__); }
+ CLog::Log(LOGERROR, "{} - Error creating {}", __FUNCTION__, url.GetRedacted());
+ return false;
+}
+
+bool CDirectory::Exists(const std::string& strPath, bool bUseCache /* = true */)
+{
+ const CURL pathToUrl(strPath);
+ return Exists(pathToUrl, bUseCache);
+}
+
+bool CDirectory::Exists(const CURL& url, bool bUseCache /* = true */)
+{
+ try
+ {
+ CURL realURL = URIUtils::SubstitutePath(url);
+ if (bUseCache)
+ {
+ bool bPathInCache;
+ std::string realPath(realURL.Get());
+ URIUtils::AddSlashAtEnd(realPath);
+ if (g_directoryCache.FileExists(realPath, bPathInCache))
+ return true;
+ if (bPathInCache)
+ return false;
+ }
+
+ if (CPasswordManager::GetInstance().IsURLSupported(realURL) && realURL.GetUserName().empty())
+ CPasswordManager::GetInstance().AuthenticateURL(realURL);
+
+ std::unique_ptr<IDirectory> pDirectory(CDirectoryFactory::Create(realURL));
+ if (pDirectory)
+ return pDirectory->Exists(realURL);
+ }
+ XBMCCOMMONS_HANDLE_UNCHECKED
+ catch (...) { CLog::Log(LOGERROR, "{} - Unhandled exception", __FUNCTION__); }
+ CLog::Log(LOGERROR, "{} - Error checking for {}", __FUNCTION__, url.GetRedacted());
+ return false;
+}
+
+bool CDirectory::Remove(const std::string& strPath)
+{
+ const CURL pathToUrl(strPath);
+ return Remove(pathToUrl);
+}
+
+bool CDirectory::RemoveRecursive(const std::string& strPath)
+{
+ return RemoveRecursive(CURL{ strPath });
+}
+
+bool CDirectory::Remove(const CURL& url)
+{
+ try
+ {
+ CURL realURL = URIUtils::SubstitutePath(url);
+ CURL authUrl = realURL;
+ if (CPasswordManager::GetInstance().IsURLSupported(authUrl) && authUrl.GetUserName().empty())
+ CPasswordManager::GetInstance().AuthenticateURL(authUrl);
+
+ std::unique_ptr<IDirectory> pDirectory(CDirectoryFactory::Create(realURL));
+ if (pDirectory)
+ if(pDirectory->Remove(authUrl))
+ {
+ g_directoryCache.ClearFile(realURL.Get());
+ return true;
+ }
+ }
+ XBMCCOMMONS_HANDLE_UNCHECKED
+ catch (...) { CLog::Log(LOGERROR, "{} - Unhandled exception", __FUNCTION__); }
+ CLog::Log(LOGERROR, "{} - Error removing {}", __FUNCTION__, url.GetRedacted());
+ return false;
+}
+
+bool CDirectory::RemoveRecursive(const CURL& url)
+{
+ try
+ {
+ CURL realURL = URIUtils::SubstitutePath(url);
+ CURL authUrl = realURL;
+ if (CPasswordManager::GetInstance().IsURLSupported(authUrl) && authUrl.GetUserName().empty())
+ CPasswordManager::GetInstance().AuthenticateURL(authUrl);
+
+ std::unique_ptr<IDirectory> pDirectory(CDirectoryFactory::Create(realURL));
+ if (pDirectory)
+ if(pDirectory->RemoveRecursive(authUrl))
+ {
+ g_directoryCache.ClearFile(realURL.Get());
+ return true;
+ }
+ }
+ XBMCCOMMONS_HANDLE_UNCHECKED
+ catch (...) { CLog::Log(LOGERROR, "{} - Unhandled exception", __FUNCTION__); }
+ CLog::Log(LOGERROR, "{} - Error removing {}", __FUNCTION__, url.GetRedacted());
+ return false;
+}
+
+void CDirectory::FilterFileDirectories(CFileItemList &items, const std::string &mask,
+ bool expandImages)
+{
+ for (int i=0; i< items.Size(); ++i)
+ {
+ CFileItemPtr pItem=items[i];
+ auto mode = expandImages && pItem->IsDiscImage() ? EFILEFOLDER_TYPE_ONBROWSE : EFILEFOLDER_TYPE_ALWAYS;
+ if (!pItem->m_bIsFolder && pItem->IsFileFolder(mode))
+ {
+ std::unique_ptr<IFileDirectory> pDirectory(CFileDirectoryFactory::Create(pItem->GetURL(),pItem.get(),mask));
+ if (pDirectory)
+ pItem->m_bIsFolder = true;
+ else
+ if (pItem->m_bIsFolder)
+ {
+ items.Remove(i);
+ i--; // don't confuse loop
+ }
+ }
+ }
+}