239f8d1a4aef326636e42cc4a6a83b1a1087f59c
[framework/web/wrt-installer.git] / src / jobs / widget_install / job_widget_install.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    job_widget_install.cpp
18  * @author  Radoslaw Wicik r.wicik@samsung.com
19  * @author  Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com)
20  * @version 1.0
21  * @brief   Implementation file for main installer task
22  */
23 #include <memory>
24 #include <string>
25 #include <sys/time.h>
26 #include <ctime>
27 #include <cstdlib>
28 #include <limits.h>
29 #include <regex.h>
30
31 #include <dpl/noncopyable.h>
32 #include <dpl/abstract_waitable_input_adapter.h>
33 #include <dpl/abstract_waitable_output_adapter.h>
34 #include <dpl/zip_input.h>
35 #include <dpl/binary_queue.h>
36 #include <dpl/copy.h>
37 #include <dpl/assert.h>
38 #include <dpl/sstream.h>
39 #include <dpl/file_input.h>
40 #include <dpl/utils/wrt_utility.h>
41 #include <dpl/wrt-dao-ro/common_dao_types.h>
42 #include <dpl/wrt-dao-ro/widget_dao_read_only.h>
43 #include <dpl/wrt-dao-ro/global_config.h>
44 #include <dpl/wrt-dao-ro/config_parser_data.h>
45 #include <dpl/wrt-dao-rw/global_dao.h> // TODO remove
46 #include <dpl/localization/w3c_file_localization.h>
47
48 #include <libiriwrapper.h>
49 #include <pkg-manager/pkgmgr_signal.h>
50 #include <app_manager.h>
51
52 #include "root_parser.h"
53 #include "widget_parser.h"
54 #include "parser_runner.h"
55 #include <widget_install/job_widget_install.h>
56 #include <widget_install/task_certify.h>
57 #include <widget_install/task_widget_config.h>
58 #include <widget_install/task_file_manipulation.h>
59 #include <widget_install/task_ace_check.h>
60 #include <widget_install/task_smack.h>
61 #include <widget_install/task_manifest_file.h>
62 #include <widget_install/task_prepare_files.h>
63 #include <widget_install/task_recovery.h>
64 #include <widget_install/task_install_ospsvc.h>
65 #include <widget_install/task_update_files.h>
66 #include <widget_install/task_database.h>
67 #include <widget_install/task_remove_backup.h>
68 #include <widget_install/task_encrypt_resource.h>
69 #include <widget_install/task_certificates.h>
70 #include <widget_install/task_unzip.h>
71 #include <widget_install/task_commons.h>
72 #include <widget_install/task_prepare_reinstall.h>
73
74 #include <widget_install/widget_install_errors.h>
75 #include <widget_install/widget_install_context.h>
76 #include <widget_install_to_external.h>
77
78 using namespace WrtDB;
79 using namespace Jobs::Exceptions;
80
81 namespace // anonymous
82 {
83 const char * const CONFIG_XML = "config.xml";
84 const char * const WITH_OSP_XML = "res/wgt/config.xml";
85
86 //allowed: a-z, A-Z, 0-9
87 const char* REG_TIZENID_PATTERN = "^[a-zA-Z0-9]{10}.{1,}$";
88 const char* REG_NAME_PATTERN = "^[a-zA-Z0-9._-]{1,}$";
89 const size_t PACKAGE_ID_LENGTH = 10;
90
91 static const DPL::String SETTING_VALUE_ENCRYPTION = L"encryption";
92 static const DPL::String SETTING_VALUE_ENCRYPTION_ENABLE = L"enable";
93 const DPL::String SETTING_VALUE_INSTALLTOEXT_NAME =
94     L"install-location";
95 const DPL::String SETTING_VALUE_INSTALLTOEXT_PREPER_EXT =
96     L"prefer-external";
97
98 const std::string XML_EXTENSION = ".xml";
99
100 bool hasExtension(const std::string& filename, const std::string& extension)
101 {
102     LogDebug("Looking for extension " << extension << " in: " << filename);
103     size_t fileLen = filename.length();
104     size_t extLen = extension.length();
105     if (fileLen < extLen) {
106         LogError("Filename " << filename << " is shorter than extension "
107                              << extension);
108         return false;
109     }
110     return (0 == filename.compare(fileLen - extLen, extLen, extension));
111 }
112 } // namespace anonymous
113
114 namespace Jobs {
115 namespace WidgetInstall {
116 class InstallerTaskFail :
117     public DPL::TaskDecl<InstallerTaskFail>
118 {
119   private:
120     ConfigureResult m_result;
121
122     void StepFail()
123     {
124         if (m_result == ConfigureResult::Deferred) {
125             ThrowMsg(Jobs::WidgetInstall::Exceptions::Deferred,
126                     "widget installation or update deferred!");
127         } else if (m_result == ConfigureResult::Failed_InvalidConfig) {
128             ThrowMsg(Jobs::WidgetInstall::Exceptions::WidgetConfigFileInvalid,
129                     "invalid config");
130         } else if (m_result == ConfigureResult::Failed_LowerVersion) {
131             ThrowMsg(Jobs::WidgetInstall::Exceptions::PackageLowerVersion,
132                     "package version is lower than installed version");
133         } else if (m_result == ConfigureResult::Failed_AlreadyInstalled) {
134             ThrowMsg(Jobs::WidgetInstall::Exceptions::PackageAlreadyInstalled,
135                     "package is already installed");
136         } else if (m_result == ConfigureResult::Failed_WidgetRunning) {
137             ThrowMsg(Jobs::WidgetInstall::Exceptions::WidgetRunningError,
138                     "widget is running");
139         } else if (m_result == ConfigureResult::Failed_DrmError) {
140             ThrowMsg(Jobs::WidgetInstall::Exceptions::DRMFailed,
141                     "drm failed");
142         } else {
143             ThrowMsg(Jobs::WidgetInstall::Exceptions::NotAllowed,
144                     "widget installation or update not allowed!");
145         }
146     }
147
148   public:
149     InstallerTaskFail(ConfigureResult result) :
150         DPL::TaskDecl<InstallerTaskFail>(this),
151         m_result(result)
152     {
153         AddStep(&InstallerTaskFail::StepFail);
154     }
155 };
156
157 JobWidgetInstall::JobWidgetInstall(
158     std::string const &widgetPath,
159     const WidgetInstallationStruct &
160     installerStruct) :
161     Job(Installation),
162     JobContextBase<WidgetInstallationStruct>(installerStruct),
163     m_exceptionCaught(Jobs::Exceptions::Success)
164 {
165     m_installerContext.m_quiet = m_jobStruct.m_quiet;
166
167     ConfigureResult result = PrePareInstallation(widgetPath);
168
169     if (result == ConfigureResult::Ok) {
170         LogInfo("Configure installation succeeded");
171         m_installerContext.job->SetProgressFlag(true);
172
173         AddTask(new TaskRecovery(m_installerContext));
174
175         // Create installation tasks
176         if (m_installerContext.widgetConfig.packagingType !=
177             WrtDB::PKG_TYPE_DIRECTORY_WEB_APP &&
178             m_installerContext.widgetConfig.packagingType !=
179             WrtDB::PKG_TYPE_HOSTED_WEB_APP &&
180             !m_isDRM)
181         {
182             AddTask(new TaskUnzip(m_installerContext));
183         }
184
185         AddTask(new TaskWidgetConfig(m_installerContext));
186         if (m_installerContext.widgetConfig.packagingType ==
187             WrtDB::PKG_TYPE_HOSTED_WEB_APP)
188         {
189             AddTask(new TaskPrepareFiles(m_installerContext));
190         }
191         AddTask(new TaskCertify(m_installerContext));
192         if (m_needEncryption) {
193             AddTask(new TaskEncryptResource(m_installerContext));
194         }
195
196         AddTask(new TaskFileManipulation(m_installerContext));
197         // TODO: Update progress information for this task
198
199         //This is sort of quick solution, because ACE verdicts are based upon
200         //data from DAO (DB). So AceCheck for now has to be AFTER DbUpdate
201         //task.
202         AddTask(new TaskSmack(m_installerContext));
203
204         AddTask(new TaskManifestFile(m_installerContext));
205         if (m_installerContext.widgetConfig.packagingType ==
206             PKG_TYPE_HYBRID_WEB_APP)
207         {
208             AddTask(new TaskInstallOspsvc(m_installerContext));
209         }
210         AddTask(new TaskCertificates(m_installerContext));
211         AddTask(new TaskDatabase(m_installerContext));
212         AddTask(new TaskAceCheck(m_installerContext));
213     } else if (result == ConfigureResult::Updated) {
214         LogInfo("Configure installation updated");
215         LogInfo("Widget Update");
216         m_installerContext.job->SetProgressFlag(true);
217
218         if (m_jobStruct.m_installMode ==
219             InstallMode::REINSTALL_MODE_DIRECTORY)
220         {
221             AddTask(new TaskPrepareReinstall(m_installerContext));
222         }
223
224         if (m_installerContext.widgetConfig.packagingType !=
225             WrtDB::PKG_TYPE_HOSTED_WEB_APP &&
226             m_installerContext.widgetConfig.packagingType !=
227             WrtDB::PKG_TYPE_DIRECTORY_WEB_APP &&
228             !m_isDRM)
229         {
230             AddTask(new TaskUnzip(m_installerContext));
231         }
232
233         AddTask(new TaskWidgetConfig(m_installerContext));
234
235         if (m_installerContext.widgetConfig.packagingType ==
236             WrtDB::PKG_TYPE_HOSTED_WEB_APP)
237         {
238             AddTask(new TaskPrepareFiles(m_installerContext));
239         }
240
241         AddTask(new TaskCertify(m_installerContext));
242         if (m_needEncryption) {
243             AddTask(new TaskEncryptResource(m_installerContext));
244         }
245
246         if (m_installerContext.widgetConfig.packagingType !=
247             WrtDB::PKG_TYPE_DIRECTORY_WEB_APP)
248         {
249             AddTask(new TaskUpdateFiles(m_installerContext));
250         }
251
252         /* TODO : To backup file, save md5 values */
253         AddTask(new TaskSmack(m_installerContext));
254
255         AddTask(new TaskManifestFile(m_installerContext));
256         if (m_installerContext.widgetConfig.packagingType ==
257             PKG_TYPE_HYBRID_WEB_APP)
258         {
259             AddTask(new TaskInstallOspsvc(m_installerContext));
260         }
261         if (m_installerContext.widgetConfig.packagingType !=
262             WrtDB::PKG_TYPE_DIRECTORY_WEB_APP)
263         {
264             AddTask(new TaskRemoveBackupFiles(m_installerContext));
265         }
266         AddTask(new TaskDatabase(m_installerContext));
267         AddTask(new TaskAceCheck(m_installerContext));
268         //TODO: remove widgetHandle from this task and move before database task
269         // by now widget handle is needed in ace check
270         // Any error in acecheck while update will break widget
271     } else if (result == ConfigureResult::Deferred) {
272         // Installation is deferred
273         LogInfo("Configure installation deferred");
274
275         AddTask(new InstallerTaskFail(result));
276     } else if (result >= ConfigureResult::Failed &&
277             result <= ConfigureResult::Failed_DrmError) {
278         // Installation is not allowed to proceed due to widget update policy
279         LogWarning("Configure installation failed!");
280
281         AddTask(new InstallerTaskFail(result));
282     } else {
283         Assert(false && "Invalid configure result!");
284     }
285 }
286
287 ConfigureResult JobWidgetInstall::PrePareInstallation(
288     const std::string &widgetPath)
289 {
290     ConfigureResult result;
291     m_needEncryption = false;
292
293     Try
294     {
295         std::string tempDir;
296         if (m_jobStruct.m_installMode ==
297             InstallMode::REINSTALL_MODE_DIRECTORY)
298         {
299             std::ostringstream tempPathBuilder;
300             tempPathBuilder << WrtDB::GlobalConfig::GetUserInstalledWidgetPath();
301             tempPathBuilder << WrtDB::GlobalConfig::GetTmpDirPath();
302             tempPathBuilder << "/";
303             tempPathBuilder << widgetPath;
304             tempDir = tempPathBuilder.str();;
305         } else {
306             tempDir =
307                 Jobs::WidgetInstall::createTempPath(
308                         m_jobStruct.m_installMode ==
309                             InstallMode::INSTALL_MODE_PRELOAD);
310         }
311
312         m_isDRM = isDRMWidget(widgetPath);
313         if (true == m_isDRM) {
314             LogDebug("decrypt DRM widget");
315             if (DecryptDRMWidget(widgetPath, tempDir)) {
316                 LogDebug("Failed decrypt DRM widget");
317                 return ConfigureResult::Failed_DrmError;
318             }
319         }
320
321         LogDebug("widgetPath:" << widgetPath);
322
323         m_installerContext.widgetConfig.packagingType =
324             checkPackageType(widgetPath, tempDir);
325         ConfigParserData configData = getWidgetDataFromXML(
326                 widgetPath,
327                 tempDir,
328                 m_installerContext.widgetConfig.packagingType,
329                 m_isDRM,
330                 m_jobStruct.m_installMode ==
331                     InstallMode::REINSTALL_MODE_DIRECTORY);
332         LogDebug("widget packaging type : " <<
333                  m_installerContext.widgetConfig.packagingType.pkgType);
334
335         setTizenId(configData);
336         setApplicationType(configData);
337         m_needEncryption = detectResourceEncryption(configData);
338         setInstallLocationType(configData);
339
340         // Configure installation
341         result = ConfigureInstallation(widgetPath, configData, tempDir);
342     }
343     Catch(Exceptions::ExtractFileFailed)
344     {
345         LogError("Failed to create temporary path for widget");
346         result = ConfigureResult::Failed_InvalidConfig;
347     }
348
349     return result;
350 }
351
352 void JobWidgetInstall::setTizenId(
353     const WrtDB::ConfigParserData &configInfo)
354 {
355     bool shouldMakeAppid = false;
356     using namespace PackageManager;
357     if (!!configInfo.tizenAppId) {
358         LogDebug("Setting tizenAppId provided in config.xml: " <<
359                  configInfo.tizenAppId);
360
361         m_installerContext.widgetConfig.tzAppid = *configInfo.tizenAppId;
362         //check package id.
363         if (!!configInfo.tizenPkgId) {
364             LogDebug("Setting tizenPkgId provided in config.xml: " <<
365                      configInfo.tizenPkgId);
366
367             m_installerContext.widgetConfig.tzPkgid = *configInfo.tizenPkgId;
368         } else {
369             DPL::String appid = *configInfo.tizenAppId;
370             if (appid.length() > PACKAGE_ID_LENGTH) {
371                 m_installerContext.widgetConfig.tzPkgid =
372                     appid.substr(0, PACKAGE_ID_LENGTH);
373             } else {
374                 //old version appid only has 10byte random character is able to install for a while.
375                 //this case appid equal pkgid.
376                 m_installerContext.widgetConfig.tzPkgid =
377                     *configInfo.tizenAppId;
378                 shouldMakeAppid = true;
379             }
380         }
381     } else {
382         shouldMakeAppid = true;
383         TizenPkgId pkgId = WidgetDAOReadOnly::generatePkgId();
384         LogDebug("Checking if pkg id is unique");
385         while (true) {
386             if (!validateTizenPackageID(pkgId)) {
387                 //path exist, chose another one
388                 pkgId = WidgetDAOReadOnly::generatePkgId();
389                 continue;
390             }
391             break;
392         }
393         m_installerContext.widgetConfig.tzPkgid = pkgId;
394         LogInfo("tizen_id name was generated by WRT: " <<
395                 m_installerContext.widgetConfig.tzPkgid);
396     }
397
398     if (shouldMakeAppid == true) {
399         DPL::OptionalString name;
400         DPL::OptionalString defaultLocale = configInfo.defaultlocale;
401
402         FOREACH(localizedData, configInfo.localizedDataSet)
403         {
404             Locale i = localizedData->first;
405             if (!!defaultLocale) {
406                 if (defaultLocale == i) {
407                     name = localizedData->second.name;
408                     break;
409                 }
410             } else {
411                 name = localizedData->second.name;
412                 break;
413             }
414         }
415         regex_t regx;
416         if (regcomp(&regx, REG_NAME_PATTERN, REG_NOSUB | REG_EXTENDED) != 0) {
417             LogDebug("Regcomp failed");
418         }
419
420         LogDebug("Name : " << name);
421         if (!name || (regexec(&regx, DPL::ToUTF8String(*name).c_str(),
422                               static_cast<size_t>(0), NULL, 0) != REG_NOERROR))
423         {
424             // TODO : generate name move to wrt-commons
425             std::string allowedString("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
426             std::ostringstream genName;
427             struct timeval tv;
428             gettimeofday(&tv, NULL);
429             unsigned int seed = time(NULL) + tv.tv_usec;
430
431             genName << "_" << allowedString[rand_r(&seed) % allowedString.length()];
432             name = DPL::FromUTF8String(genName.str());
433             LogDebug("name was generated by WRT");
434         }
435         regfree(&regx);
436         LogDebug("Name : " << name);
437         std::ostringstream genid;
438         genid << m_installerContext.widgetConfig.tzPkgid << "." << name;
439         LogDebug("tizen appid was generated by WRT : " << genid.str());
440
441         DPL::OptionalString appid = DPL::FromUTF8String(genid.str());
442         NormalizeAndTrimSpaceString(appid);
443         m_installerContext.widgetConfig.tzAppid = *appid;
444     }
445
446     // send start signal of pkgmgr
447     getInstallerStruct().pkgmgrInterface->setPkgname(DPL::ToUTF8String(
448                                                          m_installerContext.
449                                                              widgetConfig.
450                                                              tzPkgid));
451     LogInfo("Tizen App Id : " << m_installerContext.widgetConfig.tzAppid);
452     LogInfo("Tizen Pkg Id : " << m_installerContext.widgetConfig.tzPkgid);
453     LogInfo("W3C Widget GUID : " << m_installerContext.widgetConfig.guid);
454 }
455
456 void JobWidgetInstall::configureWidgetLocation(const std::string & widgetPath,
457                                                const std::string& tempPath)
458 {
459     if (m_jobStruct.m_installMode ==
460          InstallMode::REINSTALL_MODE_DIRECTORY)
461     {
462         // replace widget path to installed path
463         m_installerContext.locations =
464             WidgetLocation(DPL::ToUTF8String(m_installerContext.widgetConfig.
465                                                  tzPkgid),
466                            widgetPath, tempPath,
467                            m_installerContext.widgetConfig.packagingType,
468                            m_installerContext.locationType);
469     } else {
470         m_installerContext.locations =
471             WidgetLocation(DPL::ToUTF8String(m_installerContext.widgetConfig.
472                                                  tzPkgid),
473                            widgetPath, tempPath,
474                            m_installerContext.widgetConfig.packagingType,
475                            m_installerContext.locationType);
476     }
477     m_installerContext.locations->registerAppid(
478         DPL::ToUTF8String(m_installerContext.widgetConfig.tzAppid));
479
480     LogInfo("widgetSource " << widgetPath);
481 }
482
483 ConfigureResult JobWidgetInstall::ConfigureInstallation(
484     const std::string &widgetSource,
485     const WrtDB::ConfigParserData &configData,
486     const std::string &tempPath)
487 {
488     ConfigureResult result = ConfigureResult::Failed;
489     WidgetUpdateInfo update;
490
491     // checking installed web application
492     Try {
493         // checking existing application is installed
494         WidgetDAOReadOnly dao(m_installerContext.widgetConfig.tzAppid);
495         // no excpetion means, it isn't update mode
496         getInstallerStruct().pkgmgrInterface->sendSignal(
497                 PKGMGR_START_KEY,
498                 PKGMGR_START_UPDATE);
499
500         update = detectWidgetUpdate(configData,
501                                     m_installerContext.widgetConfig.tzAppid);
502         result = checkWidgetUpdate(update);
503         if (result != ConfigureResult::Updated) {
504             // Already installed TizenAppId. return failed
505             return ConfigureResult::Failed_AlreadyInstalled;
506         }
507         m_installerContext.isUpdateMode = true;
508     }
509     Catch(WidgetDAOReadOnly::Exception::WidgetNotExist) {
510         result = ConfigureResult::Ok;
511         getInstallerStruct().pkgmgrInterface->sendSignal(
512                 PKGMGR_START_KEY,
513                 PKGMGR_START_INSTALL);
514         m_installerContext.isUpdateMode = false;
515
516         if (!validateTizenApplicationID(
517             m_installerContext.widgetConfig.tzAppid))
518         {
519             LogError("tizen application ID is already used");
520             return ConfigureResult::Failed_InvalidConfig;
521         }
522         if (!validateTizenPackageID(m_installerContext.widgetConfig.tzPkgid)) {
523             LogError("tizen package ID is already used");
524             return ConfigureResult::Failed_AlreadyInstalled;
525         }
526     }
527
528     configureWidgetLocation(widgetSource, tempPath);
529
530     // Init installer context
531     m_installerContext.installStep = InstallerContext::INSTALL_START;
532     m_installerContext.job = this;
533     m_installerContext.widgetConfig.shareHref = std::string();
534
535     return result;
536 }
537
538 bool JobWidgetInstall::validateTizenApplicationID(
539     const WrtDB::TizenAppId &tizenAppId)
540 {
541     LogInfo("tizen application ID = [" << tizenAppId << "]");
542
543     regex_t reg;
544     if (regcomp(&reg, REG_TIZENID_PATTERN, REG_NOSUB | REG_EXTENDED) != 0) {
545         LogDebug("Regcomp failed");
546     }
547
548     if (regexec(&reg, DPL::ToUTF8String(tizenAppId).c_str(), 0, NULL, 0)
549         == REG_NOMATCH)
550     {
551         regfree(&reg);
552         return false;
553     }
554     regfree(&reg);
555     return true;
556 }
557
558 bool JobWidgetInstall::validateTizenPackageID(
559     const WrtDB::TizenPkgId &tizenPkgId)
560 {
561     std::string pkgId = DPL::ToUTF8String(tizenPkgId);
562
563     std::string installPath =
564         std::string(GlobalConfig::GetUserInstalledWidgetPath()) +
565         "/" + pkgId;
566     std::string preinstallPath =
567         std::string(GlobalConfig::GetUserPreloadedWidgetPath()) +
568         "/" + pkgId;
569
570     struct stat dirStat;
571     if ((stat(installPath.c_str(), &dirStat) == 0) ||
572         (stat(preinstallPath.c_str(), &dirStat) == 0))
573     {
574         return false;
575     }
576     return true;
577 }
578
579 ConfigureResult JobWidgetInstall::checkWidgetUpdate(
580     const WidgetUpdateInfo &update)
581 {
582     LogInfo("incoming version = '" << update.incomingVersion);
583     LogInfo("Tizen AppID = " << update.tzAppId);
584
585     if (update.existingVersion.IsNull() || update.incomingVersion.IsNull()) {
586         return ConfigureResult::Failed;
587     }
588
589     // Check running state
590     bool isRunning = false;
591     int ret =
592         app_manager_is_running(DPL::ToUTF8String(update.tzAppId).c_str(),
593                                &isRunning);
594     if (APP_MANAGER_ERROR_NONE != ret) {
595         LogError("Fail to get running state");
596         return ConfigureResult::Failed_WidgetRunning;
597     }
598
599     if (true == isRunning) {
600         // get app_context for running application
601         // app_context must be released with app_context_destroy
602         app_context_h appCtx = NULL;
603         ret =
604             app_manager_get_app_context(
605                 DPL::ToUTF8String(update.tzAppId).c_str(),
606                 &appCtx);
607         if (APP_MANAGER_ERROR_NONE != ret) {
608             LogError("Fail to get app_context");
609             return ConfigureResult::Failed_WidgetRunning;
610         }
611
612         // terminate app_context_h
613         ret = app_manager_terminate_app(appCtx);
614         if (APP_MANAGER_ERROR_NONE != ret) {
615             LogError("Fail to terminate running application");
616             app_context_destroy(appCtx);
617             return ConfigureResult::Failed_WidgetRunning;
618         } else {
619             app_context_destroy(appCtx);
620             // app_manager_terminate_app isn't sync API
621             // wait until application isn't running (50ms * 100)
622             bool isStillRunning = true;
623             int checkingloop = 100;
624             struct timespec duration = { 0, 50 * 1000 * 1000 };
625             while (--checkingloop >= 0) {
626                 nanosleep(&duration, NULL);
627                 int ret =
628                     app_manager_is_running(
629                         DPL::ToUTF8String(update.tzAppId).c_str(),
630                         &isStillRunning);
631                 if (APP_MANAGER_ERROR_NONE != ret) {
632                     LogError("Fail to get running state");
633                     return ConfigureResult::Failed_WidgetRunning;
634                 }
635                 if (!isStillRunning) {
636                     break;
637                 }
638             }
639             if (isStillRunning) {
640                 LogError("Fail to terminate running application");
641                 return ConfigureResult::Failed_WidgetRunning;
642             }
643             LogInfo("terminate application");
644         }
645     }
646
647     m_installerContext.widgetConfig.tzAppid = update.tzAppId;
648
649     if (isUpperVersion(update.existingVersion, update.incomingVersion) ||
650         (m_jobStruct.m_installMode == InstallMode::INSTALL_MODE_DIRECTORY) ||
651         (m_jobStruct.m_installMode == InstallMode::REINSTALL_MODE_DIRECTORY))
652     {
653         LogInfo("Whether widget policy allow proceed ok");
654         return ConfigureResult::Updated;
655     } else {
656         return ConfigureResult::Failed_LowerVersion;
657     }
658
659     return ConfigureResult::Failed;
660 }
661
662 bool JobWidgetInstall::isUpperVersion(
663     const OptionalWidgetVersion &existingVersion,
664     const OptionalWidgetVersion &incomingVersion)
665 {
666     LogInfo("Existing version = '" << *existingVersion);
667     LogInfo("Incoming version = '" << *incomingVersion);
668
669     if (!existingVersion && !incomingVersion) {
670         return false;
671     } else if (!existingVersion && !!incomingVersion) {
672         return false;
673     } else if (!!existingVersion && !incomingVersion) {
674         return true;
675     } else {
676         if (!existingVersion->IsWac() || !incomingVersion->IsWac()) {
677             return false;
678         } else {
679             if (*incomingVersion == *existingVersion) {
680                 return false;
681             } else if (*incomingVersion > *existingVersion) {
682                 return true;
683             } else {
684                 return false;
685             }
686         }
687     }
688 }
689
690 ConfigParserData JobWidgetInstall::getWidgetDataFromXML(
691     const std::string &widgetSource,
692     const std::string &tempPath,
693     WrtDB::PackagingType pkgType,
694     bool isDRM,
695     bool isReinstall)
696 {
697     // Parse config
698     ParserRunner parser;
699     ConfigParserData configInfo;
700
701     Try
702     {
703         if (pkgType == PKG_TYPE_HOSTED_WEB_APP) {
704             parser.Parse(widgetSource,
705                          ElementParserPtr(
706                              new RootParser<WidgetParser>(configInfo,
707                                                           DPL::FromUTF32String(
708                                                               L"widget"))));
709         } else if (pkgType == PKG_TYPE_DIRECTORY_WEB_APP) {
710             std::string configPath;
711             configPath = tempPath;
712             configPath += "/";
713             configPath += WITH_OSP_XML;
714
715             if (isReinstall) {
716                 // checking RDS data directory
717                 if (access(configPath.c_str(), F_OK) != 0) {
718                     std::string tzAppId =
719                         widgetSource.substr(widgetSource.find_last_of("/")+1);
720                     WidgetDAOReadOnly dao(WidgetDAOReadOnly::getTzAppId(DPL::FromUTF8String(tzAppId)));
721                     configPath = DPL::ToUTF8String(*dao.getWidgetInstalledPath());
722                     configPath += "/";
723                     configPath += WITH_OSP_XML;
724                 }
725             }
726             parser.Parse(configPath,
727                          ElementParserPtr(
728                              new RootParser<WidgetParser>(
729                                  configInfo,
730                                  DPL::FromUTF32String(L"widget"))));
731         } else {
732             if (!isDRM) {
733                 std::unique_ptr<DPL::ZipInput> zipFile(
734                     new DPL::ZipInput(widgetSource));
735
736                 std::unique_ptr<DPL::ZipInput::File> configFile;
737
738                 // Open config.xml file
739                 if (pkgType == PKG_TYPE_HYBRID_WEB_APP) {
740                     configFile.reset(zipFile->OpenFile(WITH_OSP_XML));
741                 } else {
742                     configFile.reset(zipFile->OpenFile(CONFIG_XML));
743                 }
744
745                 // Extract config
746                 DPL::BinaryQueue buffer;
747                 DPL::AbstractWaitableInputAdapter inputAdapter(configFile.get());
748                 DPL::AbstractWaitableOutputAdapter outputAdapter(&buffer);
749                 DPL::Copy(&inputAdapter, &outputAdapter);
750                 parser.Parse(&buffer,
751                              ElementParserPtr(
752                                  new RootParser<WidgetParser>(configInfo,
753                                                               DPL::
754                                                                   FromUTF32String(
755                                                                   L"widget"))));
756             } else {
757                 // DRM widget
758                 std::string configFile;
759                 if (pkgType == PKG_TYPE_HYBRID_WEB_APP) {
760                     configFile = tempPath + "/" + WITH_OSP_XML;
761                 } else {
762                     configFile = tempPath + "/" + CONFIG_XML;
763                 }
764
765                 parser.Parse(configFile,
766                              ElementParserPtr(
767                                  new RootParser<WidgetParser>(configInfo,
768                                                               DPL::
769                                                                   FromUTF32String(
770                                                                   L"widget"))));
771             }
772         }
773     }
774     Catch(DPL::ZipInput::Exception::OpenFailed)
775     {
776         LogError("Failed to open widget package");
777         return ConfigParserData();
778     }
779     Catch(DPL::ZipInput::Exception::OpenFileFailed)
780     {
781         LogError("Failed to open config.xml file");
782         return ConfigParserData();
783     }
784     Catch(DPL::CopyFailed)
785     {
786         LogError("Failed to extract config.xml file");
787         return ConfigParserData();
788     }
789     Catch(DPL::FileInput::Exception::OpenFailed)
790     {
791         LogError("Failed to open config.xml file");
792         return ConfigParserData();
793     }
794     Catch(ElementParser::Exception::ParseError)
795     {
796         LogError("Failed to parse config.xml file");
797         return ConfigParserData();
798     }
799     Catch(DPL::ZipInput::Exception::SeekFileFailed)
800     {
801         LogError("Failed to seek widget archive - corrupted package?");
802         return ConfigParserData();
803     }
804     return configInfo;
805 }
806
807 WidgetUpdateInfo JobWidgetInstall::detectWidgetUpdate(
808     const ConfigParserData &configInfo,
809     const WrtDB::TizenAppId &tizenId)
810 {
811     LogInfo("Checking up widget package for config.xml...");
812     OptionalWidgetVersion incomingVersion;
813
814     if (!configInfo.version.IsNull()) {
815         incomingVersion =
816             DPL::Optional<WidgetVersion>(
817                 WidgetVersion(*configInfo.version));
818     }
819
820     WidgetDAOReadOnly dao(tizenId);
821
822     OptionalWidgetVersion optVersion;
823     DPL::OptionalString version = dao.getVersion();
824     if (!version.IsNull()) {
825         optVersion = OptionalWidgetVersion(WidgetVersion(*version));
826     }
827
828     return WidgetUpdateInfo(
829         dao.getTzAppId(),
830         optVersion,
831         incomingVersion);
832 }
833
834 void JobWidgetInstall::SendProgress()
835 {
836     using namespace PackageManager;
837     if (GetProgressFlag() != false) {
838         if (getInstallerStruct().progressCallback != NULL) {
839             // send progress signal of pkgmgr
840             std::ostringstream percent;
841             percent << static_cast<int>(GetProgressPercent());
842             getInstallerStruct().pkgmgrInterface->sendSignal(
843                 PKGMGR_PROGRESS_KEY,
844                 percent.str());
845
846             LogDebug("Call widget install progressCallbak");
847             getInstallerStruct().progressCallback(
848                 getInstallerStruct().userParam,
849                 GetProgressPercent(),
850                 GetProgressDescription());
851         }
852     }
853 }
854
855 void JobWidgetInstall::SendProgressIconPath(const std::string &path)
856 {
857     using namespace PackageManager;
858     if (GetProgressFlag() != false) {
859         if (getInstallerStruct().progressCallback != NULL) {
860             // send progress signal of pkgmgr
861             getInstallerStruct().pkgmgrInterface->sendSignal(
862                 PKGMGR_ICON_PATH,
863                 path);
864         }
865     }
866 }
867
868 void JobWidgetInstall::SendFinishedSuccess()
869 {
870     using namespace PackageManager;
871     // TODO : sync should move to separate task.
872     sync();
873
874     if (INSTALL_LOCATION_TYPE_EXTERNAL == m_installerContext.locationType) {
875         if (m_installerContext.isUpdateMode) {
876             WidgetInstallToExtSingleton::Instance().postUpgrade(true);
877         } else {
878             WidgetInstallToExtSingleton::Instance().postInstallation(true);
879         }
880         WidgetInstallToExtSingleton::Instance().deinitialize();
881     }
882
883     // remove widget install information file
884     unlink(m_installerContext.installInfo.c_str());
885
886     //inform widget info
887     JobWidgetInstall::displayWidgetInfo();
888
889     TizenAppId& tizenId = m_installerContext.widgetConfig.tzAppid;
890
891     // send signal of pkgmgr
892     getInstallerStruct().pkgmgrInterface->sendSignal(
893         PKGMGR_END_KEY,
894         PKGMGR_END_SUCCESS);
895
896     LogDebug("Call widget install successfinishedCallback");
897     getInstallerStruct().finishedCallback(getInstallerStruct().userParam,
898                                           DPL::ToUTF8String(
899                                               tizenId), Jobs::Exceptions::Success);
900 }
901
902 void JobWidgetInstall::SendFinishedFailure()
903 {
904     using namespace PackageManager;
905     // remove widget install information file
906     unlink(m_installerContext.installInfo.c_str());
907
908     LogError("Error number: " << m_exceptionCaught);
909     LogError("Message: " << m_exceptionMessage);
910     TizenAppId & tizenId = m_installerContext.widgetConfig.tzAppid;
911
912     LogDebug("Call widget install failure finishedCallback");
913     std::stringstream errorNum;
914     errorNum << m_exceptionCaught;
915
916     // send signal of pkgmgr
917     getInstallerStruct().pkgmgrInterface->sendSignal(
918         PKGMGR_ERROR,
919         errorNum.str());
920
921     getInstallerStruct().pkgmgrInterface->sendSignal(
922         PKGMGR_END_KEY,
923         PKGMGR_END_FAILURE);
924
925     getInstallerStruct().finishedCallback(getInstallerStruct().userParam,
926                                           DPL::ToUTF8String(
927                                               tizenId), m_exceptionCaught);
928 }
929
930 void JobWidgetInstall::SaveExceptionData(const Jobs::JobExceptionBase &e)
931 {
932     m_exceptionCaught = static_cast<Jobs::Exceptions::Type>(e.getParam());
933     m_exceptionMessage = e.GetMessage();
934 }
935
936 void JobWidgetInstall::displayWidgetInfo()
937 {
938     WidgetDAOReadOnly dao(m_installerContext.widgetConfig.tzAppid);
939
940     std::ostringstream out;
941     WidgetLocalizedInfo localizedInfo =
942         W3CFileLocalization::getLocalizedInfo(dao.getTzAppId());
943
944     out << std::endl <<
945     "===================================== INSTALLED WIDGET INFO =========" \
946     "============================";
947     out << std::endl << "Name:                        " << localizedInfo.name;
948     out << std::endl << "AppId:                     " << dao.getTzAppId();
949     WidgetSize size = dao.getPreferredSize();
950     out << std::endl << "Width:                       " << size.width;
951     out << std::endl << "Height:                      " << size.height;
952     out << std::endl << "Start File:                  " <<
953     W3CFileLocalization::getStartFile(dao.getTzAppId());
954     out << std::endl << "Version:                     " << dao.getVersion();
955     out << std::endl << "Licence:                     " <<
956     localizedInfo.license;
957     out << std::endl << "Licence Href:                " <<
958     localizedInfo.licenseHref;
959     out << std::endl << "Description:                 " <<
960     localizedInfo.description;
961     out << std::endl << "Widget Id:                   " << dao.getGUID();
962     out << std::endl << "Widget recognized:           " << dao.isRecognized();
963     out << std::endl << "Widget distributor signed:   " <<
964     dao.isDistributorSigned();
965     out << std::endl << "Widget trusted:              " << dao.isTrusted();
966
967     OptionalWidgetIcon icon = W3CFileLocalization::getIcon(dao.getTzAppId());
968     DPL::OptionalString iconSrc =
969         !!icon ? icon->src : DPL::OptionalString::Null;
970     out << std::endl << "Icon:                        " << iconSrc;
971
972     out << std::endl << "Preferences:";
973     {
974         PropertyDAOReadOnly::WidgetPreferenceList list = dao.getPropertyList();
975         FOREACH(it, list)
976         {
977             out << std::endl << "  Key:                       " <<
978             it->key_name;
979             out << std::endl << "      Readonly:              " <<
980             it->readonly;
981         }
982     }
983
984     out << std::endl << "Features:";
985     {
986         WidgetFeatureSet list = dao.getFeaturesList();
987         FOREACH(it, list)
988         {
989             out << std::endl << "  Name:                      " << it->name;
990         }
991     }
992
993     out << std::endl;
994
995     LogInfo(out.str());
996 }
997
998 WrtDB::PackagingType JobWidgetInstall::checkPackageType(
999     const std::string &widgetSource,
1000     const std::string &tempPath)
1001 {
1002     // Check installation type (direcotory/ or config.xml or widget.wgt)
1003     if (m_jobStruct.m_installMode == InstallMode::INSTALL_MODE_DIRECTORY ||
1004          m_jobStruct.m_installMode == InstallMode::REINSTALL_MODE_DIRECTORY)
1005     {
1006         LogDebug("Install directly from directory");
1007         return PKG_TYPE_DIRECTORY_WEB_APP;
1008     }
1009     if (hasExtension(widgetSource, XML_EXTENSION)) {
1010         LogInfo("Hosted app installation");
1011         return PKG_TYPE_HOSTED_WEB_APP;
1012     }
1013
1014     if (m_isDRM) {
1015         std::string configFile = tempPath + "/" + CONFIG_XML;
1016         if (WrtUtilFileExists(configFile)) {
1017             return PKG_TYPE_NOMAL_WEB_APP;
1018         }
1019
1020         configFile = tempPath + "/" + WITH_OSP_XML;
1021         if (WrtUtilFileExists(configFile)) {
1022             return PKG_TYPE_HYBRID_WEB_APP;
1023         }
1024     } else {
1025         std::unique_ptr<DPL::ZipInput> zipFile;
1026
1027         Try
1028         {
1029             // Open zip file
1030             zipFile.reset(new DPL::ZipInput(widgetSource));
1031         }
1032         Catch(DPL::ZipInput::Exception::OpenFailed)
1033         {
1034             LogDebug("Failed to open widget package");
1035             return PKG_TYPE_UNKNOWN;
1036         }
1037         Catch(DPL::ZipInput::Exception::SeekFileFailed)
1038         {
1039             LogError("Failed to seek widget package file");
1040             return PKG_TYPE_UNKNOWN;
1041         }
1042
1043         Try
1044         {
1045             // Open config.xml file in package root
1046             std::unique_ptr<DPL::ZipInput::File> configFile(
1047                 zipFile->OpenFile(CONFIG_XML));
1048             return PKG_TYPE_NOMAL_WEB_APP;
1049         }
1050         Catch(DPL::ZipInput::Exception::OpenFileFailed)
1051         {
1052             LogDebug("Could not find config.xml");
1053         }
1054
1055         Try
1056         {
1057             // Open config.xml file in package root
1058             std::unique_ptr<DPL::ZipInput::File> configFile(
1059                 zipFile->OpenFile(WITH_OSP_XML));
1060
1061             return PKG_TYPE_HYBRID_WEB_APP;
1062         }
1063         Catch(DPL::ZipInput::Exception::OpenFileFailed)
1064         {
1065             LogDebug("Could not find wgt/config.xml");
1066             return PKG_TYPE_UNKNOWN;
1067         }
1068     }
1069
1070     return PKG_TYPE_UNKNOWN;
1071 }
1072
1073 void JobWidgetInstall::setApplicationType(
1074     const WrtDB::ConfigParserData &configInfo)
1075 {
1076     FOREACH(iterator, configInfo.nameSpaces) {
1077         LogInfo("namespace = [" << *iterator << "]");
1078         AppType currentAppType = APP_TYPE_UNKNOWN;
1079
1080         if (*iterator == ConfigurationNamespace::W3CWidgetNamespaceName) {
1081             continue;
1082         } else if (
1083             *iterator ==
1084             ConfigurationNamespace::WacWidgetNamespaceNameForLinkElement ||
1085             *iterator ==
1086             ConfigurationNamespace::WacWidgetNamespaceName)
1087         {
1088             currentAppType = APP_TYPE_WAC20;
1089         } else if (*iterator ==
1090                    ConfigurationNamespace::TizenWebAppNamespaceName)
1091         {
1092             currentAppType = APP_TYPE_TIZENWEBAPP;
1093         }
1094
1095         if (m_installerContext.widgetConfig.webAppType ==
1096             APP_TYPE_UNKNOWN)
1097         {
1098             m_installerContext.widgetConfig.webAppType = currentAppType;
1099         } else if (m_installerContext.widgetConfig.webAppType ==
1100                    currentAppType)
1101         {
1102             continue;
1103         } else {
1104             ThrowMsg(Exceptions::WidgetConfigFileInvalid,
1105                      "Config.xml has more than one namespace");
1106         }
1107     }
1108
1109     // If there is no define, type set to WAC 2.0
1110     if (m_installerContext.widgetConfig.webAppType == APP_TYPE_UNKNOWN) {
1111         m_installerContext.widgetConfig.webAppType = APP_TYPE_WAC20;
1112     }
1113
1114     LogInfo("type = [" <<
1115             m_installerContext.widgetConfig.webAppType.getApptypeToString() <<
1116             "]");
1117 }
1118
1119 bool JobWidgetInstall::detectResourceEncryption(
1120     const WrtDB::ConfigParserData &configData)
1121 {
1122     FOREACH(it, configData.settingsList)
1123     {
1124         if (it->m_name == SETTING_VALUE_ENCRYPTION &&
1125             it->m_value == SETTING_VALUE_ENCRYPTION_ENABLE)
1126         {
1127             LogDebug("resource need encryption");
1128             return true;
1129         }
1130     }
1131     return false;
1132 }
1133
1134 void JobWidgetInstall::setInstallLocationType(
1135     const
1136     WrtDB::ConfigParserData &
1137     configData)
1138 {
1139     m_installerContext.locationType = INSTALL_LOCATION_TYPE_NOMAL;
1140
1141     if (m_jobStruct.m_installMode == InstallMode::INSTALL_MODE_PRELOAD) {
1142         m_installerContext.locationType =
1143             INSTALL_LOCATION_TYPE_PRELOAD;
1144     } else {
1145         FOREACH(it, configData.settingsList)
1146         {
1147             if (it->m_name == SETTING_VALUE_INSTALLTOEXT_NAME &&
1148                 it->m_value ==
1149                 SETTING_VALUE_INSTALLTOEXT_PREPER_EXT)
1150             {
1151                 LogDebug("This widget will be installed to sd card");
1152                 m_installerContext.locationType =
1153                     INSTALL_LOCATION_TYPE_EXTERNAL;
1154             }
1155         }
1156     }
1157 }
1158
1159 bool JobWidgetInstall::isDRMWidget(std::string /*widgetPath*/)
1160 {
1161     /* TODO */
1162     return false;
1163 }
1164
1165 bool JobWidgetInstall::DecryptDRMWidget(std::string /*widgetPath*/,
1166                                         std::string /*destPath*/)
1167 {
1168     /* TODO */
1169     return false;
1170 }
1171 } //namespace WidgetInstall
1172 } //namespace Jobs