[PVR][Estuary] Timer settings dialog: Show client name in timer type selection dialog...
[xbmc.git] / xbmc / utils / FileUtils.cpp
blobb39f75a255d4ad50348dd0e610915366a01d5dd6
1 /*
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.
7 */
9 #include "FileUtils.h"
11 #include "CompileInfo.h"
12 #include "FileOperationJob.h"
13 #include "ServiceBroker.h"
14 #include "StringUtils.h"
15 #include "URIUtils.h"
16 #include "URL.h"
17 #include "Util.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"
34 #endif
36 #include <vector>
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);
45 item->Select(true);
46 return DeleteItem(item);
49 bool CFileUtils::DeleteItem(const std::shared_ptr<CFileItem>& item)
51 if (!item || item->IsParentFolder())
52 return false;
54 // Create a temporary item list containing the file/folder for deletion
55 CFileItemPtr pItemTemp(new CFileItem(*item));
56 pItemTemp->Select(true);
57 CFileItemList items;
58 items.Add(pItemTemp);
60 // grab the real filemanager window, set up the progress bar,
61 // and process the delete action
62 CFileOperationJob op(CFileOperationJob::ActionDelete, items, "");
64 return op.DoWork();
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);
81 bool success = false;
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))
89 success = true;
91 return success;
93 return CFile::Rename(strFileAndPath, strPath);
95 return false;
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/"))
109 return true;
110 else if (StringUtils::StartsWithNoCase(realPath, "musicdb://"))
111 return true;
112 else if (StringUtils::StartsWithNoCase(realPath, "videodb://"))
113 return true;
114 else if (StringUtils::StartsWithNoCase(realPath, "library://video"))
115 return true;
116 else if (StringUtils::StartsWithNoCase(realPath, "library://music"))
117 return true;
118 else if (StringUtils::StartsWithNoCase(realPath, "sources://video"))
119 return true;
120 else if (StringUtils::StartsWithNoCase(realPath, "special://musicplaylists"))
121 return true;
122 else if (StringUtils::StartsWithNoCase(realPath, "special://profile/playlists"))
123 return true;
124 else if (StringUtils::StartsWithNoCase(realPath, "special://videoplaylists"))
125 return true;
126 else if (StringUtils::StartsWithNoCase(realPath, "special://skin"))
127 return true;
128 else if (StringUtils::StartsWithNoCase(realPath, "special://profile/addon_data"))
129 return true;
130 else if (StringUtils::StartsWithNoCase(realPath, "addons://sources"))
131 return true;
132 else if (StringUtils::StartsWithNoCase(realPath, "upnp://"))
133 return true;
134 else if (StringUtils::StartsWithNoCase(realPath, "plugin://"))
135 return true;
136 else
138 std::string strPlaylistsPath = CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_SYSTEM_PLAYLISTSPATH);
139 URIUtils::RemoveSlashAtEnd(strPlaylistsPath);
140 if (StringUtils::StartsWithNoCase(realPath, strPlaylistsPath))
141 return true;
143 bool isSource;
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)
152 return true;
154 // Check auto-mounted sources
155 VECSOURCES 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)
163 return true;
165 return false;
168 CDateTime CFileUtils::GetModificationDate(const std::string& strFileNameAndPath,
169 const bool& bUseLatestDate)
171 if (bUseLatestDate)
172 return GetModificationDate(1, strFileNameAndPath);
173 else
174 return GetModificationDate(0, strFileNameAndPath);
177 CDateTime CFileUtils::GetModificationDate(const int& code, const std::string& strFileNameAndPath)
179 CDateTime dateAdded;
180 if (strFileNameAndPath.empty())
182 CLog::Log(LOGDEBUG, "{} empty strFileNameAndPath variable", __FUNCTION__);
183 return dateAdded;
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);
200 time_t addedTime;
201 // Prefer the modification time if it's valid, fallback to ctime
202 if (code == 0)
204 if (buffer.st_mtime != 0 && static_cast<time_t>(buffer.st_mtime) <= now)
205 addedTime = static_cast<time_t>(buffer.st_mtime);
206 else
207 addedTime = static_cast<time_t>(buffer.st_ctime);
209 // Use the later of the ctime and mtime
210 else if (code == 1)
212 addedTime =
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
215 if (addedTime > now)
216 addedTime =
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
220 else
222 addedTime =
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
225 if (addedTime == 0)
226 addedTime =
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)
234 struct tm* time;
235 #ifdef HAVE_LOCALTIME_R
236 struct tm result = {};
237 time = localtime_r(&addedTime, &result);
238 #else
239 time = localtime(&addedTime);
240 #endif
241 if (time)
242 dateAdded = *time;
246 catch (...)
248 CLog::Log(LOGERROR, "{} unable to extract modification date for file ({})", __FUNCTION__,
249 strFileNameAndPath);
251 return dateAdded;
254 bool CFileUtils::CheckFileAccessAllowed(const std::string &filePath)
256 // DENY access to paths matching
257 const std::vector<std::string> blacklist = {
258 "passwords.xml",
259 "sources.xml",
260 "guisettings.xml",
261 "advancedsettings.xml",
262 "server.key",
263 "/.ssh/",
265 // ALLOW kodi paths
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)
283 isImage = true;
284 decodePath.erase(pos, 8);
285 URIUtils::RemoveSlashAtEnd(decodePath);
286 if (StringUtils::StartsWith(decodePath, "music@") || StringUtils::StartsWith(decodePath, "video@"))
287 decodePath.erase(pos, 6);
290 // check blacklist
291 for (const auto &b : blacklist)
293 if (decodePath.find(b) != std::string::npos)
295 CLog::Log(LOGERROR, "{} denied access to {}", __FUNCTION__, decodePath);
296 return false;
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
305 if (fullpath)
307 const std::string realPath = fullpath;
308 free(fullpath);
310 // check whitelist
311 for (const auto &w : whitelist)
313 char *realtemp = realpath(w.c_str(), nullptr);
314 if (realtemp)
316 whiteEntry = realtemp;
317 free(realtemp);
319 if (StringUtils::StartsWith(realPath, whiteEntry))
320 return true;
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);
333 if (bufSize > 0)
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))
345 return true;
347 return CFileUtils::RemoteAccessAllowed(fullpath);
351 #endif
352 // if it isn't a local file, it must be a vfs entry
353 if (! isImage)
354 return CFileUtils::RemoteAccessAllowed(decodePath);
355 return true;
358 bool CFileUtils::Exists(const std::string& strFileName, bool bUseCache)
360 return CFile::Exists(strFileName, bUseCache);