sync with tizen_2.0
[platform/framework/native/appfw.git] / src / base / utility / FBaseUtil_FileUnzipperImpl.cpp
1 //
2 // Open Service Platform
3 // Copyright (c) 2012 Samsung Electronics Co., Ltd.
4 //
5 // Licensed under the Apache License, Version 2.0 (the License);
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 // http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 //
17
18 /**
19  * @File        :       FBaseUtil_FileUnzipperImpl.cpp
20  * @brief       :   Implementation for _FileUnzipperImpl Class
21  */
22
23 #include <new>
24 #include <errno.h>
25 #include <unistd.h>
26 #include <unzip.h>
27 #include <sys/stat.h>
28 #include <sys/types.h>
29
30 #include <FBaseResult.h>
31 #include <FBaseSysLog.h>
32 #include <FBaseUtilStringUtil.h>
33 #include <FBase_NativeError.h>
34
35 #include "FBaseUtil_ZipEntryInfo.h"
36 #include "FBaseUtil_FileUnzipperImpl.h"
37
38 using namespace Tizen::Io;
39
40 namespace Tizen {namespace Base {namespace Utility
41 {
42
43
44 #define UNZ_COMP_CASE_SENSITIVE 1
45 #define UNZ_COMP_NO_CASE_SENSITIVE  2
46
47 #define UNZ_WRITE_BUF_LEN 4096 \
48 // _BADA_SLP_FIXME : TODO - Select an optimal value for this.
49
50 struct CloseUnzipFile
51 {
52         void operator ()(void* p)
53         {
54                 if (p != null)
55                 {
56                         unzClose(p);
57                 }
58         }
59 };
60
61 struct CloseFile
62 {
63         void operator ()(FILE* p)
64         {
65                 if (p != null)
66                 {
67                         fclose(p);
68                 }
69         }
70 };
71
72 result
73 _FileUnzipperImpl::CreateDirectories(char* pFilePath)
74 {
75         result r = E_SUCCESS;
76         int ret = 0;
77
78         char* pPos = pFilePath;
79         while ((pPos = strchr(pPos + 1, '/')) != null)
80         {
81                 *pPos = '\0';
82                 if (access(pFilePath, F_OK) != 0)
83                 {
84                         ret = mkdir(pFilePath, S_IRUSR | S_IWUSR | S_IXUSR);
85                         if (ret == -1)
86                         {
87                                 r = _NativeError::ConvertNativeErrorToResult(errno, true);
88                                 SysTryReturnResult(NID_BASE_UTIL,
89                                                 (r == E_SUCCESS) || (r == E_FILE_ALREADY_EXIST), r, "Unable to create directory [%s]", pFilePath);
90                         }
91                 }
92                 *pPos = '/';
93         }
94
95         return E_SUCCESS;
96 }
97
98 bool
99 _FileUnzipperImpl::IsFileExistsInZip(const char pArchiveName[], const char* pFilePathForZip)
100 {
101         std::unique_ptr< void, CloseUnzipFile > pUnzipFile(unzOpen(pArchiveName));
102         if (pUnzipFile != null)
103         {
104                 return unzLocateFile(pUnzipFile.get(), pFilePathForZip, UNZ_COMP_CASE_SENSITIVE) != UNZ_END_OF_LIST_OF_FILE;
105         }
106         return false;
107 }
108
109
110 _FileUnzipperImpl::_FileUnzipperImpl(void)
111         : __pArchiveName(null)
112         , __entryCount(0)
113         , __dirCount(0)
114         , __fileCount(0)
115 {
116 }
117
118 _FileUnzipperImpl::~_FileUnzipperImpl(void)
119 {
120 }
121
122
123 result
124 _FileUnzipperImpl::Construct(const String& filePath)
125 {
126         SysAssertf(__pArchiveName == null, "Already constructed! "
127                         "Calling Construct() twice or more on a same instance is not allowed for this class");
128
129         std::unique_ptr< ByteBuffer > pFilePathBuff(StringUtil::StringToUtf8N(filePath));
130         SysTryReturnResult(NID_BASE_UTIL, pFilePathBuff != null, E_INVALID_ARG, "Invalid file path.");
131
132         const char* pFilePath = reinterpret_cast<const char*> (pFilePathBuff->GetPointer());
133         SysTryReturnResult(NID_BASE_UTIL, access(pFilePath, F_OK) == 0, E_FILE_NOT_FOUND, "Invalid file path [%s].", pFilePath);
134
135         std::unique_ptr< void, CloseUnzipFile > pUnzipFile(unzOpen(pFilePath));
136         SysTryReturnResult(NID_BASE_UTIL, pUnzipFile != null, E_IO, "Failed to open [%s].", pFilePath);
137
138         int dirCount = 0;
139         int fileCount = 0;
140
141         FilePath unzFileEntryName = {0};
142         unz_global_info gunzfileInfo = {0};
143         unz_file_info unzfileInfo = {0};
144
145         int err = unzGetGlobalInfo(pUnzipFile.get(), &gunzfileInfo);
146         SysTryReturnResult(NID_BASE_UTIL, err == UNZ_OK, E_IO, "Failed to open the zip entry.");
147
148         err = unzGoToFirstFile(pUnzipFile.get());
149         SysTryReturnResult(NID_BASE_UTIL, err == UNZ_OK, E_IO, "Failed to get the first file entry in the zip file.");
150
151         do
152         {
153                 err = unzGetCurrentFileInfo(pUnzipFile.get(), &unzfileInfo, unzFileEntryName, PATH_MAX, null, 0, null, 0);
154                 SysTryReturnResult(NID_BASE_UTIL, err == UNZ_OK, E_IO, "Failed to get file entry info from the zip file.");
155
156                 (unzFileEntryName[strlen(unzFileEntryName) - 1] == '/')? dirCount++ : fileCount++;
157
158                 err = unzGoToNextFile(pUnzipFile.get());
159                 SysTryReturnResult(NID_BASE_UTIL,
160                                 (err == UNZ_OK) || (err == UNZ_END_OF_LIST_OF_FILE), E_IO, "Failed to get the next entry in the zip file.");
161
162         } while (UNZ_END_OF_LIST_OF_FILE != err);
163
164         int len = strlen(pFilePath);
165         std::unique_ptr< char[] > pArchiveName(new (std::nothrow) char[len + 1]);
166         SysTryReturnResult(NID_BASE_UTIL, pArchiveName != null, E_OUT_OF_MEMORY, "Memory allocation failed.");
167
168         strncpy(pArchiveName.get(), pFilePath, len);
169         pArchiveName[len] = 0;
170
171         __pArchiveName = std::move(pArchiveName);
172         __entryCount = gunzfileInfo.number_entry;
173         __dirCount = dirCount;
174         __fileCount = fileCount;
175
176         ClearLastResult();
177         return E_SUCCESS;
178 }
179
180 result
181 _FileUnzipperImpl::UnzipCurrentFileTo(void* pUnZipFile, FilePath outPath, int usedBytes) const
182 {
183         unz_file_info unzfileInfo = {0};
184         int err = unzGetCurrentFileInfo(pUnZipFile, &unzfileInfo, &(outPath[usedBytes]), PATH_MAX - usedBytes, null, 0, null, 0);
185         SysTryReturnResult(NID_BASE_UTIL, err == UNZ_OK, E_IO, "Failed to get file entry info from the zip file.");
186
187         result r = _FileUnzipperImpl::CreateDirectories(outPath);
188         SysTryReturnResult(NID_BASE_UTIL, r == E_SUCCESS, r, "Failed to create Directory [%s]", outPath);
189
190         if(outPath[strlen(outPath) - 1] != '/')
191         {
192                 std::unique_ptr< FILE, CloseFile > pFile(fopen(outPath, "w"));
193                 r = __ConvertNativeErrorToResult(errno);
194                 SysTryReturnResult(NID_BASE_UTIL, pFile != null, r, "Failed to open file [name: %s, errorno: %d].", outPath, errno);
195
196                 err = unzOpenCurrentFile(pUnZipFile);
197                 SysTryReturnResult(NID_BASE_UTIL, err == UNZ_OK, E_IO, "Failed to open current zip file.");
198
199                 int writeCnt = 0;
200                 int readLen = 1;
201                 char writeBuf[UNZ_WRITE_BUF_LEN] = {0};
202                 while (readLen > 0)
203                 {
204                         readLen = unzReadCurrentFile(pUnZipFile, writeBuf, sizeof(writeBuf));
205                         r = (readLen < 0) ? E_IO : E_SUCCESS;
206                         SysTryCatch(NID_BASE_UTIL, r == E_SUCCESS, unzCloseCurrentFile(pUnZipFile), r,
207                                                         "[%s] Failed to read from zip file (%s).", GetErrorMessage(E_IO), outPath);
208
209                         if (readLen > 0)
210                         {
211                                 writeCnt = fwrite(writeBuf, 1, readLen, pFile.get());
212                                 if (writeCnt < readLen)
213                                 {
214                                         int ret = ferror(pFile.get());
215                                         r = __ConvertNativeErrorToResult(ret);
216                                         SysTryCatch(NID_BASE_UTIL, ret != 0, unzCloseCurrentFile(pUnZipFile), r,
217                                                                 "[%s] Failed to perform write operation.", GetErrorMessage(r));
218                                 }
219                         }
220                 }
221
222                 unzCloseCurrentFile(pUnZipFile);
223         }
224
225 CATCH:
226         return r;
227 }
228
229 result
230 _FileUnzipperImpl::UnzipTo(const String& dirPath) const
231 {
232         SysAssertf(__pArchiveName != null, "Not yet constructed! Construct() should be called before use");
233
234         String dirPathStr(dirPath);
235         dirPathStr.Replace(L"\\", L"/");
236         if (dirPathStr.EndsWith(L"/") == false)
237         {
238                 dirPathStr.Append(L"/");
239         }
240
241         std::unique_ptr< ByteBuffer > pDirBuff(StringUtil::StringToUtf8N(dirPathStr));
242         SysTryReturnResult(NID_BASE_UTIL, pDirBuff != null, E_INVALID_ARG, "Invalid file path.");
243
244         FilePath outPath = {0};
245         int dirPathLength = pDirBuff->GetCapacity() - 1;
246         strncat(outPath, reinterpret_cast<const char*> (pDirBuff->GetPointer()), dirPathLength);
247
248         std::unique_ptr< void, CloseUnzipFile > pUnzipFile(unzOpen(__pArchiveName.get()));
249         SysTryReturnResult(NID_BASE_UTIL, pUnzipFile != null, E_IO, "Failed to open [%s].", __pArchiveName.get());
250
251         int err = unzGoToFirstFile(pUnzipFile.get());
252         SysTryReturnResult(NID_BASE_UTIL, err == UNZ_OK, E_IO, "Failed to get the first file entry in the zip file.");
253
254         result r = E_SUCCESS;
255         int entryCount = __entryCount;
256         while (entryCount--)
257         {
258                 r = UnzipCurrentFileTo(pUnzipFile.get(), outPath, dirPathLength);
259                 SysTryReturnResult(NID_BASE_UTIL, r == E_SUCCESS, r, "Failed to unzip current file");
260
261                 if (entryCount)
262                 {
263                         err = unzGoToNextFile(pUnzipFile.get());
264                         SysTryReturnResult(NID_BASE_UTIL, err == UNZ_OK, E_IO, "Failed to fetch the next file entry in the zip file.");
265                 }
266         }
267
268         return r;
269 }
270
271 result
272 _FileUnzipperImpl::UnzipTo(const String& dirPath, const String& zipEntryName) const
273 {
274         SysAssertf(__pArchiveName != null, "Not yet constructed! Construct() should be called before use");
275
276         String dirPathStr = dirPath;
277         dirPathStr.Replace(L"\\", L"/");
278         if (dirPathStr.EndsWith(L"/") == false)
279         {
280                 dirPathStr.Append(L"/");
281         }
282
283         std::unique_ptr< ByteBuffer > pDirBuff(StringUtil::StringToUtf8N(dirPathStr));
284         SysTryReturnResult(NID_BASE_UTIL, pDirBuff != null, E_INVALID_ARG, "Invalid file path.");
285
286         FilePath outPath = {0};
287         int dirPathLength = pDirBuff->GetCapacity() - 1;
288         strncat(outPath, reinterpret_cast<const char*> (pDirBuff->GetPointer()), dirPathLength);
289
290         std::unique_ptr< ByteBuffer > pZipEntryBuff(StringUtil::StringToUtf8N(zipEntryName));
291         SysTryReturnResult(NID_BASE_UTIL, pZipEntryBuff != null, E_INVALID_ARG, "[%s] Invalid zip Entry Name.", GetErrorMessage(E_INVALID_ARG));
292
293         const char* pZipEntryName = reinterpret_cast<const char*> (pZipEntryBuff->GetPointer());
294
295         std::unique_ptr< void, CloseUnzipFile > pUnzipFile(unzOpen(__pArchiveName.get()));
296         SysTryReturnResult(NID_BASE_UTIL, pUnzipFile != null, E_IO, "Failed to open [%s].", __pArchiveName.get());
297
298         int err = unzLocateFile(pUnzipFile.get(), pZipEntryName, UNZ_COMP_CASE_SENSITIVE);
299         SysTryReturnResult(NID_BASE_UTIL, err == UNZ_OK, E_FILE_NOT_FOUND, "Failed to locate file entry (%s).", pZipEntryName);
300
301         return UnzipCurrentFileTo(pUnzipFile.get(), outPath, dirPathLength);
302 }
303
304 int
305 _FileUnzipperImpl::GetEntryCount(void) const
306 {
307         SysAssertf(__pArchiveName != null, "Not yet constructed! Construct() should be called before use");
308         return __entryCount;
309 }
310
311 int
312 _FileUnzipperImpl::GetFileCount(void) const
313 {
314         SysAssertf(__pArchiveName != null, "Not yet constructed! Construct() should be called before use");
315         return __fileCount;
316 }
317
318
319 int
320 _FileUnzipperImpl::GetDirectoryCount(void) const
321 {
322         SysAssertf(__pArchiveName != null, "Not yet constructed! Construct() should be called before use");
323         return __dirCount;
324 }
325
326 result
327 _FileUnzipperImpl::GetCurrentFileInfo(void* pUnZipFile, ZipEntry& entry) const
328 {
329         unz_file_info unzFileInfo = {0};
330         char unzFileEntryName[PATH_MAX + 1];
331
332         // after locate, the current file is the entry, fetch its info
333         int err = unzGetCurrentFileInfo(pUnZipFile, &unzFileInfo, unzFileEntryName, PATH_MAX, null, 0, null, 0);
334         SysTryReturnResult(NID_BASE_UTIL, err == UNZ_OK, E_IO, "Failed to fetch information of current file in the zip entry.");
335
336         String fileEntryName;
337         result r = StringUtil::Utf8ToString(unzFileEntryName, fileEntryName);
338         SysTryReturnResult(NID_BASE_UTIL, r == E_SUCCESS, E_IO, "Invalid file path in Zip file.");
339
340         String archieveName;
341         r = StringUtil::Utf8ToString(__pArchiveName.get(), archieveName);
342         SysTryReturnResult(NID_BASE_UTIL, r == E_SUCCESS, E_IO, "Invalid file path in Zip file.");
343
344         int method = 0;
345         int level = DEFAULT_COMPRESSION;
346
347         err = unzOpenCurrentFile2(pUnZipFile, &method, &level, 1);
348         SysTryReturnResult(NID_BASE_UTIL, err == UNZ_OK, E_IO, "Failed to open the current entry in the zip file.");
349
350         err = unzCloseCurrentFile(pUnZipFile);
351         SysTryReturnResult(NID_BASE_UTIL, err == UNZ_OK, E_IO, "Failed to close the zip entry.");
352
353         _ZipEntryInfo* pZipEntryInfo = null;
354         if (entry.__unzFile == null)
355         {
356                 pZipEntryInfo = new (std::nothrow) _ZipEntryInfo;
357                 SysTryReturnResult(NID_BASE_UTIL, pZipEntryInfo != null, E_OUT_OF_MEMORY, "Memory allocation failed.");
358         }
359         else
360         {
361                 pZipEntryInfo = static_cast <_ZipEntryInfo*>(entry.__unzFile);
362         }
363
364 //      pZipEntryInfo->__unzFile = __pUzFile;
365         pZipEntryInfo->__name = fileEntryName;
366         if (level == Z_BEST_SPEED)
367         {
368                 pZipEntryInfo->__compressionLevel = BEST_SPEED;
369         }
370         else if (level == Z_BEST_COMPRESSION)
371         {
372                 pZipEntryInfo->__compressionLevel = BEST_COMPRESSION;
373         }
374         else
375         {
376                 pZipEntryInfo->__compressionLevel = DEFAULT_COMPRESSION;
377         }
378
379         pZipEntryInfo->__isDirectory = (unzFileInfo.external_fa == 16) ? true : false;
380         pZipEntryInfo->__compressedSize = unzFileInfo.compressed_size;
381         pZipEntryInfo->__uncompressedSize = unzFileInfo.uncompressed_size;
382         pZipEntryInfo->__archiveName = archieveName;
383         pZipEntryInfo->__method = method;
384
385         // set the entry information
386         entry.Set(pZipEntryInfo);
387
388         return E_SUCCESS;
389 }
390
391 result
392 _FileUnzipperImpl::GetEntry(const String& zipEntryName, ZipEntry& entry) const
393 {
394         SysAssertf(__pArchiveName != null, "Not yet constructed! Construct() should be called before use");
395
396         std::unique_ptr< ByteBuffer > pZipEntryBuff(StringUtil::StringToUtf8N(zipEntryName));
397         SysTryReturnResult(NID_BASE_UTIL, pZipEntryBuff != null, E_INVALID_ARG, "Invalid file path.");
398
399         const char* pZipEntryName = reinterpret_cast<const char*> (pZipEntryBuff->GetPointer());
400
401         std::unique_ptr< void, CloseUnzipFile > pZipFile(unzOpen(__pArchiveName.get()));
402         SysTryReturnResult(NID_BASE_UTIL, pZipFile != null, E_IO, "Failed to open [%s].", __pArchiveName.get());
403
404         int err = unzLocateFile(pZipFile.get(), pZipEntryName, UNZ_COMP_CASE_SENSITIVE);
405         SysTryReturnResult(NID_BASE_UTIL, err == UNZ_OK, E_FILE_NOT_FOUND, "Failed to locate file entry (%s).", pZipEntryName);
406
407         return GetCurrentFileInfo(pZipFile.get(), entry);
408 }
409
410
411 result
412 _FileUnzipperImpl::GetEntry(int index, ZipEntry& entry) const
413 {
414         SysAssertf(__pArchiveName != null, "Not yet constructed! Construct() should be called before use");
415
416         std::unique_ptr< void, CloseUnzipFile > pZipFile(unzOpen(__pArchiveName.get()));
417         SysTryReturnResult(NID_BASE_UTIL, pZipFile != null, E_IO, "Failed to open [%s].", __pArchiveName.get());
418
419         // goto the first file in the archive
420         int err = unzGoToFirstFile(pZipFile.get());
421         SysTryReturnResult(NID_BASE_UTIL, (err == UNZ_OK), E_IO, "Failed to get the first file entry in the zip file.");
422
423         while (index--)
424         {
425                 err = unzGoToNextFile(pZipFile.get());
426                 SysTryReturnResult(NID_BASE_UTIL, err == UNZ_OK, E_IO, "Failed to move to the next entry in zip file.");
427         }
428
429         return GetCurrentFileInfo(pZipFile.get(), entry);
430 }
431
432 } } } // Tizen::Base::Utility