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