PageRenderTime 56ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/xbmc/filesystem/ZipFile.cpp

https://github.com/energy6/xbmc
C++ | 513 lines | 369 code | 45 blank | 99 comment | 94 complexity | 08c0e3578d9c88667b9662e5ff55e22f MD5 | raw file
  1. /*
  2. * Copyright (C) 2005-2013 Team XBMC
  3. * http://www.xbmc.org
  4. *
  5. * This Program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2, or (at your option)
  8. * any later version.
  9. *
  10. * This Program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with XBMC; see the file COPYING. If not, see
  17. * <http://www.gnu.org/licenses/>.
  18. *
  19. */
  20. #include "ZipFile.h"
  21. #include "URL.h"
  22. #include "utils/URIUtils.h"
  23. #include <sys/stat.h>
  24. #define ZIP_CACHE_LIMIT 4*1024*1024
  25. using namespace XFILE;
  26. using namespace std;
  27. CZipFile::CZipFile()
  28. {
  29. m_szStringBuffer = NULL;
  30. m_szStartOfStringBuffer = NULL;
  31. m_iDataInStringBuffer = 0;
  32. m_bCached = false;
  33. m_iRead = -1;
  34. }
  35. CZipFile::~CZipFile()
  36. {
  37. delete[] m_szStringBuffer;
  38. Close();
  39. }
  40. bool CZipFile::Open(const CURL&url)
  41. {
  42. CStdString strOpts = url.GetOptions();
  43. CURL url2(url);
  44. url2.SetOptions("");
  45. CStdString strPath = url2.Get();
  46. if (!g_ZipManager.GetZipEntry(strPath,mZipItem))
  47. return false;
  48. if ((mZipItem.flags & 64) == 64)
  49. {
  50. CLog::Log(LOGERROR,"FileZip: encrypted file, not supported!");
  51. return false;
  52. }
  53. if ((mZipItem.method != 8) && (mZipItem.method != 0))
  54. {
  55. CLog::Log(LOGERROR,"FileZip: unsupported compression method!");
  56. return false;
  57. }
  58. if (mZipItem.method != 0 && mZipItem.usize > ZIP_CACHE_LIMIT && strOpts != "?cache=no")
  59. {
  60. if (!CFile::Exists("special://temp/" + URIUtils::GetFileName(strPath)))
  61. {
  62. url2.SetOptions("?cache=no");
  63. if (!CFile::Cache(url2.Get(), "special://temp/" + URIUtils::GetFileName(strPath)))
  64. return false;
  65. }
  66. m_bCached = true;
  67. return mFile.Open("special://temp/" + URIUtils::GetFileName(strPath));
  68. }
  69. if (!mFile.Open(url.GetHostName())) // this is the zip-file, always open binary
  70. {
  71. CLog::Log(LOGERROR,"FileZip: unable to open zip file %s!",url.GetHostName().c_str());
  72. return false;
  73. }
  74. mFile.Seek(mZipItem.offset,SEEK_SET);
  75. return InitDecompress();
  76. }
  77. bool CZipFile::InitDecompress()
  78. {
  79. m_iRead = 1;
  80. m_iFilePos = 0;
  81. m_iZipFilePos = 0;
  82. m_iAvailBuffer = 0;
  83. m_bFlush = false;
  84. m_ZStream.zalloc = Z_NULL;
  85. m_ZStream.zfree = Z_NULL;
  86. m_ZStream.opaque = Z_NULL;
  87. if( mZipItem.method != 0 )
  88. {
  89. if (inflateInit2(&m_ZStream,-MAX_WBITS) != Z_OK)
  90. {
  91. CLog::Log(LOGERROR,"FileZip: error initializing zlib!");
  92. return false;
  93. }
  94. }
  95. m_ZStream.next_in = (Bytef*)m_szBuffer;
  96. m_ZStream.avail_in = 0;
  97. m_ZStream.total_out = 0;
  98. return true;
  99. }
  100. int64_t CZipFile::GetLength()
  101. {
  102. return mZipItem.usize;
  103. }
  104. int64_t CZipFile::GetPosition()
  105. {
  106. if (m_bCached)
  107. return mFile.GetPosition();
  108. return m_iFilePos;
  109. }
  110. int64_t CZipFile::Seek(int64_t iFilePosition, int iWhence)
  111. {
  112. if (m_bCached)
  113. return mFile.Seek(iFilePosition,iWhence);
  114. if (mZipItem.method == 0) // this is easy
  115. {
  116. int64_t iResult;
  117. switch (iWhence)
  118. {
  119. case SEEK_SET:
  120. if (iFilePosition > mZipItem.usize)
  121. return -1;
  122. m_iFilePos = iFilePosition;
  123. m_iZipFilePos = m_iFilePos;
  124. iResult = mFile.Seek(iFilePosition+mZipItem.offset,SEEK_SET)-mZipItem.offset;
  125. return iResult;
  126. break;
  127. case SEEK_CUR:
  128. if (m_iFilePos+iFilePosition > mZipItem.usize)
  129. return -1;
  130. m_iFilePos += iFilePosition;
  131. m_iZipFilePos = m_iFilePos;
  132. iResult = mFile.Seek(iFilePosition,SEEK_CUR)-mZipItem.offset;
  133. return iResult;
  134. break;
  135. case SEEK_END:
  136. if (iFilePosition > mZipItem.usize)
  137. return -1;
  138. m_iFilePos = mZipItem.usize+iFilePosition;
  139. m_iZipFilePos = m_iFilePos;
  140. iResult = mFile.Seek(mZipItem.offset+mZipItem.usize+iFilePosition,SEEK_SET)-mZipItem.offset;
  141. return iResult;
  142. break;
  143. default:
  144. return -1;
  145. }
  146. }
  147. // here goes the stupid part..
  148. if (mZipItem.method == 8)
  149. {
  150. char temp[131072];
  151. switch (iWhence)
  152. {
  153. case SEEK_SET:
  154. if (iFilePosition == m_iFilePos)
  155. return m_iFilePos; // mp3reader does this lots-of-times
  156. if (iFilePosition > mZipItem.usize || iFilePosition < 0)
  157. return -1;
  158. // read until position in 128k blocks.. only way to do it due to format.
  159. // can't start in the middle of data since then we'd have no clue where
  160. // we are in uncompressed data..
  161. if (iFilePosition < m_iFilePos)
  162. {
  163. m_iFilePos = 0;
  164. m_iZipFilePos = 0;
  165. inflateEnd(&m_ZStream);
  166. inflateInit2(&m_ZStream,-MAX_WBITS); // simply restart zlib
  167. mFile.Seek(mZipItem.offset,SEEK_SET);
  168. m_ZStream.next_in = (Bytef*)m_szBuffer;
  169. m_ZStream.avail_in = 0;
  170. m_ZStream.total_out = 0;
  171. while (m_iFilePos < iFilePosition)
  172. {
  173. unsigned int iToRead = (iFilePosition-m_iFilePos)>131072?131072:(int)(iFilePosition-m_iFilePos);
  174. if (Read(temp,iToRead) != iToRead)
  175. return -1;
  176. }
  177. return m_iFilePos;
  178. }
  179. else // seek forward
  180. return Seek(iFilePosition-m_iFilePos,SEEK_CUR);
  181. break;
  182. case SEEK_CUR:
  183. if (iFilePosition < 0)
  184. return Seek(m_iFilePos+iFilePosition,SEEK_SET); // can't rewind stream
  185. // read until requested position, drop data
  186. if (m_iFilePos+iFilePosition > mZipItem.usize)
  187. return -1;
  188. iFilePosition += m_iFilePos;
  189. while (m_iFilePos < iFilePosition)
  190. {
  191. unsigned int iToRead = (iFilePosition-m_iFilePos)>131072?131072:(int)(iFilePosition-m_iFilePos);
  192. if (Read(temp,iToRead) != iToRead)
  193. return -1;
  194. }
  195. return m_iFilePos;
  196. break;
  197. case SEEK_END:
  198. // now this is a nasty bastard, possibly takes lotsoftime
  199. // uncompress, minding m_ZStream.total_out
  200. while( (int)m_ZStream.total_out < mZipItem.usize+iFilePosition)
  201. {
  202. unsigned int iToRead = (mZipItem.usize+iFilePosition-m_ZStream.total_out > 131072)?131072:(int)(mZipItem.usize+iFilePosition-m_ZStream.total_out);
  203. if (Read(temp,iToRead) != iToRead)
  204. return -1;
  205. }
  206. return m_iFilePos;
  207. break;
  208. default:
  209. return -1;
  210. }
  211. }
  212. return -1;
  213. }
  214. bool CZipFile::Exists(const CURL& url)
  215. {
  216. SZipEntry item;
  217. if (g_ZipManager.GetZipEntry(url.Get(),item))
  218. return true;
  219. return false;
  220. }
  221. int CZipFile::Stat(struct __stat64 *buffer)
  222. {
  223. int ret;
  224. struct tm tm = {};
  225. ret = mFile.Stat(buffer);
  226. tm.tm_sec = (mZipItem.mod_time & 0x1F) << 1;
  227. tm.tm_min = (mZipItem.mod_time & 0x7E0) >> 5;
  228. tm.tm_hour = (mZipItem.mod_time & 0xF800) >> 11;
  229. tm.tm_mday = (mZipItem.mod_date & 0x1F);
  230. tm.tm_mon = (mZipItem.mod_date & 0x1E0) >> 5;
  231. tm.tm_year = (mZipItem.mod_date & 0xFE00) >> 9;
  232. buffer->st_atime = buffer->st_ctime = buffer->st_mtime = mktime(&tm);
  233. buffer->st_size = mZipItem.usize;
  234. buffer->st_dev = (buffer->st_dev << 16) ^ (buffer->st_ino << 16);
  235. buffer->st_ino ^= mZipItem.crc32;
  236. return ret;
  237. }
  238. int CZipFile::Stat(const CURL& url, struct __stat64* buffer)
  239. {
  240. if (!g_ZipManager.GetZipEntry(url.Get(),mZipItem))
  241. return -1;
  242. memset(buffer, 0, sizeof(struct __stat64));
  243. buffer->st_gid = 0;
  244. buffer->st_atime = buffer->st_ctime = mZipItem.mod_time;
  245. buffer->st_size = mZipItem.usize;
  246. return 0;
  247. }
  248. unsigned int CZipFile::Read(void* lpBuf, int64_t uiBufSize)
  249. {
  250. if (m_bCached)
  251. return mFile.Read(lpBuf,uiBufSize);
  252. // flush what might be left in the string buffer
  253. if (m_iDataInStringBuffer > 0)
  254. {
  255. size_t iMax = static_cast<size_t>((uiBufSize>m_iDataInStringBuffer?m_iDataInStringBuffer:uiBufSize));
  256. memcpy(lpBuf,m_szStartOfStringBuffer,iMax);
  257. uiBufSize -= iMax;
  258. m_iDataInStringBuffer -= iMax;
  259. }
  260. if (mZipItem.method == 8) // deflated
  261. {
  262. uLong iDecompressed = 0;
  263. uLong prevOut = m_ZStream.total_out;
  264. while (((int)iDecompressed < uiBufSize) && ((m_iZipFilePos < mZipItem.csize) || (m_bFlush)))
  265. {
  266. m_ZStream.next_out = (Bytef*)(lpBuf)+iDecompressed;
  267. m_ZStream.avail_out = static_cast<uInt>(uiBufSize-iDecompressed);
  268. if (m_bFlush) // need to flush buffer !
  269. {
  270. int iMessage = inflate(&m_ZStream,Z_SYNC_FLUSH);
  271. m_bFlush = ((iMessage == Z_OK) && (m_ZStream.avail_out == 0))?true:false;
  272. if (!m_ZStream.avail_out) // flush filled buffer, get out of here
  273. {
  274. iDecompressed = m_ZStream.total_out-prevOut;
  275. break;
  276. }
  277. }
  278. if (!m_ZStream.avail_in)
  279. {
  280. if (!FillBuffer()) // eof!
  281. {
  282. iDecompressed = m_ZStream.total_out-prevOut;
  283. break;
  284. }
  285. }
  286. int iMessage = inflate(&m_ZStream,Z_SYNC_FLUSH);
  287. if (iMessage < 0)
  288. {
  289. Close();
  290. return 0; // READ ERROR
  291. }
  292. m_bFlush = ((iMessage == Z_OK) && (m_ZStream.avail_out == 0))?true:false; // more info in input buffer
  293. iDecompressed = m_ZStream.total_out-prevOut;
  294. }
  295. m_iFilePos += iDecompressed;
  296. return static_cast<unsigned int>(iDecompressed);
  297. }
  298. else if (mZipItem.method == 0) // uncompressed. just read from file, but mind our boundaries.
  299. {
  300. if (uiBufSize+m_iFilePos > mZipItem.csize)
  301. uiBufSize = mZipItem.csize-m_iFilePos;
  302. if (uiBufSize < 0)
  303. {
  304. return 0; // we are past eof, this shouldn't happen but test anyway
  305. }
  306. unsigned int iResult = mFile.Read(lpBuf,uiBufSize);
  307. m_iZipFilePos += iResult;
  308. m_iFilePos += iResult;
  309. return iResult;
  310. }
  311. else
  312. return false; // shouldn't happen. compression method checked in open
  313. }
  314. void CZipFile::Close()
  315. {
  316. if (mZipItem.method == 8 && !m_bCached && m_iRead != -1)
  317. inflateEnd(&m_ZStream);
  318. mFile.Close();
  319. }
  320. /* CHANGED: JM - moved to CFile
  321. bool CZipFile::ReadString(char* szLine, int iLineLength)
  322. {
  323. if (!m_szStringBuffer)
  324. {
  325. m_szStringBuffer = new char[1024]; // 1024 byte long strings per read
  326. m_szStartOfStringBuffer = m_szStringBuffer;
  327. m_iDataInStringBuffer = 0;
  328. m_iRead = 0;
  329. }
  330. bool bEof = m_iDataInStringBuffer==0;
  331. while ((iLineLength > 1) && (m_iRead > -1))
  332. {
  333. if (m_iDataInStringBuffer > 0)
  334. {
  335. bEof = false;
  336. m_iRead = 1;
  337. int iMax = (iLineLength<m_iDataInStringBuffer?iLineLength-1:m_iDataInStringBuffer-1);
  338. for( int i=0;i<iMax;++i )
  339. {
  340. if (m_szStartOfStringBuffer[i] == '\r') // mac or win32 endings
  341. {
  342. strncpy(szLine,m_szStartOfStringBuffer,i);
  343. szLine[i] = '\0';
  344. m_iDataInStringBuffer -= i+1;
  345. m_szStartOfStringBuffer += i+1;
  346. if( m_szStartOfStringBuffer[0] == '\n') // win32 endings
  347. {
  348. m_szStartOfStringBuffer++;
  349. m_iDataInStringBuffer--;
  350. }
  351. return true;
  352. }
  353. else if (m_szStartOfStringBuffer[i] == '\n') // unix or fucked up win32 endings
  354. {
  355. strncpy(szLine,m_szStartOfStringBuffer,i);
  356. szLine[i] = '\0';
  357. m_iDataInStringBuffer -= i+1;
  358. m_szStartOfStringBuffer += i+1;
  359. if (m_szStartOfStringBuffer[0] == '\r')
  360. {
  361. m_szStartOfStringBuffer++;
  362. m_iDataInStringBuffer--;
  363. }
  364. return true;
  365. }
  366. }
  367. strncpy(szLine,m_szStartOfStringBuffer,iMax);
  368. szLine += iMax;
  369. iLineLength -= iMax;
  370. m_iDataInStringBuffer -= iMax;
  371. }
  372. if (m_iRead == 1 && (m_iDataInStringBuffer == 1))
  373. {
  374. m_szStringBuffer[0] = m_szStringBuffer[1023]; // need to make sure we don't loose any '\r\n' between buffers
  375. m_iDataInStringBuffer = Read(m_szStringBuffer+1,1023);
  376. }
  377. else
  378. m_iDataInStringBuffer = Read(m_szStringBuffer,1024);
  379. m_szStartOfStringBuffer = m_szStringBuffer;
  380. if (m_iDataInStringBuffer)
  381. m_iRead = 1;
  382. else
  383. m_iRead = -1;
  384. }
  385. szLine[0] = '\0';
  386. return !bEof;
  387. }*/
  388. bool CZipFile::FillBuffer()
  389. {
  390. unsigned int sToRead = 65535;
  391. if (m_iZipFilePos+65535 > mZipItem.csize)
  392. sToRead = static_cast<int>(mZipItem.csize-m_iZipFilePos);
  393. if (sToRead <= 0)
  394. return false; // eof!
  395. if (mFile.Read(m_szBuffer,sToRead) != sToRead)
  396. return false;
  397. m_ZStream.avail_in = sToRead;
  398. m_ZStream.next_in = (Bytef*)m_szBuffer;
  399. m_iZipFilePos += sToRead;
  400. return true;
  401. }
  402. void CZipFile::DestroyBuffer(void* lpBuffer, int iBufSize)
  403. {
  404. if (!m_bFlush)
  405. return;
  406. int iMessage = Z_OK;
  407. while ((iMessage == Z_OK) && (m_ZStream.avail_out == 0))
  408. {
  409. m_ZStream.next_out = (Bytef*)lpBuffer;
  410. m_ZStream.avail_out = iBufSize;
  411. iMessage = inflate(&m_ZStream,Z_SYNC_FLUSH);
  412. }
  413. m_bFlush = false;
  414. }
  415. int CZipFile::UnpackFromMemory(string& strDest, const string& strInput, bool isGZ)
  416. {
  417. unsigned int iPos=0;
  418. int iResult=0;
  419. while( iPos+LHDR_SIZE < strInput.size() || isGZ)
  420. {
  421. if (!isGZ)
  422. {
  423. CZipManager::readHeader(strInput.data()+iPos,mZipItem);
  424. if( mZipItem.header != ZIP_LOCAL_HEADER )
  425. return iResult;
  426. if( (mZipItem.flags & 8) == 8 )
  427. {
  428. CLog::Log(LOGERROR,"FileZip: extended local header, not supported!");
  429. return iResult;
  430. }
  431. }
  432. if (!InitDecompress())
  433. return iResult;
  434. // we have a file - fill the buffer
  435. char* temp;
  436. int toRead=0;
  437. if (isGZ)
  438. {
  439. m_ZStream.avail_in = strInput.size();
  440. m_ZStream.next_in = (Bytef*)strInput.data();
  441. temp = new char[8192];
  442. toRead = 8191;
  443. }
  444. else
  445. {
  446. m_ZStream.avail_in = mZipItem.csize;
  447. m_ZStream.next_in = (Bytef*)strInput.data()+iPos+LHDR_SIZE+mZipItem.flength+mZipItem.elength;
  448. // init m_zipitem
  449. strDest.reserve(mZipItem.usize);
  450. temp = new char[mZipItem.usize+1];
  451. toRead = mZipItem.usize;
  452. }
  453. int iCurrResult;
  454. while( (iCurrResult=Read(temp,toRead)) > 0)
  455. {
  456. strDest.append(temp,temp+iCurrResult);
  457. iResult += iCurrResult;
  458. }
  459. Close();
  460. delete[] temp;
  461. iPos += LHDR_SIZE+mZipItem.flength+mZipItem.elength+mZipItem.csize;
  462. if (isGZ)
  463. break;
  464. }
  465. return iResult;
  466. }