tizen 2.4 release
[framework/web/wrt-installer.git] / src / jobs / widget_install / task_file_manipulation.cpp
1 /*
2  * Copyright (c) 2010 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    task_db_update.cpp
18  * @author  Lukasz Wrzosek(l.wrzosek@samsung.com)
19  * @version 1.0
20  * @brief   Implementation file for installer task database updating
21  */
22 #include <unistd.h>
23 #include <sys/stat.h>
24 #include <dirent.h>
25 #include <string>
26 #include <fstream>
27 #include <vconf.h>
28 #include <vconf-internal-keys.h>
29 #include <widget_install/task_file_manipulation.h>
30 #include <widget_install/widget_install_errors.h>
31 #include <widget_install/widget_install_context.h>
32 #include <widget_install/directory_api.h>
33 #include <dpl/foreach.h>
34 #include <dpl/assert.h>
35 #include <dpl/errno_string.h>
36 #include <dpl/utils/folder_size.h>
37 #include <dpl/wrt-dao-ro/global_config.h>
38
39 #include <widget_install_to_external.h>
40 #include <dpl/log/secure_log.h>
41 #include <widget_unzip.h>
42 #include <boost/filesystem.hpp>
43 #include <privilege-control.h>
44
45 #define WEBAPP_DEFAULT_UID  5000
46 #define WEBAPP_DEFAULT_GID  5000
47
48 using namespace WrtDB;
49 namespace bf = boost::filesystem;
50
51 namespace {
52 const bf::perms PRIVATE_STORAGE_MODE = bf::owner_all;
53 const bf::perms SHARED_STORAGE_MODE = bf::owner_all | bf::group_read
54                 | bf::group_exe | bf::others_read | bf::others_exe;
55 }
56
57 namespace {
58 const char* GLIST_RES_DIR = "res";
59
60 bool _FolderCopy(std::string source, std::string dest)
61 {
62     DIR* dir = opendir(source.c_str());
63     if (NULL == dir) {
64         return false;
65     }
66
67     struct dirent dEntry;
68     struct dirent *dEntryResult;
69     int return_code;
70
71     do {
72         struct stat statInfo;
73         return_code = readdir_r(dir, &dEntry, &dEntryResult);
74         if (dEntryResult != NULL && return_code == 0) {
75             std::string fileName = dEntry.d_name;
76             std::string fullName = source + "/" + fileName;
77
78             if (stat(fullName.c_str(), &statInfo) != 0) {
79                 closedir(dir);
80                 return false;
81             }
82
83             if (S_ISDIR(statInfo.st_mode)) {
84                 if (("." == fileName) || (".." == fileName)) {
85                     continue;
86                 }
87                 bf::path destFolder(dest + "/" + fileName);
88                 try {
89                     if(!bf::exists(destFolder)){
90                         bf::create_directories(destFolder);
91                         bf::permissions(destFolder,SHARED_STORAGE_MODE);
92                     }
93                 } catch (const bf::filesystem_error& ex) {
94                     _E("boost::filesystem::error: %s",ex.what());
95                 }
96                 if (!_FolderCopy(fullName, destFolder.native())) {
97                     closedir(dir);
98                     return false;
99                 }
100             }
101
102             std::string destFile = dest + "/" + fileName;
103             std::ifstream infile(fullName);
104             std::ofstream outfile(destFile);
105             outfile << infile.rdbuf();
106             outfile.close();
107             infile.close();
108         }
109     } while (dEntryResult != NULL && return_code == 0);
110     closedir(dir);
111     return true;
112 }
113 }
114
115 namespace Jobs {
116 namespace WidgetInstall {
117 TaskFileManipulation::TaskFileManipulation(JobWidgetInstall * const &jobContext) :
118     DPL::TaskDecl<TaskFileManipulation>(this),
119     m_jobContext(jobContext),
120     m_extHandle(NULL)
121 {
122     AddStep(&TaskFileManipulation::StartStep);
123     AddStep(&TaskFileManipulation::StepCheckInstallLocation);
124     AddStep(&TaskFileManipulation::StepPrepareRootDirectory);
125     if (m_jobContext->m_installerContext.mode.extension != InstallMode::ExtensionType::DIRECTORY)
126     {
127         AddStep(&TaskFileManipulation::StepUnzipWgtFile);
128     }
129     AddStep(&TaskFileManipulation::EndStep);
130
131     AddAbortStep(&TaskFileManipulation::StepAbortPrepareRootDirectory);
132 }
133
134 void TaskFileManipulation::StepCheckInstallLocation()
135 {
136     _D("StepCheckInstallLocation");
137     if (m_jobContext->m_installerContext.mode.rootPath == InstallMode::RootPath::RO) {
138         m_jobContext->m_installerContext.locationType = INSTALL_LOCATION_TYPE_INTERNAL_ONLY;
139         return;
140     }
141
142     // If webapp is hybrid app, it should be installed to internal storage.
143     // Because Service app should be installed to internal.
144     if (m_jobContext->m_installerContext.widgetConfig.packagingType == PKG_TYPE_HYBRID_WEB_APP) {
145         m_jobContext->m_installerContext.locationType = INSTALL_LOCATION_TYPE_INTERNAL_ONLY;
146         return;
147     }
148
149     std::string installedPath = WrtDB::GlobalConfig::GetUserInstalledWidgetPath();
150
151     if (m_jobContext->m_installerContext.locationType == INSTALL_LOCATION_TYPE_AUTO ||
152         m_jobContext->m_installerContext.locationType == INSTALL_LOCATION_TYPE_UNKNOWN) {
153         int storage = 0;
154         // vconf_get_int(VCONFKEY_SETAPPL_DEFAULT_MEM_INSTALL_APPLICATIONS_INT)
155         // 0 : phone internal memory
156         // 1 : SD card
157         if (vconf_get_int(VCONFKEY_SETAPPL_DEFAULT_MEM_INSTALL_APPLICATIONS_INT,
158                     &storage)) {
159             _E("vconf_get_int(VCONFKEY_SETAPPL_DEFAULT_MEM_INSTALL_APPLICATIONS_INT) \
160                     failed.");
161         }
162         _D("default setting : storage [%d]", storage);
163         if (storage) {
164             m_jobContext->m_installerContext.locationType = INSTALL_LOCATION_TYPE_PREFER_EXTERNAL;
165         } else {
166             m_jobContext->m_installerContext.locationType = INSTALL_LOCATION_TYPE_INTERNAL_ONLY;
167             if(!m_jobContext->m_wgtUnzip->checkAvailableSpace(installedPath)) {
168                 m_jobContext->m_installerContext.locationType = INSTALL_LOCATION_TYPE_PREFER_EXTERNAL;
169             }
170         }
171     }
172
173     if (m_jobContext->m_installerContext.locationType == INSTALL_LOCATION_TYPE_PREFER_EXTERNAL) {
174         int mmcStatus;
175         if (vconf_get_int(VCONFKEY_SYSMAN_MMC_STATUS, &mmcStatus)) {
176             _E("vconf_get_int(VCONFKEY_SYSMAN_MMC_STATUS) failed.");
177             mmcStatus = VCONFKEY_SYSMAN_MMC_INSERTED_NOT_MOUNTED;
178         }
179
180         if (VCONFKEY_SYSMAN_MMC_MOUNTED != mmcStatus) {
181             _D("mmcStatus is MMC_REMOVED or NOT_MOUNTED.");
182             m_jobContext->m_installerContext.locationType = INSTALL_LOCATION_TYPE_INTERNAL_ONLY;
183         }
184     }
185
186     if (m_jobContext->m_installerContext.locationType == INSTALL_LOCATION_TYPE_INTERNAL_ONLY) {
187         if(!m_jobContext->m_wgtUnzip->checkAvailableSpace(installedPath)) {
188             ThrowMsg(Exceptions::OutOfStorageFailed, "There is no space for installation");
189         }
190     }
191 }
192
193 void TaskFileManipulation::StepPrepareRootDirectory()
194 {
195     if (m_jobContext->m_installerContext.locationType == INSTALL_LOCATION_TYPE_PREFER_EXTERNAL) {
196         prepareExternalDir();
197     } else {
198         bf::path widgetPath(m_jobContext->m_installerContext.locations->getPackageInstallationDir());
199         bf::path widgetBinPath(m_jobContext->m_installerContext.locations->getBinaryDir());
200         bf::path widgetSrcPath(m_jobContext->m_installerContext.locations->getSourceDir());
201
202         if (!m_jobContext->m_installerContext.isUpdateMode) {
203             _D("Remove existing directory : %s", widgetPath.c_str());
204             try {
205                 JobWidgetInstall::SecureRemove(widgetPath);
206             } catch (const bf::filesystem_error& ex) {
207                 _E("Cannot remove %s! boost::filesystem::error: %s", widgetPath.c_str(),ex.what());
208             }
209         }
210         try {
211             if (!bf::exists(widgetPath)) {
212                 bf::create_directories(widgetPath);
213                 bf::permissions(widgetPath,SHARED_STORAGE_MODE);
214             }
215         } catch (const bf::filesystem_error& ex) {
216             _E("boost::filesystem error: %s",ex.what());
217         }
218
219         _D("Create resource directory");
220         try {
221             if(!bf::exists(widgetBinPath)){
222                 bf::create_directories(widgetBinPath);
223                 bf::permissions(widgetBinPath,SHARED_STORAGE_MODE);
224             }
225         } catch (const bf::filesystem_error& ex) {
226             _E("boost::filesystem error: %s",ex.what());
227         }
228
229         try {
230             if(!bf::exists(widgetSrcPath)){
231                 bf::create_directories(widgetSrcPath);
232                 bf::permissions(widgetSrcPath,SHARED_STORAGE_MODE);
233             }
234         } catch (const bf::filesystem_error& ex) {
235             _E("boost::filesystem error: %s",ex.what());
236         }
237     }
238
239     m_jobContext->UpdateProgress(
240         InstallerContext::INSTALL_DIR_CREATE,
241         "Widget Directory Created");
242 }
243
244 void TaskFileManipulation::StepUnzipWgtFile()
245 {
246     if (m_jobContext->m_installerContext.widgetConfig.packagingType != PKG_TYPE_HOSTED_WEB_APP) {
247         std::string instDir;
248         if (m_jobContext->m_installerContext.widgetConfig.packagingType == PKG_TYPE_HYBRID_WEB_APP) {
249             instDir = m_jobContext->m_installerContext.locations->getPackageInstallationDir();
250         } else {
251             instDir = m_jobContext->m_installerContext.locations->getSourceDir();
252         }
253
254         _D("unzip file to %s", instDir.c_str());
255         m_jobContext->m_wgtUnzip->unzipWgtFile(instDir);
256     } else {
257         _D("From browser installation - unzip is not done");
258     }
259
260     m_jobContext->UpdateProgress(
261         InstallerContext::INSTALL_UNZIP_WGT,
262         "Unzip Wgt file");
263 }
264
265 void TaskFileManipulation::StepAbortPrepareRootDirectory()
266 {
267     _D("[Create Root Directory]  Aborting.... (Rename path)");
268     if (m_jobContext->m_installerContext.locationType == INSTALL_LOCATION_TYPE_PREFER_EXTERNAL) {
269         if (m_jobContext->m_installerContext.isUpdateMode) {
270             WidgetInstallToExtSingleton::Instance().postUpgrade(false);
271         } else {
272             WidgetInstallToExtSingleton::Instance().postInstallation(false);
273         }
274         WidgetInstallToExtSingleton::Instance().deinitialize();
275     } else {
276         bf::path widgetPath(m_jobContext->m_installerContext.locations->getPackageInstallationDir());
277         try {
278             JobWidgetInstall::SecureRemove(widgetPath);
279         } catch (const bf::filesystem_error& ex) {
280             _E("Error occurs during removing existing folder\n");
281             _E("boost::filesystem:error: %s", ex.what());
282         }
283         // Remove user data directory if preload web app.
284         bf::path userData(m_jobContext->m_installerContext.locations->getUserDataRootDir());
285         if (0 == access(userData.c_str(), F_OK)) {
286             try {
287                 JobWidgetInstall::SecureRemove(userData);
288             } catch (const bf::filesystem_error& ex) {
289                 _E("Error occurs during removing user data directory");
290                 _E("boost::filesystem:error: %s", ex.what());
291             }
292         }
293     }
294 }
295
296 void TaskFileManipulation::prepareExternalDirSmacklabel()
297 {
298     _D("Step prepare to smack label in exernal directory");
299
300     /* change smack label. APP2EXT set label of /.mmc and /res to pkgid.
301        In that case, file copy operation is failed because before task_smack, wrt-installer
302        wrt-installer does not contain permission for pkgid.*/
303     std::string mmcDir = m_jobContext->m_installerContext.locations->getPackageInstallationDir() + "/.mmc";
304     if (PC_OPERATION_SUCCESS != perm_app_setup_path("_", mmcDir.c_str(), APP_PATH_PRIVATE)) {
305         _W("change to wrt-installer label to %s", mmcDir.c_str());
306     }
307
308     std::string resDir = m_jobContext->m_installerContext.locations->getPackageInstallationDir() + "/res";
309     if (PC_OPERATION_SUCCESS != perm_app_setup_path("_", resDir.c_str(), APP_PATH_PRIVATE)) {
310         _W("change to wrt-installer label to %s", resDir.c_str());
311     }
312 }
313
314 void TaskFileManipulation::prepareExternalDir()
315 {
316     _D("Step prepare to install in exernal directory");
317     Try {
318         std::string pkgid =
319             DPL::ToUTF8String(m_jobContext->m_installerContext.widgetConfig.tzPkgid);
320
321         WidgetInstallToExtSingleton::Instance().initialize(pkgid);
322
323         std::unique_ptr<DPL::ZipInput> zipFile(new
324                 DPL::ZipInput(m_jobContext->m_installerContext.requestedPath));
325         double unzipSize = zipFile->GetTotalUncompressedSize();
326         int folderSize = (int)(unzipSize / (1024 * 1024)) + 1;
327
328         GList *list = NULL;
329         app2ext_dir_details* dirDetail = NULL;
330
331         dirDetail = (app2ext_dir_details*) calloc(1,
332                 sizeof(
333                     app2ext_dir_details));
334         if (NULL == dirDetail) {
335             ThrowMsg(Exceptions::ErrorExternalInstallingFailure,
336                     "error in app2ext");
337         }
338         dirDetail->name = strdup(GLIST_RES_DIR);
339         dirDetail->type = APP2EXT_DIR_RO;
340         list = g_list_append(list, dirDetail);
341
342         if (m_jobContext->m_installerContext.isUpdateMode) {
343             WidgetInstallToExtSingleton::Instance().preUpgrade(list,
344                                                                folderSize);
345         } else {
346             WidgetInstallToExtSingleton::Instance().preInstallation(list,
347                                                                     folderSize);
348         }
349         free(dirDetail);
350         g_list_free(list);
351
352         prepareExternalDirSmacklabel();
353
354         /* make bin directory */
355         bf::path widgetBinPath(m_jobContext->m_installerContext.locations->getBinaryDir());
356         try {
357             if(!bf::exists(widgetBinPath)){
358                 bf::create_directories(widgetBinPath);
359                 bf::permissions(widgetBinPath,SHARED_STORAGE_MODE);
360             }
361         } catch (const bf::filesystem_error& ex) {
362             _E("boost::filesystem::error: %s", ex.what());
363         }
364
365         bf::path sourceDir(m_jobContext->m_installerContext.locations->getSourceDir());
366         try {
367             if(!bf::exists(sourceDir)){
368                 bf::create_directories(sourceDir);
369                 bf::permissions(sourceDir,SHARED_STORAGE_MODE);
370             }
371         } catch (const bf::filesystem_error& ex) {
372             _E("boost::filesystem::error: %s", ex.what());
373         }
374     }
375     Catch(DPL::ZipInput::Exception::OpenFailed) {
376         ReThrowMsg(Exceptions::ErrorExternalInstallingFailure,
377                    "Error during \
378                 create external folder ");
379     }
380     Catch(WidgetInstallToExt::Exception::ErrorInstallToExt)
381     {
382         ReThrowMsg(Exceptions::ErrorExternalInstallingFailure,
383                    "Error during create external folder ");
384     }
385 }
386
387 void TaskFileManipulation::StartStep()
388 {
389     LOGI("--------- <TaskFileManipulation> : START ----------");
390 }
391
392 void TaskFileManipulation::EndStep()
393 {
394     LOGI("--------- <TaskFileManipulation> : END ----------");
395 }
396 } //namespace WidgetInstall
397 } //namespace Jobs