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.
13 #include "Directory.h"
14 #include "DirectoryCache.h"
15 #include "FileCache.h"
16 #include "FileFactory.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/AdvancedSettings.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 //////////////////////////////////////////////////////////////////////
36 #pragma warning (disable:4244)
39 //*********************************************************************************************
40 CFile::CFile() = default;
42 //*********************************************************************************************
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
)
61 const std::string
pathToUrl(dest
.Get());
62 if (pathToUrl
.empty())
65 // special case for zips - ignore caching
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
))
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
);
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
;
95 if (!url
.GetProtocol().empty())
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
))
113 if (!newFile
.OpenForWrite(dest
, true)) // overwrite always
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;
130 auto& components
= CServiceBroker::GetAppComponents();
131 const auto appPower
= components
.GetComponent
<CApplicationPowerHandling
>();
134 appPower
->ResetScreenSaver();
136 iRead
= file
.Read(buffer
.data(), buffer
.size());
137 if (iRead
== 0) break;
140 CLog::Log(LOGERROR
, "{} - Failed read from file {}", __FUNCTION__
, url
.GetRedacted());
141 llFileSize
= (uint64_t)-1;
145 /* write data and make sure we managed to write it all */
147 while(iWrite
< iRead
)
149 ssize_t iWrite2
= newFile
.Write(buffer
.data() + iWrite
, iRead
- iWrite
);
157 CLog::Log(LOGERROR
, "{} - Failed write to file {}", __FUNCTION__
, dest
.GetRedacted());
158 llFileSize
= (uint64_t)-1;
164 // calculate the current and average speeds
165 float end
= timer
.GetElapsedSeconds();
167 if (pCallback
&& end
- start
> 0.5f
&& end
)
171 float averageSpeed
= llPos
/ end
;
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;
185 /* close both files */
189 /* verify that we managed to completed the file */
190 if (llFileSize
&& llPos
!= llFileSize
)
200 //*********************************************************************************************
202 bool CFile::CURLCreate(const std::string
&url
)
208 bool CFile::CURLAddOption(XFILE::CURLOPTIONTYPE type
, const char* name
, const char * value
)
211 case XFILE::CURL_OPTION_CREDENTIALS
:
213 m_curl
.SetUserName(name
);
214 m_curl
.SetPassword(value
);
217 case XFILE::CURL_OPTION_PROTOCOL
:
218 case XFILE::CURL_OPTION_HEADER
:
220 m_curl
.SetProtocolOption(name
, value
);
223 case XFILE::CURL_OPTION_OPTION
:
225 m_curl
.SetOption(name
, value
);
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
)
249 if ((flags
& READ_REOPEN
) == 0)
251 CLog::Log(LOGERROR
, "File::Open - already open: {}", file
.GetRedacted());
256 return m_pFile
->ReOpen(URIUtils::SubstitutePath(file
));
265 CURL
url(URIUtils::SubstitutePath(file
)), url2(url
);
267 if (url2
.IsProtocol("apk") || url2
.IsProtocol("zip") )
270 if (!g_directoryCache
.FileExists(url2
.Get(), bPathInCache
) )
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.)
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 unsigned int iCacheBufferMode
=
291 CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_cacheBufferMode
;
292 if ((iCacheBufferMode
== CACHE_BUFFER_MODE_INTERNET
&&
293 URIUtils::IsInternetStream(pathToUrl
, true)) ||
294 (iCacheBufferMode
== CACHE_BUFFER_MODE_TRUE_INTERNET
&&
295 URIUtils::IsInternetStream(pathToUrl
, false)) ||
296 (iCacheBufferMode
== CACHE_BUFFER_MODE_NETWORK
&&
297 URIUtils::IsNetworkFilesystem(pathToUrl
)) ||
298 (iCacheBufferMode
== CACHE_BUFFER_MODE_ALL
&&
299 (URIUtils::IsNetworkFilesystem(pathToUrl
) || URIUtils::IsHD(pathToUrl
))))
301 m_flags
|= READ_CACHED
;
305 if (m_flags
& READ_CACHED
)
307 m_pFile
= std::make_unique
<CFileCache
>(m_flags
);
312 return m_pFile
->Open(url
);
316 m_pFile
.reset(CFileFactory::CreateLoader(url
));
322 if (CPasswordManager::GetInstance().IsURLSupported(authUrl
) && authUrl
.GetUserName().empty())
323 CPasswordManager::GetInstance().AuthenticateURL(authUrl
);
327 if (!m_pFile
->Open(authUrl
))
330 catch (CRedirectException
*pRedirectEx
)
332 // the file implementation decided this item should use a different implementation.
333 // the exception will contain the new implementation.
334 CLog::Log(LOGDEBUG
, "File::Open - redirecting implementation for {}", file
.GetRedacted());
335 if (pRedirectEx
&& pRedirectEx
->m_pNewFileImp
)
337 std::unique_ptr
<CURL
> pNewUrl(pRedirectEx
->m_pNewUrl
);
338 m_pFile
.reset(pRedirectEx
->m_pNewFileImp
);
343 CURL
newAuthUrl(*pNewUrl
);
344 if (CPasswordManager::GetInstance().IsURLSupported(newAuthUrl
) && newAuthUrl
.GetUserName().empty())
345 CPasswordManager::GetInstance().AuthenticateURL(newAuthUrl
);
347 if (!m_pFile
->Open(newAuthUrl
))
352 if (!m_pFile
->Open(authUrl
))
359 CLog::Log(LOGERROR
, "File::Open - unknown exception when opening {}", file
.GetRedacted());
363 if (m_pFile
->GetChunkSize() && !(m_flags
& READ_CHUNKED
))
365 m_pBuffer
= std::make_unique
<CFileStreamBuffer
>(0);
366 m_pBuffer
->Attach(m_pFile
.get());
369 if (m_flags
& READ_BITRATE
)
371 m_bitStreamStats
= std::make_unique
<BitstreamStats
>();
372 m_bitStreamStats
->Start();
377 XBMCCOMMONS_HANDLE_UNCHECKED
378 catch (...) { CLog::Log(LOGERROR
, "{} - Unhandled exception", __FUNCTION__
); }
379 CLog::Log(LOGERROR
, "{} - Error opening {}", __FUNCTION__
, file
.GetRedacted());
383 bool CFile::OpenForWrite(const std::string
& strFileName
, bool bOverWrite
)
385 const CURL
pathToUrl(strFileName
);
386 return OpenForWrite(pathToUrl
, bOverWrite
);
389 bool CFile::OpenForWrite(const CURL
& file
, bool bOverWrite
)
393 CURL url
= URIUtils::SubstitutePath(file
);
395 if (CPasswordManager::GetInstance().IsURLSupported(authUrl
) && authUrl
.GetUserName().empty())
396 CPasswordManager::GetInstance().AuthenticateURL(authUrl
);
398 m_pFile
.reset(CFileFactory::CreateLoader(url
));
400 if (m_pFile
&& m_pFile
->OpenForWrite(authUrl
, bOverWrite
))
402 // add this file to our directory cache (if it's stored)
403 g_directoryCache
.AddFile(url
.Get());
408 XBMCCOMMONS_HANDLE_UNCHECKED
411 CLog::Log(LOGERROR
, "{} - Unhandled exception opening {}", __FUNCTION__
, file
.GetRedacted());
413 CLog::Log(LOGERROR
, "{} - Error opening {}", __FUNCTION__
, file
.GetRedacted());
417 int CFile::DetermineChunkSize(const int srcChunkSize
, const int reqChunkSize
)
419 // Determine cache chunk size: if source chunk size is bigger than 1
420 // use source chunk size else use requested chunk size
421 return (srcChunkSize
> 1 ? srcChunkSize
: reqChunkSize
);
424 bool CFile::Exists(const std::string
& strFileName
, bool bUseCache
/* = true */)
426 const CURL
pathToUrl(strFileName
);
427 return Exists(pathToUrl
, bUseCache
);
430 bool CFile::Exists(const CURL
& file
, bool bUseCache
/* = true */)
432 CURL
url(URIUtils::SubstitutePath(file
));
434 if (CPasswordManager::GetInstance().IsURLSupported(authUrl
) && authUrl
.GetUserName().empty())
435 CPasswordManager::GetInstance().AuthenticateURL(authUrl
);
442 if (g_directoryCache
.FileExists(url
.Get(), bPathInCache
))
448 std::unique_ptr
<IFile
> pFile(CFileFactory::CreateLoader(url
));
452 return pFile
->Exists(authUrl
);
454 XBMCCOMMONS_HANDLE_UNCHECKED
455 catch (CRedirectException
*pRedirectEx
)
457 // the file implementation decided this item should use a different implementation.
458 // the exception will contain the new implementation and optional a redirected URL.
459 CLog::Log(LOGDEBUG
, "File::Exists - redirecting implementation for {}", file
.GetRedacted());
460 if (pRedirectEx
&& pRedirectEx
->m_pNewFileImp
)
462 std::unique_ptr
<IFile
> pImp(pRedirectEx
->m_pNewFileImp
);
463 std::unique_ptr
<CURL
> pNewUrl(pRedirectEx
->m_pNewUrl
);
473 if (g_directoryCache
.FileExists(pNewUrl
->Get(), bPathInCache
))
478 CURL newAuthUrl
= *pNewUrl
;
479 if (CPasswordManager::GetInstance().IsURLSupported(newAuthUrl
) && newAuthUrl
.GetUserName().empty())
480 CPasswordManager::GetInstance().AuthenticateURL(newAuthUrl
);
482 return pImp
->Exists(newAuthUrl
);
486 return pImp
->Exists(authUrl
);
491 catch (...) { CLog::Log(LOGERROR
, "{} - Unhandled exception", __FUNCTION__
); }
492 CLog::Log(LOGERROR
, "{} - Error checking for {}", __FUNCTION__
, file
.GetRedacted());
496 int CFile::Stat(struct __stat64
*buffer
)
508 return m_pFile
->Stat(buffer
);
511 int CFile::Stat(const std::string
& strFileName
, struct __stat64
* buffer
)
513 const CURL
pathToUrl(strFileName
);
514 return Stat(pathToUrl
, buffer
);
517 int CFile::Stat(const CURL
& file
, struct __stat64
* buffer
)
522 CURL
url(URIUtils::SubstitutePath(file
));
524 if (CPasswordManager::GetInstance().IsURLSupported(authUrl
) && authUrl
.GetUserName().empty())
525 CPasswordManager::GetInstance().AuthenticateURL(authUrl
);
529 std::unique_ptr
<IFile
> pFile(CFileFactory::CreateLoader(url
));
532 return pFile
->Stat(authUrl
, buffer
);
534 XBMCCOMMONS_HANDLE_UNCHECKED
535 catch (CRedirectException
*pRedirectEx
)
537 // the file implementation decided this item should use a different implementation.
538 // the exception will contain the new implementation and optional a redirected URL.
539 CLog::Log(LOGDEBUG
, "File::Stat - redirecting implementation for {}", file
.GetRedacted());
540 if (pRedirectEx
&& pRedirectEx
->m_pNewFileImp
)
542 std::unique_ptr
<IFile
> pImp(pRedirectEx
->m_pNewFileImp
);
543 std::unique_ptr
<CURL
> pNewUrl(pRedirectEx
->m_pNewUrl
);
550 CURL newAuthUrl
= *pNewUrl
;
551 if (CPasswordManager::GetInstance().IsURLSupported(newAuthUrl
) && newAuthUrl
.GetUserName().empty())
552 CPasswordManager::GetInstance().AuthenticateURL(newAuthUrl
);
554 if (!pImp
->Stat(newAuthUrl
, buffer
))
562 if (pImp
.get() && !pImp
->Stat(authUrl
, buffer
))
569 catch (...) { CLog::Log(LOGERROR
, "{} - Unhandled exception", __FUNCTION__
); }
570 CLog::Log(LOGERROR
, "{} - Error statting {}", __FUNCTION__
, file
.GetRedacted());
574 ssize_t
CFile::Read(void *lpBuf
, size_t uiBufSize
)
578 if (lpBuf
== NULL
&& uiBufSize
!= 0)
581 if (uiBufSize
> SSIZE_MAX
)
582 uiBufSize
= SSIZE_MAX
;
586 // "test" read with zero size
587 // some VFSs don't handle correctly null buffer pointer
588 // provide valid buffer pointer for them
590 return m_pFile
->Read(&dummy
, 0);
595 if(m_flags
& READ_TRUNCATED
)
597 const ssize_t nBytes
= m_pBuffer
->sgetn(
598 (char *)lpBuf
, std::min
<std::streamsize
>((std::streamsize
)uiBufSize
,
599 m_pBuffer
->in_avail()));
600 if (m_bitStreamStats
&& nBytes
>0)
601 m_bitStreamStats
->AddSampleBytes(nBytes
);
606 const ssize_t nBytes
= m_pBuffer
->sgetn((char*)lpBuf
, uiBufSize
);
607 if (m_bitStreamStats
&& nBytes
>0)
608 m_bitStreamStats
->AddSampleBytes(nBytes
);
615 if(m_flags
& READ_TRUNCATED
)
617 const ssize_t nBytes
= m_pFile
->Read(lpBuf
, uiBufSize
);
618 if (m_bitStreamStats
&& nBytes
>0)
619 m_bitStreamStats
->AddSampleBytes(nBytes
);
625 while((uiBufSize
-done
) > 0)
627 const ssize_t curr
= m_pFile
->Read((char*)lpBuf
+done
, uiBufSize
-done
);
630 if (curr
< 0 && done
== 0)
637 if (m_bitStreamStats
&& done
> 0)
638 m_bitStreamStats
->AddSampleBytes(done
);
642 XBMCCOMMONS_HANDLE_UNCHECKED
645 CLog::Log(LOGERROR
, "{} - Unhandled exception", __FUNCTION__
);
651 //*********************************************************************************************
662 XBMCCOMMONS_HANDLE_UNCHECKED
663 catch (...) { CLog::Log(LOGERROR
, "{} - Unhandled exception", __FUNCTION__
); }
673 XBMCCOMMONS_HANDLE_UNCHECKED
674 catch (...) { CLog::Log(LOGERROR
, "{} - Unhandled exception", __FUNCTION__
); }
677 //*********************************************************************************************
678 int64_t CFile::Seek(int64_t iFilePosition
, int iWhence
)
685 if(iWhence
== SEEK_CUR
)
686 return m_pBuffer
->pubseekoff(iFilePosition
, std::ios_base::cur
);
687 else if(iWhence
== SEEK_END
)
688 return m_pBuffer
->pubseekoff(iFilePosition
, std::ios_base::end
);
689 else if(iWhence
== SEEK_SET
)
690 return m_pBuffer
->pubseekoff(iFilePosition
, std::ios_base::beg
);
695 return m_pFile
->Seek(iFilePosition
, iWhence
);
697 XBMCCOMMONS_HANDLE_UNCHECKED
698 catch (...) { CLog::Log(LOGERROR
, "{} - Unhandled exception", __FUNCTION__
); }
702 //*********************************************************************************************
703 int CFile::Truncate(int64_t iSize
)
710 return m_pFile
->Truncate(iSize
);
712 XBMCCOMMONS_HANDLE_UNCHECKED
713 catch (...) { CLog::Log(LOGERROR
, "{} - Unhandled exception", __FUNCTION__
); }
717 //*********************************************************************************************
718 int64_t CFile::GetLength()
723 return m_pFile
->GetLength();
726 XBMCCOMMONS_HANDLE_UNCHECKED
727 catch (...) { CLog::Log(LOGERROR
, "{} - Unhandled exception", __FUNCTION__
); }
731 //*********************************************************************************************
732 int64_t CFile::GetPosition() const
738 return m_pBuffer
->pubseekoff(0, std::ios_base::cur
);
742 return m_pFile
->GetPosition();
744 XBMCCOMMONS_HANDLE_UNCHECKED
745 catch (...) { CLog::Log(LOGERROR
, "{} - Unhandled exception", __FUNCTION__
); }
750 //*********************************************************************************************
751 bool CFile::ReadString(char *szLine
, int iLineLength
)
753 if (!m_pFile
|| !szLine
)
758 typedef CFileStreamBuffer::traits_type traits
;
759 CFileStreamBuffer::int_type aByte
= m_pBuffer
->sgetc();
761 if(aByte
== traits::eof())
766 aByte
= m_pBuffer
->sbumpc();
768 if(aByte
== traits::eof())
771 if(aByte
== traits::to_int_type('\n'))
773 if(m_pBuffer
->sgetc() == traits::to_int_type('\r'))
778 if(aByte
== traits::to_int_type('\r'))
780 if(m_pBuffer
->sgetc() == traits::to_int_type('\n'))
785 *szLine
= traits::to_char_type(aByte
);
790 // if we have no space for terminating character we failed
801 return m_pFile
->ReadString(szLine
, iLineLength
);
803 XBMCCOMMONS_HANDLE_UNCHECKED
804 catch (...) { CLog::Log(LOGERROR
, "{} - Unhandled exception", __FUNCTION__
); }
808 ssize_t
CFile::Write(const void* lpBuf
, size_t uiBufSize
)
812 if (lpBuf
== NULL
&& uiBufSize
!= 0)
817 if (uiBufSize
== 0 && lpBuf
== NULL
)
818 { // "test" write with zero size
819 // some VFSs don't handle correctly null buffer pointer
820 // provide valid buffer pointer for them
821 const char dummyBuf
= 0;
822 return m_pFile
->Write(&dummyBuf
, 0);
825 return m_pFile
->Write(lpBuf
, uiBufSize
);
827 XBMCCOMMONS_HANDLE_UNCHECKED
828 catch (...) { CLog::Log(LOGERROR
, "{} - Unhandled exception", __FUNCTION__
); }
832 bool CFile::Delete(const std::string
& strFileName
)
834 const CURL
pathToUrl(strFileName
);
835 return Delete(pathToUrl
);
838 bool CFile::Delete(const CURL
& file
)
842 CURL
url(URIUtils::SubstitutePath(file
));
844 if (CPasswordManager::GetInstance().IsURLSupported(authUrl
) && authUrl
.GetUserName().empty())
845 CPasswordManager::GetInstance().AuthenticateURL(authUrl
);
847 std::unique_ptr
<IFile
> pFile(CFileFactory::CreateLoader(url
));
851 if(pFile
->Delete(authUrl
))
853 g_directoryCache
.ClearFile(url
.Get());
857 XBMCCOMMONS_HANDLE_UNCHECKED
858 catch (...) { CLog::Log(LOGERROR
, "{} - Unhandled exception", __FUNCTION__
); }
860 CLog::Log(LOGERROR
, "{} - Error deleting file {}", __FUNCTION__
, file
.GetRedacted());
864 bool CFile::Rename(const std::string
& strFileName
, const std::string
& strNewFileName
)
866 const CURL
pathToUrl(strFileName
);
867 const CURL
pathToUrlNew(strNewFileName
);
868 return Rename(pathToUrl
, pathToUrlNew
);
871 bool CFile::Rename(const CURL
& file
, const CURL
& newFile
)
875 CURL
url(URIUtils::SubstitutePath(file
));
876 CURL
urlnew(URIUtils::SubstitutePath(newFile
));
879 if (CPasswordManager::GetInstance().IsURLSupported(authUrl
) && authUrl
.GetUserName().empty())
880 CPasswordManager::GetInstance().AuthenticateURL(authUrl
);
881 CURL authUrlNew
= urlnew
;
882 if (CPasswordManager::GetInstance().IsURLSupported(authUrlNew
) && authUrlNew
.GetUserName().empty())
883 CPasswordManager::GetInstance().AuthenticateURL(authUrlNew
);
885 std::unique_ptr
<IFile
> pFile(CFileFactory::CreateLoader(url
));
889 if(pFile
->Rename(authUrl
, authUrlNew
))
891 g_directoryCache
.ClearFile(url
.Get());
892 g_directoryCache
.AddFile(urlnew
.Get());
896 XBMCCOMMONS_HANDLE_UNCHECKED
897 catch (...) { CLog::Log(LOGERROR
, "{} - Unhandled exception ", __FUNCTION__
); }
898 CLog::Log(LOGERROR
, "{} - Error renaming file {}", __FUNCTION__
, file
.GetRedacted());
902 bool CFile::SetHidden(const std::string
& fileName
, bool hidden
)
904 const CURL
pathToUrl(fileName
);
905 return SetHidden(pathToUrl
, hidden
);
908 bool CFile::SetHidden(const CURL
& file
, bool hidden
)
912 CURL
url(URIUtils::SubstitutePath(file
));
914 if (CPasswordManager::GetInstance().IsURLSupported(authUrl
) && authUrl
.GetUserName().empty())
915 CPasswordManager::GetInstance().AuthenticateURL(authUrl
);
917 std::unique_ptr
<IFile
> pFile(CFileFactory::CreateLoader(url
));
921 return pFile
->SetHidden(authUrl
, hidden
);
925 CLog::Log(LOGERROR
, "{}({}) - Unhandled exception", __FUNCTION__
, file
.GetRedacted());
930 int CFile::IoControl(EIoControl request
, void* param
)
935 result
= m_pFile
->IoControl(request
, param
);
937 if(result
== -1 && request
== IOCTRL_SEEK_POSSIBLE
)
939 if(m_pFile
->GetLength() >= 0 && m_pFile
->Seek(0, SEEK_CUR
) >= 0)
948 int CFile::GetChunkSize()
951 return m_pFile
->GetChunkSize();
955 const std::string
CFile::GetProperty(XFILE::FileProperty type
, const std::string
&name
) const
959 return m_pFile
->GetProperty(type
, name
);
962 const std::vector
<std::string
> CFile::GetPropertyValues(XFILE::FileProperty type
, const std::string
&name
) const
966 return std::vector
<std::string
>();
968 return m_pFile
->GetPropertyValues(type
, name
);
971 ssize_t
CFile::LoadFile(const std::string
& filename
, std::vector
<uint8_t>& outputBuffer
)
973 const CURL
pathToUrl(filename
);
974 return LoadFile(pathToUrl
, outputBuffer
);
977 ssize_t
CFile::LoadFile(const CURL
& file
, std::vector
<uint8_t>& outputBuffer
)
979 static const size_t max_file_size
= 0x7FFFFFFF;
980 static const size_t min_chunk_size
= 64 * 1024U;
981 static const size_t max_chunk_size
= 2048 * 1024U;
983 outputBuffer
.clear();
985 if (!Open(file
, READ_TRUNCATED
))
989 GetLength() will typically return values that fall into three cases:
990 1. The real filesize. This is the typical case.
991 2. Zero. This is the case for some http:// streams for example.
992 3. Some value smaller than the real filesize. This is the case for an expanding file.
994 In order to handle all three cases, we read the file in chunks, relying on Read()
995 returning 0 at EOF. To minimize (re)allocation of the buffer, the chunksize in
996 cases 1 and 3 is set to one byte larger than the value returned by GetLength().
997 The chunksize in case 2 is set to the lowest value larger than min_chunk_size aligned
1000 We fill the buffer entirely before reallocation. Thus, reallocation never occurs in case 1
1001 as the buffer is larger than the file, so we hit EOF before we hit the end of buffer.
1003 To minimize reallocation, we double the chunksize each read while chunksize is lower
1004 than max_chunk_size.
1006 int64_t filesize
= GetLength();
1007 if (filesize
> (int64_t)max_file_size
)
1008 return 0; /* file is too large for this function */
1010 size_t chunksize
= (filesize
> 0) ? static_cast<size_t>(filesize
+ 1)
1011 : static_cast<size_t>(DetermineChunkSize(GetChunkSize(),
1013 size_t total_read
= 0;
1016 if (total_read
== outputBuffer
.size())
1018 if (outputBuffer
.size() + chunksize
> max_file_size
)
1020 outputBuffer
.clear();
1023 outputBuffer
.resize(outputBuffer
.size() + chunksize
);
1024 if (chunksize
< max_chunk_size
)
1027 ssize_t read
= Read(outputBuffer
.data() + total_read
, outputBuffer
.size() - total_read
);
1030 outputBuffer
.clear();
1038 outputBuffer
.resize(total_read
);
1043 double CFile::GetDownloadSpeed()
1046 return m_pFile
->GetDownloadSpeed();
1050 //*********************************************************************************************
1051 //*************** Stream IO for CFile objects *************************************************
1052 //*********************************************************************************************
1053 CFileStreamBuffer::~CFileStreamBuffer()
1059 CFileStreamBuffer::CFileStreamBuffer(int backsize
)
1063 , m_backsize(backsize
)
1067 void CFileStreamBuffer::Attach(IFile
*file
)
1071 m_frontsize
= CFile::DetermineChunkSize(m_file
->GetChunkSize(), 64 * 1024);
1073 m_buffer
= new char[m_frontsize
+m_backsize
];
1078 void CFileStreamBuffer::Detach()
1086 CFileStreamBuffer::int_type
CFileStreamBuffer::underflow()
1088 if(gptr() < egptr())
1089 return traits_type::to_int_type(*gptr());
1092 return traits_type::eof();
1094 size_t backsize
= 0;
1097 backsize
= (size_t)std::min
<ptrdiff_t>((ptrdiff_t)m_backsize
, egptr()-eback());
1098 memmove(m_buffer
, egptr()-backsize
, backsize
);
1101 ssize_t size
= m_file
->Read(m_buffer
+backsize
, m_frontsize
);
1104 return traits_type::eof();
1107 CLog::LogF(LOGWARNING
, "Error reading file - assuming eof");
1108 return traits_type::eof();
1111 setg(m_buffer
, m_buffer
+backsize
, m_buffer
+backsize
+size
);
1112 return traits_type::to_int_type(*gptr());
1115 CFileStreamBuffer::pos_type
CFileStreamBuffer::seekoff(
1117 std::ios_base::seekdir way
,
1118 std::ios_base::openmode mode
)
1120 // calculate relative offset
1121 off_type aheadbytes
= (egptr() - gptr());
1122 off_type pos
= m_file
->GetPosition() - aheadbytes
;
1124 if(way
== std::ios_base::cur
)
1126 else if(way
== std::ios_base::beg
)
1127 offset2
= offset
- pos
;
1128 else if(way
== std::ios_base::end
)
1129 offset2
= offset
+ m_file
->GetLength() - pos
;
1131 return std::streampos(-1);
1133 // a non seek shouldn't modify our buffer
1137 // try to seek within buffer
1138 if(gptr()+offset2
>= eback() && gptr()+offset2
< egptr())
1141 return pos
+ offset2
;
1144 // reset our buffer pointer, will
1145 // start buffering on next read
1149 int64_t position
= -1;
1150 if(way
== std::ios_base::cur
)
1151 position
= m_file
->Seek(offset
- aheadbytes
, SEEK_CUR
);
1152 else if(way
== std::ios_base::end
)
1153 position
= m_file
->Seek(offset
, SEEK_END
);
1155 position
= m_file
->Seek(offset
, SEEK_SET
);
1158 return std::streampos(-1);
1163 CFileStreamBuffer::pos_type
CFileStreamBuffer::seekpos(
1165 std::ios_base::openmode mode
)
1167 return seekoff(pos
, std::ios_base::beg
, mode
);
1170 std::streamsize
CFileStreamBuffer::showmanyc()
1173 return egptr() - gptr();
1176 CFileStream::CFileStream(int backsize
/*= 0*/) : std::istream(&m_buffer
), m_buffer(backsize
)
1180 CFileStream::~CFileStream()
1186 bool CFileStream::Open(const CURL
& filename
)
1190 CURL
url(URIUtils::SubstitutePath(filename
));
1191 m_file
.reset(CFileFactory::CreateLoader(url
));
1194 if (CPasswordManager::GetInstance().IsURLSupported(authUrl
) && authUrl
.GetUserName().empty())
1195 CPasswordManager::GetInstance().AuthenticateURL(authUrl
);
1197 if(m_file
&& m_file
->Open(authUrl
))
1199 m_buffer
.Attach(m_file
.get());
1207 int64_t CFileStream::GetLength()
1209 return m_file
->GetLength();
1212 void CFileStream::Close()
1220 bool CFileStream::Open(const std::string
& filename
)
1222 const CURL
pathToUrl(filename
);
1223 return Open(pathToUrl
);