sync with master
[platform/framework/native/appfw.git] / src / io / FIo_ZipFileArchive.cpp
1 //
2 // Open Service Platform
3 // Copyright (c) 2013 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        FIo_ZipFileArchive.cpp
20  * @brief       This is the implementation file for _ZipFileArchive 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 #include <unique_ptr.h>
30
31 #include <FBaseResult.h>
32 #include <FBaseSysLog.h>
33 #include <FIo.h>
34
35 #include <FBase_StringConverter.h>
36 #include <FBase_NativeError.h>
37 #include <FIo_DirEnumeratorImpl.h>
38 #include <FApp_AppInfo.h>
39
40 #include "FIo_ZipFileArchive.h"
41
42 using namespace std;
43 using namespace Tizen::Base;
44 using namespace Tizen::App;
45
46 namespace Tizen { namespace Io
47 {
48
49 static const int MAX_WRITE_BUF_LEN = 4096;
50 const char _ZIP_ARCHIVE_DIR[] = "/tmp/osp/ZipArchive/\0";
51
52 struct CloseUnzipFile
53 {
54         void operator ()(void* p)
55         {
56                 if (p != null)
57                 {
58                         unzClose(p);
59                 }
60         }
61 };
62
63 struct CloseFile
64 {
65         void operator ()(FILE* p)
66         {
67                 if (p != null)
68                 {
69                         fclose(p);
70                 }
71         }
72 };
73
74 result
75 _ZipFileArchive::CreateDirectories(char* pFilePath)
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                         int ret = mkdir(pFilePath, S_IRUSR | S_IWUSR | S_IXUSR);
84                         if (ret == -1)
85                         {
86                                 result r = __ConvertNativeErrorToResult(errno);
87                                 SysTryReturnResult(NID_IO, r == E_FILE_ALREADY_EXIST, r, "Failed to create directory (%s)", pFilePath);
88                         }
89                 }
90                 *pPos = '/';
91         }
92
93         return E_SUCCESS;
94 }
95
96 _ZipFileArchive::_ZipFileArchive(void)
97         : __pUnZipArchive(null)
98 {
99 }
100
101 _ZipFileArchive::~_ZipFileArchive(void)
102 {
103 }
104
105 result
106 _ZipFileArchive::Construct(const String& zipFilePath)
107 {
108         SysAssertf(__pUnZipArchive == null, "Already constructed. Calling Construct() twice or more on a same instance is not allowed for this class.");
109         SysTryReturnResult(NID_IO, zipFilePath.GetLength() > 0 && zipFilePath.GetLength() <= PATH_MAX, E_INVALID_ARG,
110                         "Given zipFilePath length is invalid.");
111
112         std::unique_ptr< char[] > pZipFilePath(_StringConverter::CopyToCharArrayN(zipFilePath));
113         SysTryReturnResult(NID_IO, pZipFilePath != null, E_INVALID_ARG, "Invalid zip file archive path.");
114
115         std::unique_ptr< void, CloseUnzipFile > pUnzipFile(unzOpen(pZipFilePath.get()));
116         SysTryReturnResult(NID_IO, pUnzipFile != null, E_IO, "Failed to open [%s].", pZipFilePath.get());
117
118         __pUnZipArchive = pUnzipFile.release();
119
120         return E_SUCCESS;
121 }
122
123 File*
124 _ZipFileArchive::GetFileN(const String& relativePath)
125 {
126         SysLog(NID_IO, "Enter");
127
128         SysAssertf(__pUnZipArchive != null, "Not yet constructed. Construct() should be called before use.\n");
129         SysTryReturn(NID_IO, relativePath.GetLength() > 0 && relativePath.GetLength() <= PATH_MAX, null, E_INVALID_ARG, "[E_INVALID_ARG] Invalid input path.");
130         SysTryReturn(NID_IO, relativePath.StartsWith(L"/", 0) == false || relativePath.EndsWith(L"/") == false, null, E_INVALID_ARG, "[E_INVALID_ARG] Invalid input path.");
131
132         char unzFileEntryName[PATH_MAX + 1] = { 0, };
133         unz_file_info unzfileInfo = { 0, };
134         bool fileEntryFound = false;
135         String tempFilePath;
136         result r = E_SUCCESS;
137
138         int err = unzGoToFirstFile(__pUnZipArchive);
139         SysTryReturn(NID_IO, err == UNZ_OK, null, E_IO, "[E_IO] Failed to get the first file entry in the zip file.");
140
141         do
142         {
143                 err = unzGetCurrentFileInfo(__pUnZipArchive, &unzfileInfo, unzFileEntryName, PATH_MAX, null, 0, null, 0);               
144                 SysTryReturn(NID_IO, err == UNZ_OK, null, E_IO, "[E_IO] Failed to get file entry info from the zip file.");
145
146                 String filePath(unzFileEntryName);
147                 if (filePath.StartsWith(relativePath, 0))
148                 {
149                         fileEntryFound = true;
150                         break;
151                 }
152                 err = unzGoToNextFile(__pUnZipArchive);
153                 SysTryReturn(NID_IO,
154                                 (err == UNZ_OK) || (err == UNZ_END_OF_LIST_OF_FILE), null, E_IO, "[E_IO] Failed to get the next entry in the zip file.");
155
156         } while (err != UNZ_END_OF_LIST_OF_FILE);
157         SysTryReturn(NID_IO, fileEntryFound == true, null, E_FILE_NOT_FOUND, "[E_FILE_NOT_FOUND] The specified file is not found in the archive.");
158
159         tempFilePath.Append(String(_ZIP_ARCHIVE_DIR) + _AppInfo::GetApplicationId());
160         tempFilePath.Append(String(L"/") + String(unzFileEntryName));
161         r = _ZipFileArchive::UnzipCurrentFileTo(__pUnZipArchive, tempFilePath);
162         SysTryReturn(NID_IO, !IsFailed(r), null, r, "[%s] File could not be decompressed.", GetErrorMessage(r));
163
164         std::unique_ptr<File> pFile(new (std::nothrow) File());
165         SysTryReturn(NID_IO, pFile != null, null, E_OUT_OF_MEMORY, "[E_OUT_OF_MEMORY] The memory is insufficient.");
166
167         r = pFile->Construct(tempFilePath, L"r");
168         SysTryReturn(NID_IO, !IsFailed(r), null, r, "[%s] Propagated.", GetErrorMessage(r));
169
170         return pFile.release();
171 }
172
173 DirEnumerator* 
174 _ZipFileArchive::GetDirEnumeratorN(const String& subDirName)
175 {
176         SysAssertf(__pUnZipArchive != null, "Not yet constructed. Construct() should be called before use.\n");
177         SysTryReturn(NID_IO, subDirName.GetLength() > 0 && subDirName.GetLength() <= PATH_MAX, null, E_INVALID_ARG, "[E_INVALID_ARG] Invalid input path.");
178         SysTryReturn(NID_IO, subDirName.StartsWith(L"/", 0) == false, null, E_INVALID_ARG, "[E_INVALID_ARG] Invalid input path.");
179
180         char unzFileEntryName[PATH_MAX + 1] = {0};
181         unz_file_info unzfileInfo = {0};
182         bool dirEntryFound = false;
183         String relativePath = subDirName;
184         String tempRootDirPath(L"");
185         result r = E_SUCCESS;
186
187         if (relativePath.EndsWith(L'/') == false)
188         {
189                 relativePath.Append(L'/');
190         }
191
192         int err = unzGoToFirstFile(__pUnZipArchive);
193         SysTryReturn(NID_IO, err == UNZ_OK, null, E_IO, "[E_IO] Failed to get the first file entry in the zip file.");
194
195         do
196         {
197                 err = unzGetCurrentFileInfo(__pUnZipArchive, &unzfileInfo, unzFileEntryName, PATH_MAX, null, 0, null, 0);               
198                 SysTryReturn(NID_IO, err == UNZ_OK, null, E_IO, "[E_IO] Failed to get file entry info from the zip file.");
199
200                 String filePath(unzFileEntryName);
201                 if (filePath.EndsWith(relativePath))
202                 {
203                         dirEntryFound = true;
204                         tempRootDirPath.Append(unzFileEntryName);
205                 }
206
207                 if (dirEntryFound == true)
208                 {
209                         String tempPath;
210                         tempPath.Append(unzFileEntryName);
211                         if (tempPath.StartsWith(tempRootDirPath, 0) == true)
212                         {
213                                 String absoluteDirPath(String(_ZIP_ARCHIVE_DIR) + _AppInfo::GetApplicationId());
214                                 absoluteDirPath.Append(String(L"/") + String(unzFileEntryName));
215                                 r = _ZipFileArchive::UnzipCurrentFileTo(__pUnZipArchive, absoluteDirPath);
216                                 SysTryReturn(NID_IO, !IsFailed(r), null, r, "[%s] Propogating to caller...", GetErrorMessage(r));                                       
217                         }
218                 }
219                 err = unzGoToNextFile(__pUnZipArchive);
220                 SysTryReturn(NID_IO,
221                                 (err == UNZ_OK) || (err == UNZ_END_OF_LIST_OF_FILE), null, E_IO, "[E_IO] Failed to get the next entry in the zip file.");
222
223         } while (err != UNZ_END_OF_LIST_OF_FILE);
224
225         SysTryReturn(NID_IO, dirEntryFound == true, null, E_IO, "[E_IO] The specified subDirName is not found in the archive.");
226
227         String absoluteDirPath(String(_ZIP_ARCHIVE_DIR) + _AppInfo::GetApplicationId());
228         absoluteDirPath.Append(String(L"/") + tempRootDirPath);
229
230         unique_ptr<DirEnumerator> pDirEnum(_DirEnumeratorImpl::CreateDirEnumeratorInstanceN(absoluteDirPath));
231         SysTryReturn(NID_IO, pDirEnum != null, null, E_OUT_OF_MEMORY,
232                            "[E_OUT_OF_MEMORY] The memory is insufficient.");
233
234         SysLog(NID_IO, "Exit");
235
236         return pDirEnum.release();
237 }
238
239 result
240 _ZipFileArchive::UnzipCurrentFileTo(void* pUnZipArchive, const String& filePath)
241 {
242         SysLog(NID_IO, "Enter with filePath(%ls))", filePath.GetPointer());
243
244         std::unique_ptr< char[] > pOutPath(_StringConverter::CopyToCharArrayN(filePath));
245         SysTryReturn(NID_IO, pOutPath != null, GetLastResult(), GetLastResult(), "[%s] Failed to create Directory [%s]", GetErrorMessage(GetLastResult()), pOutPath.get());
246         
247         result r = _ZipFileArchive::CreateDirectories(pOutPath.get());
248         SysTryReturnResult(NID_IO, r == E_SUCCESS, r, "Failed to create Directory [%s]", pOutPath.get());
249
250         if(pOutPath[strlen(pOutPath.get()) - 1] != '/')
251         {
252                 std::unique_ptr< FILE, CloseFile > pFile(fopen(pOutPath.get(), "w"));
253                 r = __ConvertNativeErrorToResult(errno);
254                 SysTryReturnResult(NID_IO, pFile != null, r, "Failed to open file [name: %s, errorno: %d].", pOutPath.get(), errno);
255
256                 int err = unzOpenCurrentFile(pUnZipArchive);
257                 SysTryReturnResult(NID_IO, err == UNZ_OK, E_IO, "Failed to open current zip file.");
258
259                 int writeCnt = 0;
260                 int readLen = 1;
261                 char writeBuf[MAX_WRITE_BUF_LEN] = {0};
262                 while (readLen > 0)
263                 {
264                         readLen = unzReadCurrentFile(pUnZipArchive, writeBuf, sizeof(writeBuf));
265                         r = (readLen < 0) ? E_IO : E_SUCCESS;
266                         SysTryCatch(NID_IO, r == E_SUCCESS, unzCloseCurrentFile(pUnZipArchive), r,
267                                                         "[%s] Failed to read from zip file (%s).", GetErrorMessage(E_IO), pOutPath.get());
268
269                         if (readLen > 0)
270                         {
271                                 writeCnt = fwrite(writeBuf, 1, readLen, pFile.get());
272                                 if (writeCnt < readLen)
273                                 {
274                                         int ret = ferror(pFile.get());
275                                         r = __ConvertNativeErrorToResult(ret);
276                                         SysTryCatch(NID_IO, ret != 0, unzCloseCurrentFile(pUnZipArchive), r,
277                                                                 "[%s] Failed to perform write operation.", GetErrorMessage(r));
278                                 }
279                         }
280                 }
281
282                 unzCloseCurrentFile(pUnZipArchive);
283         }
284
285 CATCH:
286         return r;
287 }
288
289 }}