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