2 * Copyright (C) 2010-2020 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 "CompileInfo.h"
12 #include "FileOperationJob.h"
13 #include "ServiceBroker.h"
14 #include "StringUtils.h"
18 #include "filesystem/File.h"
19 #include "filesystem/MultiPathDirectory.h"
20 #include "filesystem/SpecialProtocol.h"
21 #include "filesystem/StackDirectory.h"
22 #include "guilib/GUIComponent.h"
23 #include "guilib/GUIKeyboardFactory.h"
24 #include "guilib/LocalizeStrings.h"
25 #include "imagefiles/ImageFileURL.h"
26 #include "settings/MediaSourceSettings.h"
27 #include "settings/Settings.h"
28 #include "settings/SettingsComponent.h"
29 #include "storage/MediaManager.h"
30 #include "utils/Variant.h"
31 #include "utils/log.h"
33 #if defined(TARGET_WINDOWS)
34 #include "platform/win32/WIN32Util.h"
35 #include "utils/CharsetConverter.h"
40 using namespace XFILE
;
42 bool CFileUtils::DeleteItem(const std::string
&strPath
)
44 CFileItemPtr
item(new CFileItem(strPath
));
45 item
->SetPath(strPath
);
46 item
->m_bIsFolder
= URIUtils::HasSlashAtEnd(strPath
);
48 return DeleteItem(item
);
51 bool CFileUtils::DeleteItem(const std::shared_ptr
<CFileItem
>& item
)
53 if (!item
|| item
->IsParentFolder())
56 // Create a temporary item list containing the file/folder for deletion
57 CFileItemPtr
pItemTemp(new CFileItem(*item
));
58 pItemTemp
->Select(true);
62 // grab the real filemanager window, set up the progress bar,
63 // and process the delete action
64 CFileOperationJob
op(CFileOperationJob::ActionDelete
, items
, "");
69 bool CFileUtils::RenameFile(const std::string
&strFile
)
71 std::string
strFileAndPath(strFile
);
72 URIUtils::RemoveSlashAtEnd(strFileAndPath
);
73 std::string strFileName
= URIUtils::GetFileName(strFileAndPath
);
74 std::string strPath
= URIUtils::GetDirectory(strFileAndPath
);
75 if (CGUIKeyboardFactory::ShowAndGetInput(strFileName
, CVariant
{g_localizeStrings
.Get(16013)}, false))
77 strPath
= URIUtils::AddFileToFolder(strPath
, strFileName
);
78 CLog::Log(LOGINFO
, "FileUtils: rename {}->{}", strFileAndPath
, strPath
);
79 if (URIUtils::IsMultiPath(strFileAndPath
))
80 { // special case for multipath renames - rename all the paths.
81 std::vector
<std::string
> paths
;
82 CMultiPathDirectory::GetPaths(strFileAndPath
, paths
);
84 for (unsigned int i
= 0; i
< paths
.size(); ++i
)
86 std::string
filePath(paths
[i
]);
87 URIUtils::RemoveSlashAtEnd(filePath
);
88 filePath
= URIUtils::GetDirectory(filePath
);
89 filePath
= URIUtils::AddFileToFolder(filePath
, strFileName
);
90 if (CFile::Rename(paths
[i
], filePath
))
95 return CFile::Rename(strFileAndPath
, strPath
);
100 bool CFileUtils::RemoteAccessAllowed(const std::string
&strPath
)
102 std::string SourceNames
[] = { "programs", "files", "video", "music", "pictures" };
104 std::string realPath
= URIUtils::GetRealPath(strPath
);
105 // for rar:// and zip:// paths we need to extract the path to the archive
106 // instead of using the VFS path
107 while (URIUtils::IsInArchive(realPath
))
108 realPath
= CURL(realPath
).GetHostName();
110 if (StringUtils::StartsWithNoCase(realPath
, "virtualpath://upnproot/"))
112 else if (StringUtils::StartsWithNoCase(realPath
, "musicdb://"))
114 else if (StringUtils::StartsWithNoCase(realPath
, "videodb://"))
116 else if (StringUtils::StartsWithNoCase(realPath
, "library://video"))
118 else if (StringUtils::StartsWithNoCase(realPath
, "library://music"))
120 else if (StringUtils::StartsWithNoCase(realPath
, "sources://video"))
122 else if (StringUtils::StartsWithNoCase(realPath
, "special://musicplaylists"))
124 else if (StringUtils::StartsWithNoCase(realPath
, "special://profile/playlists"))
126 else if (StringUtils::StartsWithNoCase(realPath
, "special://videoplaylists"))
128 else if (StringUtils::StartsWithNoCase(realPath
, "special://skin"))
130 else if (StringUtils::StartsWithNoCase(realPath
, "special://profile/addon_data"))
132 else if (StringUtils::StartsWithNoCase(realPath
, "addons://sources"))
134 else if (StringUtils::StartsWithNoCase(realPath
, "upnp://"))
136 else if (StringUtils::StartsWithNoCase(realPath
, "plugin://"))
140 std::string strPlaylistsPath
= CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_SYSTEM_PLAYLISTSPATH
);
141 URIUtils::RemoveSlashAtEnd(strPlaylistsPath
);
142 if (StringUtils::StartsWithNoCase(realPath
, strPlaylistsPath
))
146 // Check manually added sources (held in sources.xml)
147 for (const std::string
& sourceName
: SourceNames
)
149 std::vector
<CMediaSource
>* sources
= CMediaSourceSettings::GetInstance().GetSources(sourceName
);
150 int sourceIndex
= CUtil::GetMatchingSource(realPath
, *sources
, isSource
);
151 if (sourceIndex
>= 0 && sourceIndex
< static_cast<int>(sources
->size()) &&
152 sources
->at(sourceIndex
).m_iHasLock
!= LOCK_STATE_LOCKED
&&
153 sources
->at(sourceIndex
).m_allowSharing
)
156 // Check auto-mounted sources
157 std::vector
<CMediaSource
> sources
;
158 CServiceBroker::GetMediaManager().GetRemovableDrives(
159 sources
); // Sources returned always have m_allowsharing = true
160 //! @todo Make sharing of auto-mounted sources user configurable
161 int sourceIndex
= CUtil::GetMatchingSource(realPath
, sources
, isSource
);
162 if (sourceIndex
>= 0 && sourceIndex
< static_cast<int>(sources
.size()) &&
163 sources
.at(sourceIndex
).m_iHasLock
!= LOCK_STATE_LOCKED
&&
164 sources
.at(sourceIndex
).m_allowSharing
)
170 CDateTime
CFileUtils::GetModificationDate(const std::string
& strFileNameAndPath
,
171 const bool& bUseLatestDate
)
174 return GetModificationDate(1, strFileNameAndPath
);
176 return GetModificationDate(0, strFileNameAndPath
);
179 CDateTime
CFileUtils::GetModificationDate(const int& code
, const std::string
& strFileNameAndPath
)
182 if (strFileNameAndPath
.empty())
184 CLog::Log(LOGDEBUG
, "{} empty strFileNameAndPath variable", __FUNCTION__
);
190 std::string file
= strFileNameAndPath
;
191 if (URIUtils::IsStack(strFileNameAndPath
))
192 file
= CStackDirectory::GetFirstStackedFile(strFileNameAndPath
);
194 if (URIUtils::IsInArchive(file
))
195 file
= CURL(file
).GetHostName();
197 // Try to get ctime (creation on Windows, metadata change on Linux) and mtime (modification)
198 struct __stat64 buffer
;
199 if (CFile::Stat(file
, &buffer
) == 0 && (buffer
.st_mtime
!= 0 || buffer
.st_ctime
!= 0))
201 time_t now
= time(NULL
);
203 // Prefer the modification time if it's valid, fallback to ctime
206 if (buffer
.st_mtime
!= 0 && static_cast<time_t>(buffer
.st_mtime
) <= now
)
207 addedTime
= static_cast<time_t>(buffer
.st_mtime
);
209 addedTime
= static_cast<time_t>(buffer
.st_ctime
);
211 // Use the later of the ctime and mtime
215 std::max(static_cast<time_t>(buffer
.st_ctime
), static_cast<time_t>(buffer
.st_mtime
));
216 // if the newer of the two dates is in the future, we try it with the older one
219 std::min(static_cast<time_t>(buffer
.st_ctime
), static_cast<time_t>(buffer
.st_mtime
));
221 // Prefer the earliest of ctime and mtime, fallback to other
225 std::min(static_cast<time_t>(buffer
.st_ctime
), static_cast<time_t>(buffer
.st_mtime
));
226 // if the older of the two dates is invalid, we try it with the newer one
229 std::max(static_cast<time_t>(buffer
.st_ctime
), static_cast<time_t>(buffer
.st_mtime
));
233 // make sure the datetime does is not in the future
234 if (addedTime
<= now
)
237 #ifdef HAVE_LOCALTIME_R
238 struct tm result
= {};
239 time
= localtime_r(&addedTime
, &result
);
241 time
= localtime(&addedTime
);
250 CLog::Log(LOGERROR
, "{} unable to extract modification date for file ({})", __FUNCTION__
,
256 bool CFileUtils::CheckFileAccessAllowed(const std::string
&filePath
)
258 // DENY access to paths matching
259 const std::vector
<std::string
> blacklist
= {
263 "advancedsettings.xml",
268 std::vector
<std::string
> whitelist
= {
269 CSpecialProtocol::TranslatePath("special://home"),
270 CSpecialProtocol::TranslatePath("special://xbmc"),
271 CSpecialProtocol::TranslatePath("special://musicartistsinfo"),
274 auto kodiExtraWhitelist
= CCompileInfo::GetWebserverExtraWhitelist();
275 whitelist
.insert(whitelist
.end(), kodiExtraWhitelist
.begin(), kodiExtraWhitelist
.end());
277 bool isImage
= false;
278 std::string decodePath
= CURL::Decode(filePath
);
279 if (URIUtils::IsProtocol(filePath
, "image"))
282 decodePath
= IMAGE_FILES::CImageFileURL(filePath
).GetTargetFile();
286 for (const auto &b
: blacklist
)
288 if (decodePath
.find(b
) != std::string::npos
)
290 CLog::Log(LOGERROR
, "{} denied access to {}", __FUNCTION__
, decodePath
);
295 #if defined(TARGET_POSIX)
296 std::string whiteEntry
;
297 char *fullpath
= realpath(decodePath
.c_str(), nullptr);
299 // if this is a locally existing file, check access permissions
302 const std::string realPath
= fullpath
;
306 for (const auto &w
: whitelist
)
308 char *realtemp
= realpath(w
.c_str(), nullptr);
311 whiteEntry
= realtemp
;
314 if (StringUtils::StartsWith(realPath
, whiteEntry
))
317 // check sources with realPath
318 return CFileUtils::RemoteAccessAllowed(realPath
);
320 #elif defined(TARGET_WINDOWS)
321 CURL
url(decodePath
);
322 if (url
.GetProtocol().empty())
324 std::wstring decodePathW
;
325 g_charsetConverter
.utf8ToW(decodePath
, decodePathW
, false);
326 CWIN32Util::AddExtraLongPathPrefix(decodePathW
);
327 DWORD bufSize
= GetFullPathNameW(decodePathW
.c_str(), 0, nullptr, nullptr);
330 std::wstring fullpathW
;
331 fullpathW
.resize(bufSize
);
332 if (GetFullPathNameW(decodePathW
.c_str(), bufSize
, const_cast<wchar_t*>(fullpathW
.c_str()), nullptr) <= bufSize
- 1)
334 CWIN32Util::RemoveExtraLongPathPrefix(fullpathW
);
335 std::string fullpath
;
336 g_charsetConverter
.wToUTF8(fullpathW
, fullpath
, false);
337 for (const std::string
& whiteEntry
: whitelist
)
339 if (StringUtils::StartsWith(fullpath
, whiteEntry
))
342 return CFileUtils::RemoteAccessAllowed(fullpath
);
347 // if it isn't a local file, it must be a vfs entry
349 return CFileUtils::RemoteAccessAllowed(decodePath
);
353 bool CFileUtils::Exists(const std::string
& strFileName
, bool bUseCache
)
355 return CFile::Exists(strFileName
, bUseCache
);
358 bool CFileUtils::DeleteItemWithConfirm(const std::shared_ptr
<CFileItem
>& item
)
360 if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(
361 CSettings::SETTING_FILELISTS_CONFIRMFILEDELETION
))
363 CGUIComponent
* gui
= CServiceBroker::GetGUI();
365 if (!gui
|| !gui
->ConfirmDelete(item
->GetPath()))
369 return CFileUtils::DeleteItem(item
);