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