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