summaryrefslogtreecommitdiffstats
path: root/xbmc/utils/FileUtils.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'xbmc/utils/FileUtils.cpp')
-rw-r--r--xbmc/utils/FileUtils.cpp351
1 files changed, 351 insertions, 0 deletions
diff --git a/xbmc/utils/FileUtils.cpp b/xbmc/utils/FileUtils.cpp
new file mode 100644
index 0000000..e51f3d6
--- /dev/null
+++ b/xbmc/utils/FileUtils.cpp
@@ -0,0 +1,351 @@
1/*
2 * Copyright (C) 2010-2020 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
8
9#include "FileUtils.h"
10#include "ServiceBroker.h"
11#include "guilib/GUIKeyboardFactory.h"
12#include "utils/log.h"
13#include "guilib/LocalizeStrings.h"
14#include "JobManager.h"
15#include "FileOperationJob.h"
16#include "URIUtils.h"
17#include "filesystem/MultiPathDirectory.h"
18#include "filesystem/SpecialProtocol.h"
19#include "filesystem/StackDirectory.h"
20#include "settings/MediaSourceSettings.h"
21#include "Util.h"
22#include "StringUtils.h"
23#include "URL.h"
24#include "settings/Settings.h"
25#include "settings/SettingsComponent.h"
26#include "storage/MediaManager.h"
27#include "utils/Variant.h"
28
29#if defined(TARGET_WINDOWS)
30#include "platform/win32/WIN32Util.h"
31#include "utils/CharsetConverter.h"
32#endif
33
34#include <vector>
35
36using namespace XFILE;
37
38bool CFileUtils::DeleteItem(const std::string &strPath)
39{
40 CFileItemPtr item(new CFileItem(strPath));
41 item->SetPath(strPath);
42 item->m_bIsFolder = URIUtils::HasSlashAtEnd(strPath);
43 item->Select(true);
44 return DeleteItem(item);
45}
46
47bool CFileUtils::DeleteItem(const CFileItemPtr &item)
48{
49 if (!item || item->IsParentFolder())
50 return false;
51
52 // Create a temporary item list containing the file/folder for deletion
53 CFileItemPtr pItemTemp(new CFileItem(*item));
54 pItemTemp->Select(true);
55 CFileItemList items;
56 items.Add(pItemTemp);
57
58 // grab the real filemanager window, set up the progress bar,
59 // and process the delete action
60 CFileOperationJob op(CFileOperationJob::ActionDelete, items, "");
61
62 return op.DoWork();
63}
64
65bool CFileUtils::RenameFile(const std::string &strFile)
66{
67 std::string strFileAndPath(strFile);
68 URIUtils::RemoveSlashAtEnd(strFileAndPath);
69 std::string strFileName = URIUtils::GetFileName(strFileAndPath);
70 std::string strPath = URIUtils::GetDirectory(strFileAndPath);
71 if (CGUIKeyboardFactory::ShowAndGetInput(strFileName, CVariant{g_localizeStrings.Get(16013)}, false))
72 {
73 strPath = URIUtils::AddFileToFolder(strPath, strFileName);
74 CLog::Log(LOGINFO, "FileUtils: rename %s->%s", strFileAndPath.c_str(), strPath.c_str());
75 if (URIUtils::IsMultiPath(strFileAndPath))
76 { // special case for multipath renames - rename all the paths.
77 std::vector<std::string> paths;
78 CMultiPathDirectory::GetPaths(strFileAndPath, paths);
79 bool success = false;
80 for (unsigned int i = 0; i < paths.size(); ++i)
81 {
82 std::string filePath(paths[i]);
83 URIUtils::RemoveSlashAtEnd(filePath);
84 filePath = URIUtils::GetDirectory(filePath);
85 filePath = URIUtils::AddFileToFolder(filePath, strFileName);
86 if (CFile::Rename(paths[i], filePath))
87 success = true;
88 }
89 return success;
90 }
91 return CFile::Rename(strFileAndPath, strPath);
92 }
93 return false;
94}
95
96bool CFileUtils::RemoteAccessAllowed(const std::string &strPath)
97{
98 std::string SourceNames[] = { "programs", "files", "video", "music", "pictures" };
99
100 std::string realPath = URIUtils::GetRealPath(strPath);
101 // for rar:// and zip:// paths we need to extract the path to the archive
102 // instead of using the VFS path
103 while (URIUtils::IsInArchive(realPath))
104 realPath = CURL(realPath).GetHostName();
105
106 if (StringUtils::StartsWithNoCase(realPath, "virtualpath://upnproot/"))
107 return true;
108 else if (StringUtils::StartsWithNoCase(realPath, "musicdb://"))
109 return true;
110 else if (StringUtils::StartsWithNoCase(realPath, "videodb://"))
111 return true;
112 else if (StringUtils::StartsWithNoCase(realPath, "library://video"))
113 return true;
114 else if (StringUtils::StartsWithNoCase(realPath, "library://music"))
115 return true;
116 else if (StringUtils::StartsWithNoCase(realPath, "sources://video"))
117 return true;
118 else if (StringUtils::StartsWithNoCase(realPath, "special://musicplaylists"))
119 return true;
120 else if (StringUtils::StartsWithNoCase(realPath, "special://profile/playlists"))
121 return true;
122 else if (StringUtils::StartsWithNoCase(realPath, "special://videoplaylists"))
123 return true;
124 else if (StringUtils::StartsWithNoCase(realPath, "special://skin"))
125 return true;
126 else if (StringUtils::StartsWithNoCase(realPath, "special://profile/addon_data"))
127 return true;
128 else if (StringUtils::StartsWithNoCase(realPath, "addons://sources"))
129 return true;
130 else if (StringUtils::StartsWithNoCase(realPath, "upnp://"))
131 return true;
132 else if (StringUtils::StartsWithNoCase(realPath, "plugin://"))
133 return true;
134 else
135 {
136 std::string strPlaylistsPath = CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_SYSTEM_PLAYLISTSPATH);
137 URIUtils::RemoveSlashAtEnd(strPlaylistsPath);
138 if (StringUtils::StartsWithNoCase(realPath, strPlaylistsPath))
139 return true;
140 }
141 bool isSource;
142 // Check manually added sources (held in sources.xml)
143 for (const std::string& sourceName : SourceNames)
144 {
145 VECSOURCES* sources = CMediaSourceSettings::GetInstance().GetSources(sourceName);
146 int sourceIndex = CUtil::GetMatchingSource(realPath, *sources, isSource);
147 if (sourceIndex >= 0 && sourceIndex < static_cast<int>(sources->size()) &&
148 sources->at(sourceIndex).m_iHasLock != LOCK_STATE_LOCKED &&
149 sources->at(sourceIndex).m_allowSharing)
150 return true;
151 }
152 // Check auto-mounted sources
153 VECSOURCES sources;
154 CServiceBroker::GetMediaManager().GetRemovableDrives(
155 sources); // Sources returned allways have m_allowsharing = true
156 //! @todo Make sharing of auto-mounted sources user configurable
157 int sourceIndex = CUtil::GetMatchingSource(realPath, sources, isSource);
158 if (sourceIndex >= 0 && sourceIndex < static_cast<int>(sources.size()) &&
159 sources.at(sourceIndex).m_iHasLock != LOCK_STATE_LOCKED &&
160 sources.at(sourceIndex).m_allowSharing)
161 return true;
162
163 return false;
164}
165
166CDateTime CFileUtils::GetModificationDate(const std::string& strFileNameAndPath,
167 const bool& bUseLatestDate)
168{
169 if (bUseLatestDate)
170 return GetModificationDate(1, strFileNameAndPath);
171 else
172 return GetModificationDate(0, strFileNameAndPath);
173}
174
175CDateTime CFileUtils::GetModificationDate(const int& code, const std::string& strFileNameAndPath)
176{
177 CDateTime dateAdded;
178 if (strFileNameAndPath.empty())
179 {
180 CLog::Log(LOGDEBUG, "%s empty strFileNameAndPath variable", __FUNCTION__);
181 return dateAdded;
182 }
183
184 try
185 {
186 std::string file = strFileNameAndPath;
187 if (URIUtils::IsStack(strFileNameAndPath))
188 file = CStackDirectory::GetFirstStackedFile(strFileNameAndPath);
189
190 if (URIUtils::IsInArchive(file))
191 file = CURL(file).GetHostName();
192
193 // Try to get ctime (creation on Windows, metadata change on Linux) and mtime (modification)
194 struct __stat64 buffer;
195 if (CFile::Stat(file, &buffer) == 0 && (buffer.st_mtime != 0 || buffer.st_ctime != 0))
196 {
197 time_t now = time(NULL);
198 time_t addedTime;
199 // Prefer the modification time if it's valid, fallback to ctime
200 if (code == 0)
201 {
202 if (buffer.st_mtime != 0 && static_cast<time_t>(buffer.st_mtime) <= now)
203 addedTime = static_cast<time_t>(buffer.st_mtime);
204 else
205 addedTime = static_cast<time_t>(buffer.st_ctime);
206 }
207 // Use the later of the ctime and mtime
208 else if (code == 1)
209 {
210 addedTime =
211 std::max(static_cast<time_t>(buffer.st_ctime), static_cast<time_t>(buffer.st_mtime));
212 // if the newer of the two dates is in the future, we try it with the older one
213 if (addedTime > now)
214 addedTime =
215 std::min(static_cast<time_t>(buffer.st_ctime), static_cast<time_t>(buffer.st_mtime));
216 }
217 // Perfer the earliest of ctime and mtime, fallback to other
218 else
219 {
220 addedTime =
221 std::min(static_cast<time_t>(buffer.st_ctime), static_cast<time_t>(buffer.st_mtime));
222 // if the older of the two dates is invalid, we try it with the newer one
223 if (addedTime == 0)
224 addedTime =
225 std::max(static_cast<time_t>(buffer.st_ctime), static_cast<time_t>(buffer.st_mtime));
226 }
227
228
229 // make sure the datetime does is not in the future
230 if (addedTime <= now)
231 {
232 struct tm* time;
233#ifdef HAVE_LOCALTIME_R
234 struct tm result = {};
235 time = localtime_r(&addedTime, &result);
236#else
237 time = localtime(&addedTime);
238#endif
239 if (time)
240 dateAdded = *time;
241 }
242 }
243 }
244 catch (...)
245 {
246 CLog::Log(LOGERROR, "%s unable to extract modification date for file (%s)", __FUNCTION__,
247 strFileNameAndPath.c_str());
248 }
249 return dateAdded;
250}
251
252bool CFileUtils::CheckFileAccessAllowed(const std::string &filePath)
253{
254 // DENY access to paths matching
255 const std::vector<std::string> blacklist = {
256 "passwords.xml",
257 "sources.xml",
258 "guisettings.xml",
259 "advancedsettings.xml",
260 "server.key",
261 "/.ssh/",
262 };
263 // ALLOW kodi paths
264 const std::vector<std::string> whitelist = {
265 CSpecialProtocol::TranslatePath("special://home"),
266 CSpecialProtocol::TranslatePath("special://xbmc"),
267 CSpecialProtocol::TranslatePath("special://musicartistsinfo")
268 };
269
270 // image urls come in the form of image://... sometimes with a / appended at the end
271 // and can be embedded in a music or video file image://music@...
272 // strip this off to get the real file path
273 bool isImage = false;
274 std::string decodePath = CURL::Decode(filePath);
275 size_t pos = decodePath.find("image://");
276 if (pos != std::string::npos)
277 {
278 isImage = true;
279 decodePath.erase(pos, 8);
280 URIUtils::RemoveSlashAtEnd(decodePath);
281 if (StringUtils::StartsWith(decodePath, "music@") || StringUtils::StartsWith(decodePath, "video@"))
282 decodePath.erase(pos, 6);
283 }
284
285 // check blacklist
286 for (const auto &b : blacklist)
287 {
288 if (decodePath.find(b) != std::string::npos)
289 {
290 CLog::Log(LOGERROR,"%s denied access to %s", __FUNCTION__, decodePath.c_str());
291 return false;
292 }
293 }
294
295#if defined(TARGET_POSIX)
296 std::string whiteEntry;
297 char *fullpath = realpath(decodePath.c_str(), nullptr);
298
299 // if this is a locally existing file, check access permissions
300 if (fullpath)
301 {
302 const std::string realPath = fullpath;
303 free(fullpath);
304
305 // check whitelist
306 for (const auto &w : whitelist)
307 {
308 char *realtemp = realpath(w.c_str(), nullptr);
309 if (realtemp)
310 {
311 whiteEntry = realtemp;
312 free(realtemp);
313 }
314 if (StringUtils::StartsWith(realPath, whiteEntry))
315 return true;
316 }
317 // check sources with realPath
318 return CFileUtils::RemoteAccessAllowed(realPath);
319 }
320#elif defined(TARGET_WINDOWS)
321 CURL url(decodePath);
322 if (url.GetProtocol().empty())
323 {
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);
328 if (bufSize > 0)
329 {
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)
333 {
334 CWIN32Util::RemoveExtraLongPathPrefix(fullpathW);
335 std::string fullpath;
336 g_charsetConverter.wToUTF8(fullpathW, fullpath, false);
337 for (const std::string& whiteEntry : whitelist)
338 {
339 if (StringUtils::StartsWith(fullpath, whiteEntry))
340 return true;
341 }
342 return CFileUtils::RemoteAccessAllowed(fullpath);
343 }
344 }
345 }
346#endif
347 // if it isn't a local file, it must be a vfs entry
348 if (! isImage)
349 return CFileUtils::RemoteAccessAllowed(decodePath);
350 return true;
351}