[Install Location: Part 3] Implement about install location (auto, internal only...
[framework/web/wrt-installer.git] / src / jobs / widget_install / widget_unzip.cpp
1 /*
2  * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
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  * @file    widget_unzip.cpp
18  * @author  Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com)
19  * @version 1.0
20  * @brief   Implementation file for installer widget unzip
21  */
22 #include <widget_install/widget_unzip.h>
23 #include <widget_install/widget_install_errors.h>
24 #include <widget_install/widget_install_context.h>
25 #include <widget_install/job_widget_install.h>
26 #include <dpl/copy.h>
27 #include <dpl/file_output.h>
28 #include <dpl/abstract_waitable_input_adapter.h>
29 #include <dpl/wrt-dao-ro/global_config.h>
30 #include <task_commons.h>
31 #include <sys/stat.h>
32 #include <sys/statvfs.h>
33 #include <dlfcn.h>
34 #include <installer_log.h>
35
36 using namespace WrtDB;
37
38 namespace {
39 const char *const DRM_LIB_PATH = "/usr/lib/libdrm-service-core-tizen.so";
40 const size_t SPACE_SIZE = 1024 * 1024;
41 const char *const WEB_APP_CONFIG_XML= "config.xml";
42 const char *const HYBRID_CONFIG_XML = "res/wgt/config.xml";
43
44 struct PathAndFilePair
45 {
46     std::string path;
47     std::string file;
48
49     PathAndFilePair(const std::string &p,
50                     const std::string &f) :
51         path(p),
52         file(f)
53     {}
54 };
55
56 PathAndFilePair SplitFileAndPath(const std::string &filePath)
57 {
58     std::string::size_type position = filePath.rfind('/');
59
60     // Is this only a file without a path ?
61     if (position == std::string::npos) {
62         return PathAndFilePair(std::string(), filePath);
63     }
64
65     // This is full file-path pair
66     return PathAndFilePair(filePath.substr(0,
67                                            position),
68                            filePath.substr(position + 1));
69 }
70 }
71
72 namespace Jobs {
73 namespace WidgetInstall {
74
75 WidgetUnzip::WidgetUnzip(const std::string &source)
76 {
77     Try {
78         m_requestFile = getDecryptedPackage(source);
79         m_zip.reset(new DPL::ZipInput(m_requestFile));
80     }
81     Catch(DPL::ZipInput::Exception::OpenFailed)
82     {
83         ReThrowMsg(Exceptions::OpenZipFailed, source);
84     }
85     Catch(Exceptions::DrmDecryptFailed)
86     {
87         ReThrowMsg(Exceptions::ExtractFileFailed, source);
88     }
89 }
90
91 void WidgetUnzip::ExtractFile(DPL::ZipInput::File *input,
92                             const std::string &destFileName)
93 {
94     Try
95     {
96         DPL::AbstractWaitableInputAdapter inputAdapter(input);
97         DPL::FileOutput output(destFileName);
98
99         DPL::Copy(&inputAdapter, &output);
100     }
101     Catch(DPL::FileOutput::Exception::OpenFailed)
102     {
103         ReThrowMsg(Exceptions::ExtractFileFailed, destFileName);
104     }
105     Catch(DPL::CopyFailed)
106     {
107         ReThrowMsg(Exceptions::ExtractFileFailed, destFileName);
108     }
109 }
110
111 void WidgetUnzip::unzipProgress(const std::string &destination)
112 {
113     // Show file info
114     _D("Unzipping: '%s', Comment: '%s', Compressed size: %lld, Uncompressed size: %lld",
115             m_zipIterator->name.c_str(), m_zipIterator->comment.c_str(), m_zipIterator->compressedSize, m_zipIterator->uncompressedSize);
116
117     // Normalize file paths
118     // FIXME: Implement checking for invalid characters
119
120     // Extract file or path
121     std::string fileName = m_zipIterator->name;
122
123     if (fileName[fileName.size() - 1] == '/') {
124         // This is path
125         std::string newPath = destination + "/" +
126             fileName.substr(0, fileName.size() - 1);
127         _D("Path to extract: %s", newPath.c_str());
128
129         // Create path in case of it is empty
130         createTempPath(newPath);
131     } else {
132         // This is regular file
133         std::string fileExtractPath = destination + "/" + fileName;
134
135         _D("File to extract: %s", fileExtractPath.c_str());
136
137         // Split into pat & file pair
138         PathAndFilePair pathAndFile = SplitFileAndPath(fileExtractPath);
139
140         _D("Path and file: %s : %s", pathAndFile.path.c_str(), pathAndFile.file.c_str());
141
142         // First, ensure that path exists
143         createTempPath(pathAndFile.path);
144
145         Try
146         {
147             // Open file
148             std::unique_ptr<DPL::ZipInput::File> file(
149                 m_zip->OpenFile(fileName));
150
151             // Extract single file
152             ExtractFile(file.get(), fileExtractPath);
153         }
154         Catch(DPL::ZipInput::Exception::OpenFileFailed)
155         {
156             ThrowMsg(Exceptions::ExtractFileFailed, fileName);
157         }
158     }
159
160     // Check whether there are more files to extract
161     if (++m_zipIterator == m_zip->end()) {
162         _D("Unzip progress finished successfuly");
163     } else {
164         unzipProgress(destination);
165     }
166 }
167
168 bool WidgetUnzip::isDRMPackage(const std::string &source)
169 {
170     _D("Enter : isDRMPackage()");
171     int ret = 0;
172     void* pHandle = NULL;
173     char* pErrorMsg = NULL;
174     int (*drm_oem_sapps_is_drm_file)(const char* pDcfPath, int dcfPathLen);
175
176     pHandle = dlopen(DRM_LIB_PATH, RTLD_LAZY | RTLD_GLOBAL);
177     if (!pHandle) {
178         _E("dlopen failed : %s [%s]", source.c_str(), dlerror());
179         return false;
180     }
181
182     // clear existing error
183     dlerror();
184
185     drm_oem_sapps_is_drm_file = reinterpret_cast <int (*)(const char*, int)>
186         (dlsym(pHandle, "drm_oem_sapps_is_drm_file"));
187
188     if ((pErrorMsg = dlerror()) != NULL) {
189         _E("dlsym failed : %s [%s]", source.c_str(), pErrorMsg);
190         dlclose(pHandle);
191         return false;
192     }
193
194     if (drm_oem_sapps_is_drm_file == NULL) {
195         _E("drm_oem_sapps_is_drm_file is NULL : %s", source.c_str());
196         dlclose(pHandle);
197         return false;
198     }
199
200     ret = drm_oem_sapps_is_drm_file(source.c_str(), source.length());
201     dlclose(pHandle);
202     if (1 == ret) {
203         _D("%s is DRM file", source.c_str());
204         return true;
205     }
206     _D("%s isn't DRM file", source.c_str());
207     return false;
208 }
209
210 bool WidgetUnzip::decryptDRMPackage(const std::string &source, const std::string
211         &decryptedSource)
212 {
213     _D("Enter : decryptDRMPackage()");
214     int ret = 0;
215     void* pHandle = NULL;
216     char* pErrorMsg = NULL;
217     int (*drm_oem_sapps_decrypt_package)(const char* pDcfPath, int dcfPathLen,
218             const char* pDecryptedFile, int decryptedFileLen);
219
220     pHandle = dlopen(DRM_LIB_PATH, RTLD_LAZY | RTLD_GLOBAL);
221     if (!pHandle) {
222         _E("dlopen failed : %s [%s]", source.c_str(), dlerror());
223         return false;
224     }
225
226     // clear existing error
227     dlerror();
228
229     drm_oem_sapps_decrypt_package = reinterpret_cast <int (*)(const char*, int,
230             const char*, int)>
231         (dlsym(pHandle, "drm_oem_sapps_decrypt_package"));
232
233     if ((pErrorMsg = dlerror()) != NULL) {
234         _E("dlsym failed : %s [%s]", source.c_str(), pErrorMsg);
235         dlclose(pHandle);
236         return false;
237     }
238
239     if (drm_oem_sapps_decrypt_package == NULL) {
240         _E("drm_oem_sapps_decrypt_package is NULL : %s", source.c_str());
241         dlclose(pHandle);
242         return false;
243     }
244
245     ret = drm_oem_sapps_decrypt_package(source.c_str(), source.length(),
246             decryptedSource.c_str(), decryptedSource.length());
247     dlclose(pHandle);
248     if (1 == ret) {
249         _D("%s is decrypted : %s", source.c_str(), decryptedSource.c_str());
250         return true;
251     }
252     return false;
253 }
254
255 std::string WidgetUnzip::getDecryptedPackage(const std::string &source)
256 {
257     _D("Check DRM...");
258     if (isDRMPackage(source)) {
259         std::string decryptedFile;
260         size_t found = source.find_last_of(".wgt");
261         if (found == std::string::npos) {
262             decryptedFile += source + "_tmp.wgt";
263         } else {
264             decryptedFile += source.substr(0, source.find_last_not_of(".wgt") +
265                     1) + "_tmp.wgt";
266         }
267
268         _D("decrypted file name : %s", decryptedFile.c_str());
269         if (!decryptDRMPackage(source, decryptedFile)) {
270             _E("Failed decrypt drm file");
271             ThrowMsg(Exceptions::DrmDecryptFailed, source);
272         }
273         return decryptedFile;
274     }
275     return source;
276 }
277
278 void WidgetUnzip::unzipWgtFile(const std::string &destination)
279 {
280     _D("Prepare to unzip...");
281     Try
282     {
283         _D("wgtFile : %s", m_requestFile.c_str());
284         _D("Widget package comment: %s", m_zip->GetGlobalComment().c_str());
285
286         // Widget package must not be empty
287         if (m_zip->empty()) {
288             ThrowMsg(Exceptions::ZipEmpty, m_requestFile);
289         }
290
291         // Set iterator to first file
292         m_zipIterator = m_zip->begin();
293
294         unzipProgress(destination);
295
296         // Unzip finished, close internal structures
297         m_zip.reset();
298
299         // Done
300         _D("Unzip finished");
301     }
302     Catch(DPL::ZipInput::Exception::OpenFailed)
303     {
304         ReThrowMsg(Exceptions::OpenZipFailed, m_requestFile);
305     }
306     Catch(DPL::ZipInput::Exception::SeekFileFailed)
307     {
308         ThrowMsg(Exceptions::ExtractFileFailed, m_requestFile);
309     }
310     Catch(Exceptions::DrmDecryptFailed)
311     {
312         ReThrowMsg(Exceptions::ExtractFileFailed, m_requestFile);
313     }
314 }
315
316 bool WidgetUnzip::checkAvailableSpace(const std::string &destination)
317 {
318     _D("checkAvailableSpace ... ");
319
320     double unCompressedSize = m_zip->GetTotalUncompressedSize();
321     _D("unCompressedSize : %ld", unCompressedSize);
322
323     struct statvfs vfs;
324     if (-1 == statvfs(destination.c_str(), &vfs)) {
325         _E("There is no space for installation");
326         return false;
327     }
328
329     double freeSize = (double)vfs.f_bsize * vfs.f_bavail;
330     _D("Space Size : %ld", freeSize);
331
332     if (unCompressedSize + SPACE_SIZE >= freeSize) {
333         _E("There is no space for installation");
334         return false;
335     }
336     return true;
337 }
338
339 void WidgetUnzip::unzipConfiguration(const std::string &destination,
340         WrtDB::PackagingType* type)
341 {
342     _D("unzipConfiguration");
343
344     Try {
345         _D("wgtFile : %s", m_requestFile.c_str());
346
347         std::unique_ptr<DPL::ZipInput::File> configFile;
348
349         Try {
350             configFile.reset(m_zip->OpenFile(HYBRID_CONFIG_XML));
351             *type = PKG_TYPE_HYBRID_WEB_APP;
352         } Catch(DPL::ZipInput::Exception::OpenFileFailed) {
353             configFile.reset(m_zip->OpenFile(WEB_APP_CONFIG_XML));
354             *type = PKG_TYPE_NOMAL_WEB_APP;
355         }
356
357         std::string extractPath = destination + "/" + WEB_APP_CONFIG_XML;
358         ExtractFile(configFile.get(), extractPath);
359     }
360     Catch(DPL::ZipInput::Exception::OpenFailed)
361     {
362         ReThrowMsg(Exceptions::OpenZipFailed, m_requestFile);
363     }
364     Catch(DPL::ZipInput::Exception::OpenFileFailed)
365     {
366         ThrowMsg(Exceptions::ExtractFileFailed, "config.xml");
367     }
368     Catch(Exceptions::DrmDecryptFailed)
369     {
370         ReThrowMsg(Exceptions::ExtractFileFailed, m_requestFile);
371     }
372 }
373
374 } //namespace WidgetInstall
375 } //namespace Jobs