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/GUIKeyboardFactory.h"
23 #include "guilib/LocalizeStrings.h"
24 #include "settings/MediaSourceSettings.h"
25 #include "settings/Settings.h"
26 #include "settings/SettingsComponent.h"
27 #include "storage/MediaManager.h"
28 #include "utils/Variant.h"
29 #include "utils/log.h"
31 #if defined(TARGET_WINDOWS)
32 #include "platform/win32/WIN32Util.h"
33 #include "utils/CharsetConverter.h"
38 using namespace XFILE
;
40 bool CFileUtils::DeleteItem(const std::string
&strPath
)
42 CFileItemPtr
item(new CFileItem(strPath
));
43 item
->SetPath(strPath
);
44 item
->m_bIsFolder
= URIUtils::HasSlashAtEnd(strPath
);
46 return DeleteItem(item
);
49 bool CFileUtils::DeleteItem(const std::shared_ptr
<CFileItem
>& item
)
51 if (!item
|| item
->IsParentFolder())
54 // Create a temporary item list containing the file/folder for deletion
55 CFileItemPtr
pItemTemp(new CFileItem(*item
));
56 pItemTemp
->Select(true);
60 // grab the real filemanager window, set up the progress bar,
61 // and process the delete action
62 CFileOperationJob
op(CFileOperationJob::ActionDelete
, items
, "");
67 bool CFileUtils::RenameFile(const std::string
&strFile
)
69 std::string
strFileAndPath(strFile
);
70 URIUtils::RemoveSlashAtEnd(strFileAndPath
);
71 std::string strFileName
= URIUtils::GetFileName(strFileAndPath
);
72 std::string strPath
= URIUtils::GetDirectory(strFileAndPath
);
73 if (CGUIKeyboardFactory::ShowAndGetInput(strFileName
, CVariant
{g_localizeStrings
.Get(16013)}, false))
75 strPath
= URIUtils::AddFileToFolder(strPath
, strFileName
);
76 CLog::Log(LOGINFO
, "FileUtils: rename {}->{}", strFileAndPath
, strPath
);
77 if (URIUtils::IsMultiPath(strFileAndPath
))
78 { // special case for multipath renames - rename all the paths.
79 std::vector
<std::string
> paths
;
80 CMultiPathDirectory::GetPaths(strFileAndPath
, paths
);
82 for (unsigned int i
= 0; i
< paths
.size(); ++i
)
84 std::string
filePath(paths
[i
]);
85 URIUtils::RemoveSlashAtEnd(filePath
);
86 filePath
= URIUtils::GetDirectory(filePath
);
87 filePath
= URIUtils::AddFileToFolder(filePath
, strFileName
);
88 if (CFile::Rename(paths
[i
], filePath
))
93 return CFile::Rename(strFileAndPath
, strPath
);
98 bool CFileUtils::RemoteAccessAllowed(const std::string
&strPath
)
100 std::string SourceNames
[] = { "programs", "files", "video", "music", "pictures" };
102 std::string realPath
= URIUtils::GetRealPath(strPath
);
103 // for rar:// and zip:// paths we need to extract the path to the archive
104 // instead of using the VFS path
105 while (URIUtils::IsInArchive(realPath
))
106 realPath
= CURL(realPath
).GetHostName();
108 if (StringUtils::StartsWithNoCase(realPath
, "virtualpath://upnproot/"))
110 else if (StringUtils::StartsWithNoCase(realPath
, "musicdb://"))
112 else if (StringUtils::StartsWithNoCase(realPath
, "videodb://"))
114 else if (StringUtils::StartsWithNoCase(realPath
, "library://video"))
116 else if (StringUtils::StartsWithNoCase(realPath
, "library://music"))
118 else if (StringUtils::StartsWithNoCase(realPath
, "sources://video"))
120 else if (StringUtils::StartsWithNoCase(realPath
, "special://musicplaylists"))
122 else if (StringUtils::StartsWithNoCase(realPath
, "special://profile/playlists"))
124 else if (StringUtils::StartsWithNoCase(realPath
, "special://videoplaylists"))
126 else if (StringUtils::StartsWithNoCase(realPath
, "special://skin"))
128 else if (StringUtils::StartsWithNoCase(realPath
, "special://profile/addon_data"))
130 else if (StringUtils::StartsWithNoCase(realPath
, "addons://sources"))
132 else if (StringUtils::StartsWithNoCase(realPath
, "upnp://"))
134 else if (StringUtils::StartsWithNoCase(realPath
, "plugin://"))
138 std::string strPlaylistsPath
= CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_SYSTEM_PLAYLISTSPATH
);
139 URIUtils::RemoveSlashAtEnd(strPlaylistsPath
);
140 if (StringUtils::StartsWithNoCase(realPath
, strPlaylistsPath
))
144 // Check manually added sources (held in sources.xml)
145 for (const std::string
& sourceName
: SourceNames
)
147 VECSOURCES
* sources
= CMediaSourceSettings::GetInstance().GetSources(sourceName
);
148 int sourceIndex
= CUtil::GetMatchingSource(realPath
, *sources
, isSource
);
149 if (sourceIndex
>= 0 && sourceIndex
< static_cast<int>(sources
->size()) &&
150 sources
->at(sourceIndex
).m_iHasLock
!= LOCK_STATE_LOCKED
&&
151 sources
->at(sourceIndex
).m_allowSharing
)
154 // Check auto-mounted sources
156 CServiceBroker::GetMediaManager().GetRemovableDrives(
157 sources
); // Sources returned always have m_allowsharing = true
158 //! @todo Make sharing of auto-mounted sources user configurable
159 int sourceIndex
= CUtil::GetMatchingSource(realPath
, sources
, isSource
);
160 if (sourceIndex
>= 0 && sourceIndex
< static_cast<int>(sources
.size()) &&
161 sources
.at(sourceIndex
).m_iHasLock
!= LOCK_STATE_LOCKED
&&
162 sources
.at(sourceIndex
).m_allowSharing
)
168 CDateTime
CFileUtils::GetModificationDate(const std::string
& strFileNameAndPath
,
169 const bool& bUseLatestDate
)
172 return GetModificationDate(1, strFileNameAndPath
);
174 return GetModificationDate(0, strFileNameAndPath
);
177 CDateTime
CFileUtils::GetModificationDate(const int& code
, const std::string
& strFileNameAndPath
)
180 if (strFileNameAndPath
.empty())
182 CLog::Log(LOGDEBUG
, "{} empty strFileNameAndPath variable", __FUNCTION__
);
188 std::string file
= strFileNameAndPath
;
189 if (URIUtils::IsStack(strFileNameAndPath
))
190 file
= CStackDirectory::GetFirstStackedFile(strFileNameAndPath
);
192 if (URIUtils::IsInArchive(file
))
193 file
= CURL(file
).GetHostName();
195 // Try to get ctime (creation on Windows, metadata change on Linux) and mtime (modification)
196 struct __stat64 buffer
;
197 if (CFile::Stat(file
, &buffer
) == 0 && (buffer
.st_mtime
!= 0 || buffer
.st_ctime
!= 0))
199 time_t now
= time(NULL
);
201 // Prefer the modification time if it's valid, fallback to ctime
204 if (buffer
.st_mtime
!= 0 && static_cast<time_t>(buffer
.st_mtime
) <= now
)
205 addedTime
= static_cast<time_t>(buffer
.st_mtime
);
207 addedTime
= static_cast<time_t>(buffer
.st_ctime
);
209 // Use the later of the ctime and mtime
213 std::max(static_cast<time_t>(buffer
.st_ctime
), static_cast<time_t>(buffer
.st_mtime
));
214 // if the newer of the two dates is in the future, we try it with the older one
217 std::min(static_cast<time_t>(buffer
.st_ctime
), static_cast<time_t>(buffer
.st_mtime
));
219 // Prefer the earliest of ctime and mtime, fallback to other
223 std::min(static_cast<time_t>(buffer
.st_ctime
), static_cast<time_t>(buffer
.st_mtime
));
224 // if the older of the two dates is invalid, we try it with the newer one
227 std::max(static_cast<time_t>(buffer
.st_ctime
), static_cast<time_t>(buffer
.st_mtime
));
231 // make sure the datetime does is not in the future
232 if (addedTime
<= now
)
235 #ifdef HAVE_LOCALTIME_R
236 struct tm result
= {};
237 time
= localtime_r(&addedTime
, &result
);
239 time
= localtime(&addedTime
);
248 CLog::Log(LOGERROR
, "{} unable to extract modification date for file ({})", __FUNCTION__
,
254 bool CFileUtils::CheckFileAccessAllowed(const std::string
&filePath
)
256 // DENY access to paths matching
257 const std::vector
<std::string
> blacklist
= {
261 "advancedsettings.xml",
266 std::vector
<std::string
> whitelist
= {
267 CSpecialProtocol::TranslatePath("special://home"),
268 CSpecialProtocol::TranslatePath("special://xbmc"),
269 CSpecialProtocol::TranslatePath("special://musicartistsinfo"),
272 auto kodiExtraWhitelist
= CCompileInfo::GetWebserverExtraWhitelist();
273 whitelist
.insert(whitelist
.end(), kodiExtraWhitelist
.begin(), kodiExtraWhitelist
.end());
275 // image urls come in the form of image://... sometimes with a / appended at the end
276 // and can be embedded in a music or video file image://music@...
277 // strip this off to get the real file path
278 bool isImage
= false;
279 std::string decodePath
= CURL::Decode(filePath
);
280 size_t pos
= decodePath
.find("image://");
281 if (pos
!= std::string::npos
)
284 decodePath
.erase(pos
, 8);
285 URIUtils::RemoveSlashAtEnd(decodePath
);
286 if (StringUtils::StartsWith(decodePath
, "music@") || StringUtils::StartsWith(decodePath
, "video@"))
287 decodePath
.erase(pos
, 6);
291 for (const auto &b
: blacklist
)
293 if (decodePath
.find(b
) != std::string::npos
)
295 CLog::Log(LOGERROR
, "{} denied access to {}", __FUNCTION__
, decodePath
);
300 #if defined(TARGET_POSIX)
301 std::string whiteEntry
;
302 char *fullpath
= realpath(decodePath
.c_str(), nullptr);
304 // if this is a locally existing file, check access permissions
307 const std::string realPath
= fullpath
;
311 for (const auto &w
: whitelist
)
313 char *realtemp
= realpath(w
.c_str(), nullptr);
316 whiteEntry
= realtemp
;
319 if (StringUtils::StartsWith(realPath
, whiteEntry
))
322 // check sources with realPath
323 return CFileUtils::RemoteAccessAllowed(realPath
);
325 #elif defined(TARGET_WINDOWS)
326 CURL
url(decodePath
);
327 if (url
.GetProtocol().empty())
329 std::wstring decodePathW
;
330 g_charsetConverter
.utf8ToW(decodePath
, decodePathW
, false);
331 CWIN32Util::AddExtraLongPathPrefix(decodePathW
);
332 DWORD bufSize
= GetFullPathNameW(decodePathW
.c_str(), 0, nullptr, nullptr);
335 std::wstring fullpathW
;
336 fullpathW
.resize(bufSize
);
337 if (GetFullPathNameW(decodePathW
.c_str(), bufSize
, const_cast<wchar_t*>(fullpathW
.c_str()), nullptr) <= bufSize
- 1)
339 CWIN32Util::RemoveExtraLongPathPrefix(fullpathW
);
340 std::string fullpath
;
341 g_charsetConverter
.wToUTF8(fullpathW
, fullpath
, false);
342 for (const std::string
& whiteEntry
: whitelist
)
344 if (StringUtils::StartsWith(fullpath
, whiteEntry
))
347 return CFileUtils::RemoteAccessAllowed(fullpath
);
352 // if it isn't a local file, it must be a vfs entry
354 return CFileUtils::RemoteAccessAllowed(decodePath
);
358 bool CFileUtils::Exists(const std::string
& strFileName
, bool bUseCache
)
360 return CFile::Exists(strFileName
, bUseCache
);