Merge pull request #25808 from CastagnaIT/fix_url_parse
[xbmc.git] / xbmc / filesystem / File.cpp
blob317816d792d13adddf09961e08c501f7e161c3f7
1 /*
2 * Copyright (c) 2002 Frodo
3 * Portions Copyright (c) by the authors of ffmpeg and xvid
4 * Copyright (C) 2002-2018 Team Kodi
5 * This file is part of Kodi - https://kodi.tv
7 * SPDX-License-Identifier: GPL-2.0-or-later
8 * See LICENSES/README.md for more information.
9 */
11 #include "File.h"
13 #include "Directory.h"
14 #include "DirectoryCache.h"
15 #include "FileCache.h"
16 #include "FileFactory.h"
17 #include "IFile.h"
18 #include "PasswordManager.h"
19 #include "ServiceBroker.h"
20 #include "application/ApplicationComponents.h"
21 #include "application/ApplicationPowerHandling.h"
22 #include "commons/Exception.h"
23 #include "settings/Settings.h"
24 #include "settings/SettingsComponent.h"
25 #include "utils/BitstreamStats.h"
26 #include "utils/StringUtils.h"
27 #include "utils/URIUtils.h"
28 #include "utils/log.h"
30 using namespace XFILE;
32 //////////////////////////////////////////////////////////////////////
33 // Construction/Destruction
34 //////////////////////////////////////////////////////////////////////
35 #ifndef __GNUC__
36 #pragma warning (disable:4244)
37 #endif
39 //*********************************************************************************************
40 CFile::CFile() = default;
42 //*********************************************************************************************
43 CFile::~CFile()
45 Close();
48 //*********************************************************************************************
50 bool CFile::Copy(const std::string& strFileName, const std::string& strDest, XFILE::IFileCallback* pCallback, void* pContext)
52 const CURL pathToUrl(strFileName);
53 const CURL pathToUrlDest(strDest);
54 return Copy(pathToUrl, pathToUrlDest, pCallback, pContext);
57 bool CFile::Copy(const CURL& url2, const CURL& dest, XFILE::IFileCallback* pCallback, void* pContext)
59 CFile file;
61 const std::string pathToUrl(dest.Get());
62 if (pathToUrl.empty())
63 return false;
65 // special case for zips - ignore caching
66 CURL url(url2);
67 if (StringUtils::StartsWith(url.Get(), "zip://") || URIUtils::IsInAPK(url.Get()))
68 url.SetOptions("?cache=no");
69 if (file.Open(url.Get(), READ_TRUNCATED | READ_CHUNKED))
72 CFile newFile;
73 if (URIUtils::IsHD(pathToUrl)) // create possible missing dirs
75 std::vector<std::string> tokens;
76 std::string strDirectory = URIUtils::GetDirectory(pathToUrl);
77 URIUtils::RemoveSlashAtEnd(strDirectory); // for the test below
78 if (!(strDirectory.size() == 2 && strDirectory[1] == ':'))
80 CURL url(strDirectory);
81 std::string pathsep;
82 #ifndef TARGET_POSIX
83 pathsep = "\\";
84 #else
85 pathsep = "/";
86 #endif
87 // Try to use the recursive creation first, if it fails
88 // it might not be implemented for that subsystem so let's
89 // fall back to the old method in that case
90 if (!CDirectory::Create(url))
92 StringUtils::Tokenize(url.GetFileName(), tokens, pathsep);
93 std::string strCurrPath;
94 // Handle special
95 if (!url.GetProtocol().empty())
97 pathsep = "/";
98 strCurrPath += url.GetProtocol() + "://";
99 } // If the directory has a / at the beginning, don't forget it
100 else if (strDirectory[0] == pathsep[0])
101 strCurrPath += pathsep;
103 for (const std::string& iter : tokens)
105 strCurrPath += iter + pathsep;
106 CDirectory::Create(strCurrPath);
111 if (CFile::Exists(dest))
112 CFile::Delete(dest);
113 if (!newFile.OpenForWrite(dest, true)) // overwrite always
115 file.Close();
116 return false;
119 int iBufferSize = DetermineChunkSize(file.GetChunkSize(), 128 * 1024);
121 std::vector<char> buffer(iBufferSize);
122 ssize_t iRead, iWrite;
124 unsigned long long llFileSize = file.GetLength();
125 unsigned long long llPos = 0;
127 CStopWatch timer;
128 timer.StartZero();
129 float start = 0.0f;
130 auto& components = CServiceBroker::GetAppComponents();
131 const auto appPower = components.GetComponent<CApplicationPowerHandling>();
132 while (true)
134 appPower->ResetScreenSaver();
136 iRead = file.Read(buffer.data(), buffer.size());
137 if (iRead == 0) break;
138 else if (iRead < 0)
140 CLog::Log(LOGERROR, "{} - Failed read from file {}", __FUNCTION__, url.GetRedacted());
141 llFileSize = (uint64_t)-1;
142 break;
145 /* write data and make sure we managed to write it all */
146 iWrite = 0;
147 while(iWrite < iRead)
149 ssize_t iWrite2 = newFile.Write(buffer.data() + iWrite, iRead - iWrite);
150 if(iWrite2 <=0)
151 break;
152 iWrite+=iWrite2;
155 if (iWrite != iRead)
157 CLog::Log(LOGERROR, "{} - Failed write to file {}", __FUNCTION__, dest.GetRedacted());
158 llFileSize = (uint64_t)-1;
159 break;
162 llPos += iRead;
164 // calculate the current and average speeds
165 float end = timer.GetElapsedSeconds();
167 if (pCallback && end - start > 0.5f && end)
169 start = end;
171 float averageSpeed = llPos / end;
172 int ipercent = 0;
173 if(llFileSize)
174 ipercent = 100 * llPos / llFileSize;
176 if(!pCallback->OnFileCallback(pContext, ipercent, averageSpeed))
178 CLog::Log(LOGERROR, "{} - User aborted copy", __FUNCTION__);
179 llFileSize = (uint64_t)-1;
180 break;
185 /* close both files */
186 newFile.Close();
187 file.Close();
189 /* verify that we managed to completed the file */
190 if (llFileSize && llPos != llFileSize)
192 CFile::Delete(dest);
193 return false;
195 return true;
197 return false;
200 //*********************************************************************************************
202 bool CFile::CURLCreate(const std::string &url)
204 m_curl.Parse(url);
205 return true;
208 bool CFile::CURLAddOption(XFILE::CURLOPTIONTYPE type, const char* name, const char * value)
210 switch (type){
211 case XFILE::CURL_OPTION_CREDENTIALS:
213 m_curl.SetUserName(name);
214 m_curl.SetPassword(value);
215 break;
217 case XFILE::CURL_OPTION_PROTOCOL:
218 case XFILE::CURL_OPTION_HEADER:
220 m_curl.SetProtocolOption(name, value);
221 break;
223 case XFILE::CURL_OPTION_OPTION:
225 m_curl.SetOption(name, value);
226 break;
228 default:
229 return false;
231 return true;
234 bool CFile::CURLOpen(unsigned int flags)
236 return Open(m_curl, flags);
239 bool CFile::Open(const std::string& strFileName, const unsigned int flags)
241 const CURL pathToUrl(strFileName);
242 return Open(pathToUrl, flags);
245 bool CFile::Open(const CURL& file, const unsigned int flags)
247 if (m_pFile)
249 if ((flags & READ_REOPEN) == 0)
251 CLog::Log(LOGERROR, "File::Open - already open: {}", file.GetRedacted());
252 return false;
254 else
256 return m_pFile->ReOpen(URIUtils::SubstitutePath(file));
260 m_flags = flags;
263 bool bPathInCache;
265 CURL url(URIUtils::SubstitutePath(file)), url2(url);
267 if (url2.IsProtocol("apk") || url2.IsProtocol("zip") )
268 url2.SetOptions("");
270 if (!g_directoryCache.FileExists(url2.Get(), bPathInCache) )
272 if (bPathInCache)
273 return false;
277 * There are 5 buffer modes available (configurable in as.xml)
278 * 0) Buffer all internet filesystems (like 2 but additionally also ftp, webdav, etc.)
279 * 1) Buffer all filesystems (including local)
280 * 2) Only buffer true internet filesystems (streams) (http, etc.)
281 * 3) No buffer
282 * 4) Buffer all remote (non-local) filesystems
284 if (!(m_flags & READ_NO_CACHE))
286 const std::string pathToUrl(url.Get());
287 if (URIUtils::IsDVD(pathToUrl) || URIUtils::IsBluray(pathToUrl) ||
288 (m_flags & READ_AUDIO_VIDEO))
290 const auto settings = CServiceBroker::GetSettingsComponent()->GetSettings();
292 const int cacheBufferMode = (settings)
293 ? settings->GetInt(CSettings::SETTING_FILECACHE_BUFFERMODE)
294 : CACHE_BUFFER_MODE_NETWORK;
296 if ((cacheBufferMode == CACHE_BUFFER_MODE_INTERNET &&
297 URIUtils::IsInternetStream(pathToUrl, true)) ||
298 (cacheBufferMode == CACHE_BUFFER_MODE_TRUE_INTERNET &&
299 URIUtils::IsInternetStream(pathToUrl, false)) ||
300 (cacheBufferMode == CACHE_BUFFER_MODE_NETWORK &&
301 URIUtils::IsNetworkFilesystem(pathToUrl)) ||
302 (cacheBufferMode == CACHE_BUFFER_MODE_ALL &&
303 (URIUtils::IsNetworkFilesystem(pathToUrl) || URIUtils::IsHD(pathToUrl))))
305 m_flags |= READ_CACHED;
309 if (m_flags & READ_CACHED)
311 m_pFile = std::make_unique<CFileCache>(m_flags);
313 if (!m_pFile)
314 return false;
316 return m_pFile->Open(url);
320 m_pFile.reset(CFileFactory::CreateLoader(url));
322 if (!m_pFile)
323 return false;
325 CURL authUrl(url);
326 if (CPasswordManager::GetInstance().IsURLSupported(authUrl) && authUrl.GetUserName().empty())
327 CPasswordManager::GetInstance().AuthenticateURL(authUrl);
331 if (!m_pFile->Open(authUrl))
332 return false;
334 catch (CRedirectException *pRedirectEx)
336 // the file implementation decided this item should use a different implementation.
337 // the exception will contain the new implementation.
338 CLog::Log(LOGDEBUG, "File::Open - redirecting implementation for {}", file.GetRedacted());
339 if (pRedirectEx && pRedirectEx->m_pNewFileImp)
341 std::unique_ptr<CURL> pNewUrl(pRedirectEx->m_pNewUrl);
342 m_pFile.reset(pRedirectEx->m_pNewFileImp);
343 delete pRedirectEx;
345 if (pNewUrl)
347 CURL newAuthUrl(*pNewUrl);
348 if (CPasswordManager::GetInstance().IsURLSupported(newAuthUrl) && newAuthUrl.GetUserName().empty())
349 CPasswordManager::GetInstance().AuthenticateURL(newAuthUrl);
351 if (!m_pFile->Open(newAuthUrl))
352 return false;
354 else
356 if (!m_pFile->Open(authUrl))
357 return false;
361 catch (...)
363 CLog::Log(LOGERROR, "File::Open - unknown exception when opening {}", file.GetRedacted());
364 return false;
367 if (ShouldUseStreamBuffer(url))
369 m_pBuffer = std::make_unique<CFileStreamBuffer>(0);
370 m_pBuffer->Attach(m_pFile.get());
373 if (m_flags & READ_BITRATE)
375 m_bitStreamStats = std::make_unique<BitstreamStats>();
376 m_bitStreamStats->Start();
379 return true;
381 XBMCCOMMONS_HANDLE_UNCHECKED
382 catch (...) { CLog::Log(LOGERROR, "{} - Unhandled exception", __FUNCTION__); }
383 CLog::Log(LOGERROR, "{} - Error opening {}", __FUNCTION__, file.GetRedacted());
384 return false;
387 bool CFile::ShouldUseStreamBuffer(const CURL& url)
389 if (m_flags & READ_NO_BUFFER)
390 return false;
392 if (m_flags & READ_CHUNKED || m_pFile->GetChunkSize() > 0)
393 return true;
395 // file size > 200 MB but not in optical disk
396 if (m_pFile->GetLength() > 200 * 1024 * 1024 && !URIUtils::IsDVD(url.GetShareName()))
397 return true;
399 return false;
402 bool CFile::OpenForWrite(const std::string& strFileName, bool bOverWrite)
404 const CURL pathToUrl(strFileName);
405 return OpenForWrite(pathToUrl, bOverWrite);
408 bool CFile::OpenForWrite(const CURL& file, bool bOverWrite)
412 CURL url = URIUtils::SubstitutePath(file);
413 CURL authUrl = url;
414 if (CPasswordManager::GetInstance().IsURLSupported(authUrl) && authUrl.GetUserName().empty())
415 CPasswordManager::GetInstance().AuthenticateURL(authUrl);
417 m_pFile.reset(CFileFactory::CreateLoader(url));
419 if (m_pFile && m_pFile->OpenForWrite(authUrl, bOverWrite))
421 // add this file to our directory cache (if it's stored)
422 g_directoryCache.AddFile(url.Get());
423 return true;
425 return false;
427 XBMCCOMMONS_HANDLE_UNCHECKED
428 catch(...)
430 CLog::Log(LOGERROR, "{} - Unhandled exception opening {}", __FUNCTION__, file.GetRedacted());
432 CLog::Log(LOGERROR, "{} - Error opening {}", __FUNCTION__, file.GetRedacted());
433 return false;
436 int CFile::DetermineChunkSize(const int srcChunkSize, const int reqChunkSize)
438 // Determine cache chunk size: if source chunk size is bigger than 1
439 // use source chunk size else use requested chunk size
440 return (srcChunkSize > 1 ? srcChunkSize : reqChunkSize);
443 bool CFile::Exists(const std::string& strFileName, bool bUseCache /* = true */)
445 const CURL pathToUrl(strFileName);
446 return Exists(pathToUrl, bUseCache);
449 bool CFile::Exists(const CURL& file, bool bUseCache /* = true */)
451 CURL url(URIUtils::SubstitutePath(file));
452 CURL authUrl = url;
453 if (CPasswordManager::GetInstance().IsURLSupported(authUrl) && authUrl.GetUserName().empty())
454 CPasswordManager::GetInstance().AuthenticateURL(authUrl);
458 if (bUseCache)
460 bool bPathInCache;
461 if (g_directoryCache.FileExists(url.Get(), bPathInCache))
462 return true;
463 if (bPathInCache)
464 return false;
467 std::unique_ptr<IFile> pFile(CFileFactory::CreateLoader(url));
468 if (!pFile)
469 return false;
471 return pFile->Exists(authUrl);
473 XBMCCOMMONS_HANDLE_UNCHECKED
474 catch (CRedirectException *pRedirectEx)
476 // the file implementation decided this item should use a different implementation.
477 // the exception will contain the new implementation and optional a redirected URL.
478 CLog::Log(LOGDEBUG, "File::Exists - redirecting implementation for {}", file.GetRedacted());
479 if (pRedirectEx && pRedirectEx->m_pNewFileImp)
481 std::unique_ptr<IFile> pImp(pRedirectEx->m_pNewFileImp);
482 std::unique_ptr<CURL> pNewUrl(pRedirectEx->m_pNewUrl);
483 delete pRedirectEx;
485 if (pImp)
487 if (pNewUrl)
489 if (bUseCache)
491 bool bPathInCache;
492 if (g_directoryCache.FileExists(pNewUrl->Get(), bPathInCache))
493 return true;
494 if (bPathInCache)
495 return false;
497 CURL newAuthUrl = *pNewUrl;
498 if (CPasswordManager::GetInstance().IsURLSupported(newAuthUrl) && newAuthUrl.GetUserName().empty())
499 CPasswordManager::GetInstance().AuthenticateURL(newAuthUrl);
501 return pImp->Exists(newAuthUrl);
503 else
505 return pImp->Exists(authUrl);
510 catch (...) { CLog::Log(LOGERROR, "{} - Unhandled exception", __FUNCTION__); }
511 CLog::Log(LOGERROR, "{} - Error checking for {}", __FUNCTION__, file.GetRedacted());
512 return false;
515 int CFile::Stat(struct __stat64 *buffer)
517 if (!buffer)
518 return -1;
520 if (!m_pFile)
522 *buffer = {};
523 errno = ENOENT;
524 return -1;
527 return m_pFile->Stat(buffer);
530 int CFile::Stat(const std::string& strFileName, struct __stat64* buffer)
532 const CURL pathToUrl(strFileName);
533 return Stat(pathToUrl, buffer);
536 int CFile::Stat(const CURL& file, struct __stat64* buffer)
538 if (!buffer)
539 return -1;
541 CURL url(URIUtils::SubstitutePath(file));
542 CURL authUrl = url;
543 if (CPasswordManager::GetInstance().IsURLSupported(authUrl) && authUrl.GetUserName().empty())
544 CPasswordManager::GetInstance().AuthenticateURL(authUrl);
548 std::unique_ptr<IFile> pFile(CFileFactory::CreateLoader(url));
549 if (!pFile)
550 return -1;
551 return pFile->Stat(authUrl, buffer);
553 XBMCCOMMONS_HANDLE_UNCHECKED
554 catch (CRedirectException *pRedirectEx)
556 // the file implementation decided this item should use a different implementation.
557 // the exception will contain the new implementation and optional a redirected URL.
558 CLog::Log(LOGDEBUG, "File::Stat - redirecting implementation for {}", file.GetRedacted());
559 if (pRedirectEx && pRedirectEx->m_pNewFileImp)
561 std::unique_ptr<IFile> pImp(pRedirectEx->m_pNewFileImp);
562 std::unique_ptr<CURL> pNewUrl(pRedirectEx->m_pNewUrl);
563 delete pRedirectEx;
565 if (pNewUrl)
567 if (pImp)
569 CURL newAuthUrl = *pNewUrl;
570 if (CPasswordManager::GetInstance().IsURLSupported(newAuthUrl) && newAuthUrl.GetUserName().empty())
571 CPasswordManager::GetInstance().AuthenticateURL(newAuthUrl);
573 if (!pImp->Stat(newAuthUrl, buffer))
575 return 0;
579 else
581 if (pImp.get() && !pImp->Stat(authUrl, buffer))
583 return 0;
588 catch (...) { CLog::Log(LOGERROR, "{} - Unhandled exception", __FUNCTION__); }
589 CLog::Log(LOGERROR, "{} - Error statting {}", __FUNCTION__, file.GetRedacted());
590 return -1;
593 ssize_t CFile::Read(void *lpBuf, size_t uiBufSize)
595 if (!m_pFile)
596 return -1;
597 if (lpBuf == NULL && uiBufSize != 0)
598 return -1;
600 if (uiBufSize > SSIZE_MAX)
601 uiBufSize = SSIZE_MAX;
603 if (uiBufSize == 0)
605 // "test" read with zero size
606 // some VFSs don't handle correctly null buffer pointer
607 // provide valid buffer pointer for them
608 char dummy;
609 return m_pFile->Read(&dummy, 0);
612 if(m_pBuffer)
614 if(m_flags & READ_TRUNCATED)
616 const ssize_t nBytes = m_pBuffer->sgetn(
617 (char *)lpBuf, std::min<std::streamsize>((std::streamsize)uiBufSize,
618 m_pBuffer->in_avail()));
619 if (m_bitStreamStats && nBytes>0)
620 m_bitStreamStats->AddSampleBytes(nBytes);
621 return nBytes;
623 else
625 const ssize_t nBytes = m_pBuffer->sgetn((char*)lpBuf, uiBufSize);
626 if (m_bitStreamStats && nBytes>0)
627 m_bitStreamStats->AddSampleBytes(nBytes);
628 return nBytes;
634 if(m_flags & READ_TRUNCATED)
636 const ssize_t nBytes = m_pFile->Read(lpBuf, uiBufSize);
637 if (m_bitStreamStats && nBytes>0)
638 m_bitStreamStats->AddSampleBytes(nBytes);
639 return nBytes;
641 else
643 ssize_t done = 0;
644 while((uiBufSize-done) > 0)
646 const ssize_t curr = m_pFile->Read((char*)lpBuf+done, uiBufSize-done);
647 if (curr <= 0)
649 if (curr < 0 && done == 0)
650 return -1;
652 break;
654 done+=curr;
656 if (m_bitStreamStats && done > 0)
657 m_bitStreamStats->AddSampleBytes(done);
658 return done;
661 XBMCCOMMONS_HANDLE_UNCHECKED
662 catch(...)
664 CLog::Log(LOGERROR, "{} - Unhandled exception", __FUNCTION__);
665 return -1;
667 return 0;
670 //*********************************************************************************************
671 void CFile::Close()
675 if (m_pFile)
676 m_pFile->Close();
678 m_pBuffer.reset();
679 m_pFile.reset();
681 XBMCCOMMONS_HANDLE_UNCHECKED
682 catch (...) { CLog::Log(LOGERROR, "{} - Unhandled exception", __FUNCTION__); }
685 void CFile::Flush()
689 if (m_pFile)
690 m_pFile->Flush();
692 XBMCCOMMONS_HANDLE_UNCHECKED
693 catch (...) { CLog::Log(LOGERROR, "{} - Unhandled exception", __FUNCTION__); }
696 //*********************************************************************************************
697 int64_t CFile::Seek(int64_t iFilePosition, int iWhence)
699 if (!m_pFile)
700 return -1;
702 if (m_pBuffer)
704 if(iWhence == SEEK_CUR)
705 return m_pBuffer->pubseekoff(iFilePosition, std::ios_base::cur);
706 else if(iWhence == SEEK_END)
707 return m_pBuffer->pubseekoff(iFilePosition, std::ios_base::end);
708 else if(iWhence == SEEK_SET)
709 return m_pBuffer->pubseekoff(iFilePosition, std::ios_base::beg);
714 return m_pFile->Seek(iFilePosition, iWhence);
716 XBMCCOMMONS_HANDLE_UNCHECKED
717 catch (...) { CLog::Log(LOGERROR, "{} - Unhandled exception", __FUNCTION__); }
718 return -1;
721 //*********************************************************************************************
722 int CFile::Truncate(int64_t iSize)
724 if (!m_pFile)
725 return -1;
729 return m_pFile->Truncate(iSize);
731 XBMCCOMMONS_HANDLE_UNCHECKED
732 catch (...) { CLog::Log(LOGERROR, "{} - Unhandled exception", __FUNCTION__); }
733 return -1;
736 //*********************************************************************************************
737 int64_t CFile::GetLength()
741 if (m_pFile)
742 return m_pFile->GetLength();
743 return 0;
745 XBMCCOMMONS_HANDLE_UNCHECKED
746 catch (...) { CLog::Log(LOGERROR, "{} - Unhandled exception", __FUNCTION__); }
747 return 0;
750 //*********************************************************************************************
751 int64_t CFile::GetPosition() const
753 if (!m_pFile)
754 return -1;
756 if (m_pBuffer)
757 return m_pBuffer->pubseekoff(0, std::ios_base::cur);
761 return m_pFile->GetPosition();
763 XBMCCOMMONS_HANDLE_UNCHECKED
764 catch (...) { CLog::Log(LOGERROR, "{} - Unhandled exception", __FUNCTION__); }
765 return -1;
769 //*********************************************************************************************
770 bool CFile::ReadString(char *szLine, int iLineLength)
772 if (!m_pFile || !szLine)
773 return false;
775 if (m_pBuffer)
777 typedef CFileStreamBuffer::traits_type traits;
778 CFileStreamBuffer::int_type aByte = m_pBuffer->sgetc();
780 if(aByte == traits::eof())
781 return false;
783 while(iLineLength>0)
785 aByte = m_pBuffer->sbumpc();
787 if(aByte == traits::eof())
788 break;
790 if(aByte == traits::to_int_type('\n'))
792 if(m_pBuffer->sgetc() == traits::to_int_type('\r'))
793 m_pBuffer->sbumpc();
794 break;
797 if(aByte == traits::to_int_type('\r'))
799 if(m_pBuffer->sgetc() == traits::to_int_type('\n'))
800 m_pBuffer->sbumpc();
801 break;
804 *szLine = traits::to_char_type(aByte);
805 szLine++;
806 iLineLength--;
809 // if we have no space for terminating character we failed
810 if(iLineLength==0)
811 return false;
813 *szLine = 0;
815 return true;
820 return m_pFile->ReadString(szLine, iLineLength);
822 XBMCCOMMONS_HANDLE_UNCHECKED
823 catch (...) { CLog::Log(LOGERROR, "{} - Unhandled exception", __FUNCTION__); }
824 return false;
827 ssize_t CFile::Write(const void* lpBuf, size_t uiBufSize)
829 if (!m_pFile)
830 return -1;
831 if (lpBuf == NULL && uiBufSize != 0)
832 return -1;
836 if (uiBufSize == 0 && lpBuf == NULL)
837 { // "test" write with zero size
838 // some VFSs don't handle correctly null buffer pointer
839 // provide valid buffer pointer for them
840 const char dummyBuf = 0;
841 return m_pFile->Write(&dummyBuf, 0);
844 return m_pFile->Write(lpBuf, uiBufSize);
846 XBMCCOMMONS_HANDLE_UNCHECKED
847 catch (...) { CLog::Log(LOGERROR, "{} - Unhandled exception", __FUNCTION__); }
848 return -1;
851 bool CFile::Delete(const std::string& strFileName)
853 const CURL pathToUrl(strFileName);
854 return Delete(pathToUrl);
857 bool CFile::Delete(const CURL& file)
861 CURL url(URIUtils::SubstitutePath(file));
862 CURL authUrl = url;
863 if (CPasswordManager::GetInstance().IsURLSupported(authUrl) && authUrl.GetUserName().empty())
864 CPasswordManager::GetInstance().AuthenticateURL(authUrl);
866 std::unique_ptr<IFile> pFile(CFileFactory::CreateLoader(url));
867 if (!pFile)
868 return false;
870 if(pFile->Delete(authUrl))
872 g_directoryCache.ClearFile(url.Get());
873 return true;
876 XBMCCOMMONS_HANDLE_UNCHECKED
877 catch (...) { CLog::Log(LOGERROR, "{} - Unhandled exception", __FUNCTION__); }
878 if (Exists(file))
879 CLog::Log(LOGERROR, "{} - Error deleting file {}", __FUNCTION__, file.GetRedacted());
880 return false;
883 bool CFile::Rename(const std::string& strFileName, const std::string& strNewFileName)
885 const CURL pathToUrl(strFileName);
886 const CURL pathToUrlNew(strNewFileName);
887 return Rename(pathToUrl, pathToUrlNew);
890 bool CFile::Rename(const CURL& file, const CURL& newFile)
894 CURL url(URIUtils::SubstitutePath(file));
895 CURL urlnew(URIUtils::SubstitutePath(newFile));
897 CURL authUrl = url;
898 if (CPasswordManager::GetInstance().IsURLSupported(authUrl) && authUrl.GetUserName().empty())
899 CPasswordManager::GetInstance().AuthenticateURL(authUrl);
900 CURL authUrlNew = urlnew;
901 if (CPasswordManager::GetInstance().IsURLSupported(authUrlNew) && authUrlNew.GetUserName().empty())
902 CPasswordManager::GetInstance().AuthenticateURL(authUrlNew);
904 std::unique_ptr<IFile> pFile(CFileFactory::CreateLoader(url));
905 if (!pFile)
906 return false;
908 if(pFile->Rename(authUrl, authUrlNew))
910 g_directoryCache.ClearFile(url.Get());
911 g_directoryCache.AddFile(urlnew.Get());
912 return true;
915 XBMCCOMMONS_HANDLE_UNCHECKED
916 catch (...) { CLog::Log(LOGERROR, "{} - Unhandled exception ", __FUNCTION__); }
917 CLog::Log(LOGERROR, "{} - Error renaming file {}", __FUNCTION__, file.GetRedacted());
918 return false;
921 bool CFile::SetHidden(const std::string& fileName, bool hidden)
923 const CURL pathToUrl(fileName);
924 return SetHidden(pathToUrl, hidden);
927 bool CFile::SetHidden(const CURL& file, bool hidden)
931 CURL url(URIUtils::SubstitutePath(file));
932 CURL authUrl = url;
933 if (CPasswordManager::GetInstance().IsURLSupported(authUrl) && authUrl.GetUserName().empty())
934 CPasswordManager::GetInstance().AuthenticateURL(authUrl);
936 std::unique_ptr<IFile> pFile(CFileFactory::CreateLoader(url));
937 if (!pFile)
938 return false;
940 return pFile->SetHidden(authUrl, hidden);
942 catch(...)
944 CLog::Log(LOGERROR, "{}({}) - Unhandled exception", __FUNCTION__, file.GetRedacted());
946 return false;
949 int CFile::IoControl(EIoControl request, void* param)
951 int result = -1;
952 if (!m_pFile)
953 return -1;
954 result = m_pFile->IoControl(request, param);
956 if(result == -1 && request == IOCTRL_SEEK_POSSIBLE)
958 if(m_pFile->GetLength() >= 0 && m_pFile->Seek(0, SEEK_CUR) >= 0)
959 return 1;
960 else
961 return 0;
964 return result;
967 int CFile::GetChunkSize()
969 if (m_pFile)
970 return m_pFile->GetChunkSize();
971 return 0;
974 const std::string CFile::GetProperty(XFILE::FileProperty type, const std::string &name) const
976 if (!m_pFile)
977 return "";
978 return m_pFile->GetProperty(type, name);
981 const std::vector<std::string> CFile::GetPropertyValues(XFILE::FileProperty type, const std::string &name) const
983 if (!m_pFile)
985 return std::vector<std::string>();
987 return m_pFile->GetPropertyValues(type, name);
990 ssize_t CFile::LoadFile(const std::string& filename, std::vector<uint8_t>& outputBuffer)
992 const CURL pathToUrl(filename);
993 return LoadFile(pathToUrl, outputBuffer);
996 ssize_t CFile::LoadFile(const CURL& file, std::vector<uint8_t>& outputBuffer)
999 static const size_t max_file_size = 0x7FFFFFFF;
1000 static const size_t min_chunk_size = 64 * 1024U;
1001 static const size_t max_chunk_size = 2048 * 1024U;
1003 outputBuffer.clear();
1005 if (!Open(file, READ_TRUNCATED))
1006 return 0;
1009 GetLength() will typically return values that fall into three cases:
1010 1. The real filesize. This is the typical case.
1011 2. Zero. This is the case for some http:// streams for example.
1012 3. Some value smaller than the real filesize. This is the case for an expanding file.
1014 In order to handle all three cases, we read the file in chunks, relying on Read()
1015 returning 0 at EOF. To minimize (re)allocation of the buffer, the chunksize in
1016 cases 1 and 3 is set to one byte larger than the value returned by GetLength().
1017 The chunksize in case 2 is set to the lowest value larger than min_chunk_size aligned
1018 to GetChunkSize().
1020 We fill the buffer entirely before reallocation. Thus, reallocation never occurs in case 1
1021 as the buffer is larger than the file, so we hit EOF before we hit the end of buffer.
1023 To minimize reallocation, we double the chunksize each read while chunksize is lower
1024 than max_chunk_size.
1026 int64_t filesize = GetLength();
1027 if (filesize > (int64_t)max_file_size)
1028 return 0; /* file is too large for this function */
1030 size_t chunksize = (filesize > 0) ? static_cast<size_t>(filesize + 1)
1031 : static_cast<size_t>(DetermineChunkSize(GetChunkSize(),
1032 min_chunk_size));
1033 size_t total_read = 0;
1034 while (true)
1036 if (total_read == outputBuffer.size())
1037 { // (re)alloc
1038 if (outputBuffer.size() + chunksize > max_file_size)
1040 outputBuffer.clear();
1041 return -1;
1043 outputBuffer.resize(outputBuffer.size() + chunksize);
1044 if (chunksize < max_chunk_size)
1045 chunksize *= 2;
1047 ssize_t read = Read(outputBuffer.data() + total_read, outputBuffer.size() - total_read);
1048 if (read < 0)
1050 outputBuffer.clear();
1051 return -1;
1053 total_read += read;
1054 if (!read)
1055 break;
1058 outputBuffer.resize(total_read);
1060 return total_read;
1062 catch (const std::bad_alloc&)
1064 outputBuffer.clear();
1065 CLog::LogF(LOGERROR, "Failed to load {}: out of memory", file.GetFileName());
1066 return -1;
1069 double CFile::GetDownloadSpeed()
1071 if (m_pFile)
1072 return m_pFile->GetDownloadSpeed();
1073 return 0.0;
1076 //*********************************************************************************************
1077 //*************** Stream IO for CFile objects *************************************************
1078 //*********************************************************************************************
1079 CFileStreamBuffer::~CFileStreamBuffer()
1081 sync();
1082 Detach();
1085 CFileStreamBuffer::CFileStreamBuffer(int backsize)
1086 : std::streambuf()
1087 , m_file(NULL)
1088 , m_buffer(NULL)
1089 , m_backsize(backsize)
1093 void CFileStreamBuffer::Attach(IFile *file)
1095 m_file = file;
1097 m_frontsize = CFile::DetermineChunkSize(m_file->GetChunkSize(), 64 * 1024);
1099 m_buffer = new char[m_frontsize+m_backsize];
1100 setg(0,0,0);
1101 setp(0,0);
1104 void CFileStreamBuffer::Detach()
1106 setg(0,0,0);
1107 setp(0,0);
1108 delete[] m_buffer;
1109 m_buffer = NULL;
1112 CFileStreamBuffer::int_type CFileStreamBuffer::underflow()
1114 if(gptr() < egptr())
1115 return traits_type::to_int_type(*gptr());
1117 if(!m_file)
1118 return traits_type::eof();
1120 size_t backsize = 0;
1121 if(m_backsize)
1123 backsize = (size_t)std::min<ptrdiff_t>((ptrdiff_t)m_backsize, egptr()-eback());
1124 memmove(m_buffer, egptr()-backsize, backsize);
1127 ssize_t size = m_file->Read(m_buffer+backsize, m_frontsize);
1129 if (size == 0)
1130 return traits_type::eof();
1131 else if (size < 0)
1133 CLog::LogF(LOGWARNING, "Error reading file - assuming eof");
1134 return traits_type::eof();
1137 setg(m_buffer, m_buffer+backsize, m_buffer+backsize+size);
1138 return traits_type::to_int_type(*gptr());
1141 CFileStreamBuffer::pos_type CFileStreamBuffer::seekoff(
1142 off_type offset,
1143 std::ios_base::seekdir way,
1144 std::ios_base::openmode mode)
1146 // calculate relative offset
1147 off_type aheadbytes = (egptr() - gptr());
1148 off_type pos = m_file->GetPosition() - aheadbytes;
1149 off_type offset2;
1150 if(way == std::ios_base::cur)
1151 offset2 = offset;
1152 else if(way == std::ios_base::beg)
1153 offset2 = offset - pos;
1154 else if(way == std::ios_base::end)
1155 offset2 = offset + m_file->GetLength() - pos;
1156 else
1157 return std::streampos(-1);
1159 // a non seek shouldn't modify our buffer
1160 if(offset2 == 0)
1161 return pos;
1163 // try to seek within buffer
1164 if(gptr()+offset2 >= eback() && gptr()+offset2 < egptr())
1166 gbump(offset2);
1167 return pos + offset2;
1170 // reset our buffer pointer, will
1171 // start buffering on next read
1172 setg(0,0,0);
1173 setp(0,0);
1175 int64_t position = -1;
1176 if(way == std::ios_base::cur)
1177 position = m_file->Seek(offset - aheadbytes, SEEK_CUR);
1178 else if(way == std::ios_base::end)
1179 position = m_file->Seek(offset, SEEK_END);
1180 else
1181 position = m_file->Seek(offset, SEEK_SET);
1183 if(position<0)
1184 return std::streampos(-1);
1186 return position;
1189 CFileStreamBuffer::pos_type CFileStreamBuffer::seekpos(
1190 pos_type pos,
1191 std::ios_base::openmode mode)
1193 return seekoff(pos, std::ios_base::beg, mode);
1196 std::streamsize CFileStreamBuffer::showmanyc()
1198 underflow();
1199 return egptr() - gptr();
1202 CFileStream::CFileStream(int backsize /*= 0*/) : std::istream(&m_buffer), m_buffer(backsize)
1206 CFileStream::~CFileStream()
1208 Close();
1212 bool CFileStream::Open(const CURL& filename)
1214 Close();
1216 CURL url(URIUtils::SubstitutePath(filename));
1217 m_file.reset(CFileFactory::CreateLoader(url));
1219 CURL authUrl = url;
1220 if (CPasswordManager::GetInstance().IsURLSupported(authUrl) && authUrl.GetUserName().empty())
1221 CPasswordManager::GetInstance().AuthenticateURL(authUrl);
1223 if(m_file && m_file->Open(authUrl))
1225 m_buffer.Attach(m_file.get());
1226 return true;
1229 setstate(failbit);
1230 return false;
1233 int64_t CFileStream::GetLength()
1235 return m_file->GetLength();
1238 void CFileStream::Close()
1240 if(!m_file)
1241 return;
1243 m_buffer.Detach();
1246 bool CFileStream::Open(const std::string& filename)
1248 const CURL pathToUrl(filename);
1249 return Open(pathToUrl);