[Playlist][M3U] Fix long line reading
[xbmc.git] / xbmc / filesystem / File.cpp
blob9bdb0d43d87ba41eef8fa893b80a0d1f14c1aa2d
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;
768 bool XFILE::CFile::ReadString(std::vector<char>& line)
770 if (!m_pFile)
771 return false;
773 line.clear();
775 if (m_pBuffer)
777 using traits = CFileStreamBuffer::traits_type;
778 CFileStreamBuffer::int_type aByte = m_pBuffer->sgetc();
780 while (aByte != traits::eof())
782 aByte = m_pBuffer->sbumpc();
784 if (aByte == traits::eof())
785 break;
787 if (aByte == traits::to_int_type('\n'))
789 if (m_pBuffer->sgetc() == traits::to_int_type('\r'))
790 m_pBuffer->sbumpc();
791 break;
794 if (aByte == traits::to_int_type('\r'))
796 if (m_pBuffer->sgetc() == traits::to_int_type('\n'))
797 m_pBuffer->sbumpc();
798 break;
801 line.emplace_back(traits::to_char_type(aByte));
804 return true;
809 // Read by buffer chuncks until to EOL or EOF
812 char bufferLine[1025];
813 if (!m_pFile->ReadString(bufferLine, sizeof(bufferLine))) // EOF or error
814 return !line.empty();
816 const size_t length = std::strlen(bufferLine);
817 line.insert(line.end(), bufferLine, bufferLine + length);
818 } while (line.back() != '\n' && line.back() != '\r');
820 return true;
822 XBMCCOMMONS_HANDLE_UNCHECKED
823 catch (...)
825 CLog::Log(LOGERROR, "{} - Unhandled exception", __FUNCTION__);
827 return false;
830 //*********************************************************************************************
831 bool CFile::ReadString(char *szLine, int iLineLength)
833 if (!m_pFile || !szLine)
834 return false;
836 if (m_pBuffer)
838 typedef CFileStreamBuffer::traits_type traits;
839 CFileStreamBuffer::int_type aByte = m_pBuffer->sgetc();
841 if(aByte == traits::eof())
842 return false;
844 while(iLineLength>0)
846 aByte = m_pBuffer->sbumpc();
848 if(aByte == traits::eof())
849 break;
851 if(aByte == traits::to_int_type('\n'))
853 if(m_pBuffer->sgetc() == traits::to_int_type('\r'))
854 m_pBuffer->sbumpc();
855 break;
858 if(aByte == traits::to_int_type('\r'))
860 if(m_pBuffer->sgetc() == traits::to_int_type('\n'))
861 m_pBuffer->sbumpc();
862 break;
865 *szLine = traits::to_char_type(aByte);
866 szLine++;
867 iLineLength--;
870 // if we have no space for terminating character we failed
871 if(iLineLength==0)
872 return false;
874 *szLine = 0;
876 return true;
881 return m_pFile->ReadString(szLine, iLineLength);
883 XBMCCOMMONS_HANDLE_UNCHECKED
884 catch (...) { CLog::Log(LOGERROR, "{} - Unhandled exception", __FUNCTION__); }
885 return false;
888 ssize_t CFile::Write(const void* lpBuf, size_t uiBufSize)
890 if (!m_pFile)
891 return -1;
892 if (lpBuf == NULL && uiBufSize != 0)
893 return -1;
897 if (uiBufSize == 0 && lpBuf == NULL)
898 { // "test" write with zero size
899 // some VFSs don't handle correctly null buffer pointer
900 // provide valid buffer pointer for them
901 const char dummyBuf = 0;
902 return m_pFile->Write(&dummyBuf, 0);
905 return m_pFile->Write(lpBuf, uiBufSize);
907 XBMCCOMMONS_HANDLE_UNCHECKED
908 catch (...) { CLog::Log(LOGERROR, "{} - Unhandled exception", __FUNCTION__); }
909 return -1;
912 bool CFile::Delete(const std::string& strFileName)
914 const CURL pathToUrl(strFileName);
915 return Delete(pathToUrl);
918 bool CFile::Delete(const CURL& file)
922 CURL url(URIUtils::SubstitutePath(file));
923 CURL authUrl = url;
924 if (CPasswordManager::GetInstance().IsURLSupported(authUrl) && authUrl.GetUserName().empty())
925 CPasswordManager::GetInstance().AuthenticateURL(authUrl);
927 std::unique_ptr<IFile> pFile(CFileFactory::CreateLoader(url));
928 if (!pFile)
929 return false;
931 if(pFile->Delete(authUrl))
933 g_directoryCache.ClearFile(url.Get());
934 return true;
937 XBMCCOMMONS_HANDLE_UNCHECKED
938 catch (...) { CLog::Log(LOGERROR, "{} - Unhandled exception", __FUNCTION__); }
939 if (Exists(file))
940 CLog::Log(LOGERROR, "{} - Error deleting file {}", __FUNCTION__, file.GetRedacted());
941 return false;
944 bool CFile::Rename(const std::string& strFileName, const std::string& strNewFileName)
946 const CURL pathToUrl(strFileName);
947 const CURL pathToUrlNew(strNewFileName);
948 return Rename(pathToUrl, pathToUrlNew);
951 bool CFile::Rename(const CURL& file, const CURL& newFile)
955 CURL url(URIUtils::SubstitutePath(file));
956 CURL urlnew(URIUtils::SubstitutePath(newFile));
958 CURL authUrl = url;
959 if (CPasswordManager::GetInstance().IsURLSupported(authUrl) && authUrl.GetUserName().empty())
960 CPasswordManager::GetInstance().AuthenticateURL(authUrl);
961 CURL authUrlNew = urlnew;
962 if (CPasswordManager::GetInstance().IsURLSupported(authUrlNew) && authUrlNew.GetUserName().empty())
963 CPasswordManager::GetInstance().AuthenticateURL(authUrlNew);
965 std::unique_ptr<IFile> pFile(CFileFactory::CreateLoader(url));
966 if (!pFile)
967 return false;
969 if(pFile->Rename(authUrl, authUrlNew))
971 g_directoryCache.ClearFile(url.Get());
972 g_directoryCache.AddFile(urlnew.Get());
973 return true;
976 XBMCCOMMONS_HANDLE_UNCHECKED
977 catch (...) { CLog::Log(LOGERROR, "{} - Unhandled exception ", __FUNCTION__); }
978 CLog::Log(LOGERROR, "{} - Error renaming file {}", __FUNCTION__, file.GetRedacted());
979 return false;
982 bool CFile::SetHidden(const std::string& fileName, bool hidden)
984 const CURL pathToUrl(fileName);
985 return SetHidden(pathToUrl, hidden);
988 bool CFile::SetHidden(const CURL& file, bool hidden)
992 CURL url(URIUtils::SubstitutePath(file));
993 CURL authUrl = url;
994 if (CPasswordManager::GetInstance().IsURLSupported(authUrl) && authUrl.GetUserName().empty())
995 CPasswordManager::GetInstance().AuthenticateURL(authUrl);
997 std::unique_ptr<IFile> pFile(CFileFactory::CreateLoader(url));
998 if (!pFile)
999 return false;
1001 return pFile->SetHidden(authUrl, hidden);
1003 catch(...)
1005 CLog::Log(LOGERROR, "{}({}) - Unhandled exception", __FUNCTION__, file.GetRedacted());
1007 return false;
1010 int CFile::IoControl(EIoControl request, void* param)
1012 int result = -1;
1013 if (!m_pFile)
1014 return -1;
1015 result = m_pFile->IoControl(request, param);
1017 if(result == -1 && request == IOCTRL_SEEK_POSSIBLE)
1019 if(m_pFile->GetLength() >= 0 && m_pFile->Seek(0, SEEK_CUR) >= 0)
1020 return 1;
1021 else
1022 return 0;
1025 return result;
1028 int CFile::GetChunkSize()
1030 if (m_pFile)
1031 return m_pFile->GetChunkSize();
1032 return 0;
1035 const std::string CFile::GetProperty(XFILE::FileProperty type, const std::string &name) const
1037 if (!m_pFile)
1038 return "";
1039 return m_pFile->GetProperty(type, name);
1042 const std::vector<std::string> CFile::GetPropertyValues(XFILE::FileProperty type, const std::string &name) const
1044 if (!m_pFile)
1046 return std::vector<std::string>();
1048 return m_pFile->GetPropertyValues(type, name);
1051 ssize_t CFile::LoadFile(const std::string& filename, std::vector<uint8_t>& outputBuffer)
1053 const CURL pathToUrl(filename);
1054 return LoadFile(pathToUrl, outputBuffer);
1057 ssize_t CFile::LoadFile(const CURL& file, std::vector<uint8_t>& outputBuffer)
1060 static const size_t max_file_size = 0x7FFFFFFF;
1061 static const size_t min_chunk_size = 64 * 1024U;
1062 static const size_t max_chunk_size = 2048 * 1024U;
1064 outputBuffer.clear();
1066 if (!Open(file, READ_TRUNCATED))
1067 return 0;
1070 GetLength() will typically return values that fall into three cases:
1071 1. The real filesize. This is the typical case.
1072 2. Zero. This is the case for some http:// streams for example.
1073 3. Some value smaller than the real filesize. This is the case for an expanding file.
1075 In order to handle all three cases, we read the file in chunks, relying on Read()
1076 returning 0 at EOF. To minimize (re)allocation of the buffer, the chunksize in
1077 cases 1 and 3 is set to one byte larger than the value returned by GetLength().
1078 The chunksize in case 2 is set to the lowest value larger than min_chunk_size aligned
1079 to GetChunkSize().
1081 We fill the buffer entirely before reallocation. Thus, reallocation never occurs in case 1
1082 as the buffer is larger than the file, so we hit EOF before we hit the end of buffer.
1084 To minimize reallocation, we double the chunksize each read while chunksize is lower
1085 than max_chunk_size.
1087 int64_t filesize = GetLength();
1088 if (filesize > (int64_t)max_file_size)
1089 return 0; /* file is too large for this function */
1091 size_t chunksize = (filesize > 0) ? static_cast<size_t>(filesize + 1)
1092 : static_cast<size_t>(DetermineChunkSize(GetChunkSize(),
1093 min_chunk_size));
1094 size_t total_read = 0;
1095 while (true)
1097 if (total_read == outputBuffer.size())
1098 { // (re)alloc
1099 if (outputBuffer.size() + chunksize > max_file_size)
1101 outputBuffer.clear();
1102 return -1;
1104 outputBuffer.resize(outputBuffer.size() + chunksize);
1105 if (chunksize < max_chunk_size)
1106 chunksize *= 2;
1108 ssize_t read = Read(outputBuffer.data() + total_read, outputBuffer.size() - total_read);
1109 if (read < 0)
1111 outputBuffer.clear();
1112 return -1;
1114 total_read += read;
1115 if (!read)
1116 break;
1119 outputBuffer.resize(total_read);
1121 return total_read;
1123 catch (const std::bad_alloc&)
1125 outputBuffer.clear();
1126 CLog::LogF(LOGERROR, "Failed to load {}: out of memory", file.GetFileName());
1127 return -1;
1130 double CFile::GetDownloadSpeed()
1132 if (m_pFile)
1133 return m_pFile->GetDownloadSpeed();
1134 return 0.0;
1137 //*********************************************************************************************
1138 //*************** Stream IO for CFile objects *************************************************
1139 //*********************************************************************************************
1140 CFileStreamBuffer::~CFileStreamBuffer()
1142 sync();
1143 Detach();
1146 CFileStreamBuffer::CFileStreamBuffer(int backsize)
1147 : std::streambuf()
1148 , m_file(NULL)
1149 , m_buffer(NULL)
1150 , m_backsize(backsize)
1154 void CFileStreamBuffer::Attach(IFile *file)
1156 m_file = file;
1158 m_frontsize = CFile::DetermineChunkSize(m_file->GetChunkSize(), 64 * 1024);
1160 m_buffer = new char[m_frontsize+m_backsize];
1161 setg(0,0,0);
1162 setp(0,0);
1165 void CFileStreamBuffer::Detach()
1167 setg(0,0,0);
1168 setp(0,0);
1169 delete[] m_buffer;
1170 m_buffer = NULL;
1173 CFileStreamBuffer::int_type CFileStreamBuffer::underflow()
1175 if(gptr() < egptr())
1176 return traits_type::to_int_type(*gptr());
1178 if(!m_file)
1179 return traits_type::eof();
1181 size_t backsize = 0;
1182 if(m_backsize)
1184 backsize = (size_t)std::min<ptrdiff_t>((ptrdiff_t)m_backsize, egptr()-eback());
1185 memmove(m_buffer, egptr()-backsize, backsize);
1188 ssize_t size = m_file->Read(m_buffer+backsize, m_frontsize);
1190 if (size == 0)
1191 return traits_type::eof();
1192 else if (size < 0)
1194 CLog::LogF(LOGWARNING, "Error reading file - assuming eof");
1195 return traits_type::eof();
1198 setg(m_buffer, m_buffer+backsize, m_buffer+backsize+size);
1199 return traits_type::to_int_type(*gptr());
1202 CFileStreamBuffer::pos_type CFileStreamBuffer::seekoff(
1203 off_type offset,
1204 std::ios_base::seekdir way,
1205 std::ios_base::openmode mode)
1207 // calculate relative offset
1208 off_type aheadbytes = (egptr() - gptr());
1209 off_type pos = m_file->GetPosition() - aheadbytes;
1210 off_type offset2;
1211 if(way == std::ios_base::cur)
1212 offset2 = offset;
1213 else if(way == std::ios_base::beg)
1214 offset2 = offset - pos;
1215 else if(way == std::ios_base::end)
1216 offset2 = offset + m_file->GetLength() - pos;
1217 else
1218 return std::streampos(-1);
1220 // a non seek shouldn't modify our buffer
1221 if(offset2 == 0)
1222 return pos;
1224 // try to seek within buffer
1225 if(gptr()+offset2 >= eback() && gptr()+offset2 < egptr())
1227 gbump(offset2);
1228 return pos + offset2;
1231 // reset our buffer pointer, will
1232 // start buffering on next read
1233 setg(0,0,0);
1234 setp(0,0);
1236 int64_t position = -1;
1237 if(way == std::ios_base::cur)
1238 position = m_file->Seek(offset - aheadbytes, SEEK_CUR);
1239 else if(way == std::ios_base::end)
1240 position = m_file->Seek(offset, SEEK_END);
1241 else
1242 position = m_file->Seek(offset, SEEK_SET);
1244 if(position<0)
1245 return std::streampos(-1);
1247 return position;
1250 CFileStreamBuffer::pos_type CFileStreamBuffer::seekpos(
1251 pos_type pos,
1252 std::ios_base::openmode mode)
1254 return seekoff(pos, std::ios_base::beg, mode);
1257 std::streamsize CFileStreamBuffer::showmanyc()
1259 underflow();
1260 return egptr() - gptr();
1263 CFileStream::CFileStream(int backsize /*= 0*/) : std::istream(&m_buffer), m_buffer(backsize)
1267 CFileStream::~CFileStream()
1269 Close();
1273 bool CFileStream::Open(const CURL& filename)
1275 Close();
1277 CURL url(URIUtils::SubstitutePath(filename));
1278 m_file.reset(CFileFactory::CreateLoader(url));
1280 CURL authUrl = url;
1281 if (CPasswordManager::GetInstance().IsURLSupported(authUrl) && authUrl.GetUserName().empty())
1282 CPasswordManager::GetInstance().AuthenticateURL(authUrl);
1284 if(m_file && m_file->Open(authUrl))
1286 m_buffer.Attach(m_file.get());
1287 return true;
1290 setstate(failbit);
1291 return false;
1294 int64_t CFileStream::GetLength()
1296 return m_file->GetLength();
1299 void CFileStream::Close()
1301 if(!m_file)
1302 return;
1304 m_buffer.Detach();
1307 bool CFileStream::Open(const std::string& filename)
1309 const CURL pathToUrl(filename);
1310 return Open(pathToUrl);