2 * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 * @file job_widget_install.cpp
18 * @author Radoslaw Wicik r.wicik@samsung.com
19 * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com)
21 * @brief Implementation file for main installer task
23 #include <dpl/noncopyable.h>
24 #include <dpl/abstract_waitable_input_adapter.h>
25 #include <dpl/abstract_waitable_output_adapter.h>
26 #include <dpl/zip_input.h>
27 #include <dpl/scoped_ptr.h>
28 #include <dpl/binary_queue.h>
30 #include <dpl/assert.h>
31 #include <dpl/sstream.h>
32 #include <dpl/wrt-dao-ro/common_dao_types.h>
33 #include "root_parser.h"
34 #include "widget_parser.h"
35 #include "parser_runner.h"
36 #include <widget_install/job_widget_install.h>
37 #include <widget_install/task_unzip.h>
38 #include <widget_install/task_certify.h>
39 #include <widget_install/task_widget_config.h>
40 #include <widget_install/task_db_update.h>
41 #include <widget_install/task_ace_check.h>
42 #include <widget_install/task_smack.h>
43 #include <widget_install/task_manifest_file.h>
44 #include <widget_install/task_private_storage.h>
45 #include <widget_install/task_prepare_files.h>
46 #include <widget_install/task_recovery.h>
47 #include <widget_install/task_install_ospsvc.h>
48 #include <widget_install/task_update_files.h>
49 #include <widget_install/task_new_db_insert.h>
50 #include <widget_install/task_remove_backup.h>
51 #include <widget_install/widget_install_errors.h>
52 #include <widget_install/widget_install_context.h>
59 #include <dpl/wrt-dao-ro/widget_dao_read_only.h>
60 #include <dpl/wrt-dao-ro/global_config.h>
61 #include <dpl/wrt-dao-rw/global_dao.h> // TODO remove
63 #include <dpl/localization/w3c_file_localization.h>
64 #include <libiriwrapper.h>
65 #include <pkg-manager/pkgmgr_signal.h>
67 using namespace WrtDB;
69 namespace // anonymous
71 const char * const CONFIG_XML = "config.xml";
72 const char * const WITH_OSP_XML = "res/wgt/config.xml";
74 //allowed: a-z, A-Z, 0-9, '.', '-', '_', ' '
75 const char* REG_TIZENID_PATTERN = "^[-. a-zA-Z0-9_@+=]*$";
76 const int RESERVED_COUNT = 20; //number of chars reserved in name (e.g. for '.desktop')
77 const int MAX_TIZENID_LENTH = 255 - RESERVED_COUNT;
79 class InstallerTaskFail :
80 public DPL::TaskDecl<InstallerTaskFail>
88 ThrowMsg(Jobs::WidgetInstall::Exceptions::Deferred,
89 "Widget installation or update deferred!");
91 ThrowMsg(Jobs::WidgetInstall::Exceptions::NotAllowed,
92 "Widget installation or update not allowed!");
97 InstallerTaskFail(bool deferred) :
98 DPL::TaskDecl<InstallerTaskFail>(this),
101 AddStep(&InstallerTaskFail::StepFail);
105 const std::string XML_EXTENSION = ".xml";
107 bool hasExtension(const std::string& filename, const std::string& extension) {
108 LogDebug("Looking for extension " << extension << " in: " << filename);
109 size_t fileLen = filename.length();
110 size_t extLen = extension.length();
111 if (fileLen < extLen) {
112 LogError("Filename " << filename << " is shorter than extension "
116 return (0 == filename.compare(fileLen-extLen, extLen, extension));
118 } // namespace anonymous
121 namespace WidgetInstall {
122 JobWidgetInstall::JobWidgetInstall(std::string const &widgetPath,
123 const WidgetInstallationStruct &installerStruct) :
125 JobContextBase<WidgetInstallationStruct>(installerStruct),
126 m_exceptionCaught(Exceptions::Success)
128 // Check installation type (config.xml or widget.wgt)
129 m_installerContext.browserRequest = hasExtension(widgetPath, XML_EXTENSION);
131 LogInfo("Hosted app installation: " << m_installerContext.browserRequest);
134 gettimeofday(&tv, NULL);
135 srand(time(NULL) + tv.tv_usec);
136 WrtDB::DbWidgetHandle handle;
138 handle = rand() % INT_MAX + 1;
139 LogInfo("Random widget handle: " << handle);
140 } while (WidgetDAOReadOnly::isWidgetInstalled(handle));
142 m_installerContext.widgetHandle = handle;
143 m_installerContext.m_quiet = m_jobStruct.m_quiet;
145 m_installerContext.widgetConfig.pType = checkPackageType(widgetPath);
147 ConfigParserData configData = getWidgetDataFromXML(widgetPath,
148 m_installerContext.browserRequest,
149 m_installerContext.widgetConfig.pType);
150 WidgetUpdateInfo update = detectWidgetUpdate(configData);
153 // Configure installation
154 ConfigureResult result = ConfigureInstallation(widgetPath,
155 m_installerContext.browserRequest, update);
156 if (!setTizenId(configData)) {
157 result = ConfigureResult::Failed;
159 using namespace PackageManager;
160 LogInfo("WidgetHandle: " << m_installerContext.widgetHandle);
161 LogInfo("Tizen Id: " << m_installerContext.widgetConfig.pkgname);
163 // send start signal of pkgmgr
164 PkgmgrSignalSingleton::Instance().setPkgname(
166 *m_installerContext.widgetConfig.pkgname));
167 PkgmgrSignalSingleton::Instance().sendSignal(
169 PKGMGR_START_INSTALL);
172 if (result == ConfigureResult::Ok) {
173 LogInfo("Configure installation succeeded");
175 AddTask(new TaskRecovery(m_installerContext));
177 // Create installation tasks
178 if (!m_installerContext.browserRequest) {
179 AddTask(new TaskUnzip(m_installerContext));
181 AddTask(new TaskWidgetConfig(m_installerContext));
182 AddTask(new TaskCertify(m_installerContext));
183 AddTask(new TaskDbUpdate(m_installerContext));
184 // TODO: Update progress information for this task
186 AddTask(new TaskPrivateStorage(m_installerContext));
187 AddTask(new TaskAceCheck(m_installerContext));
188 //This is sort of quick solution, because ACE verdicts are based upon
189 //data from DAO (DB). So AceCheck for now has to be AFTER DbUpdate
191 AddTask(new TaskSmack(m_installerContext));
193 AddTask(new TaskManifestFile(m_installerContext));
194 if (m_installerContext.widgetConfig.pType ==
195 PKG_TYPE_TIZEN_WITHSVCAPP) {
196 AddTask(new TaskInstallOspsvc(m_installerContext));
198 } else if (result == ConfigureResult::Updated) {
199 LogInfo("Configure installation updated");
200 LogInfo("Widget Update");
202 if (!m_installerContext.browserRequest) {
203 AddTask(new TaskUnzip(m_installerContext));
205 AddTask(new TaskWidgetConfig(m_installerContext));
207 AddTask(new TaskCertify(m_installerContext));
208 AddTask(new TaskUpdateFiles(m_installerContext));
209 AddTask(new TaskNewDbInsert(m_installerContext));
211 /* TODO : To backup file, save md5 values */
212 AddTask(new TaskAceCheck(m_installerContext));
213 AddTask(new TaskSmack(m_installerContext));
215 AddTask(new TaskManifestFile(m_installerContext));
216 AddTask(new TaskRemoveBackupFiles(m_installerContext));
217 if (m_installerContext.widgetConfig.pType ==
218 PKG_TYPE_TIZEN_WITHSVCAPP) {
219 AddTask(new TaskInstallOspsvc(m_installerContext));
221 } else if (result == ConfigureResult::Deferred) {
222 // Installation is deferred
223 LogInfo("Configure installation deferred");
225 AddTask(new InstallerTaskFail(true));
226 } else if (result == ConfigureResult::Failed) {
227 // Installation is not allowed to proceed due to widget update policy
228 LogWarning("Configure installation failed!");
230 AddTask(new InstallerTaskFail(false));
232 Assert(false && "Invalid configure result!");
236 bool JobWidgetInstall::setTizenId(
237 const WrtDB::ConfigParserData &configInfo)
239 Assert(!!m_installerContext.widgetHandle
240 && "Widget Handle should be initialized");
243 regcomp(®, REG_TIZENID_PATTERN, REG_NOSUB);
245 if(!!configInfo.tizenId) {
246 LogDebug("Setting tizenId provided in config.xml");
247 if ((regexec(®, DPL::ToUTF8String(*(configInfo.tizenId)).c_str(),
248 static_cast<size_t>(0), NULL, 0) != 0) ||
249 (DPL::ToUTF8String(*(configInfo.tizenId)).size() > MAX_TIZENID_LENTH) ||
250 (stat((std::string(GlobalConfig::GetUserInstalledWidgetPath()) + "/"
251 + DPL::ToUTF8String(*(configInfo.tizenId))).c_str(), &dirStat) == 0))
253 //it is true when tizenId does not fit REG_TIZENID_PATTERN
254 LogError("pkgName provided but not proper.");
258 m_installerContext.widgetConfig.pkgname = configInfo.tizenId;
261 LogInfo("package name is generated by WRT");
262 // tizen id should be generated by secure random algorithm
263 std::string pkgName = WrtDB::GlobalConfig::GetPkgnamePrefix();
266 FOREACH(it, configInfo.localizedDataSet)
268 if (!!((it->second).name)) {
269 //there is a name provided
270 std::string name = DPL::ToUTF8String(*(it->second).name);
271 //cut very long widget's name
272 name = name.substr(0, MAX_TIZENID_LENTH - strlen(
273 WrtDB::GlobalConfig::GetPkgnamePrefix()));
274 //check name if all characters are supported by filesystem
275 if (regexec(®, name.c_str(), static_cast<size_t>(0), NULL, 0)
278 //WidgetName is ok and can be used as package name
279 //replace all spaces with '_'
281 while((pos = name.find(" ", pos)) != std::string::npos) {
282 name.replace(pos, 1, "_");
292 if (!named) // there is no widget name provided, use widgetHandle
294 pkgName += std::to_string(*(m_installerContext.widgetHandle));
297 //check if there is package with same name and if generate different name
298 std::string path = GlobalConfig::GetUserInstalledWidgetPath();
301 std::ostringstream newPath;
302 newPath << path << pkgName;
305 for (int i = 0;; ++i) {
306 if (stat(newPath.str().c_str(), &dirStat) == 0) {
307 //path exist, chose another one, eg. widgetName1
308 suffix = std::to_string(i + 1);
309 pkgName = pkgName.substr(
310 0, MAX_TIZENID_LENTH - suffix.size());
312 newPath << path << pkgName << suffix;
319 m_installerContext.widgetConfig.pkgname =
320 DPL::FromUTF8String(pkgName);
325 LogInfo("Tizen Id : " << m_installerContext.widgetConfig.pkgname);
326 LogInfo("W3C Widget GUID : " << m_installerContext.widgetConfig.guid);
330 DPL::Optional<WidgetHandle> JobWidgetInstall::getNewWidgetHandle() const
332 return m_installerContext.widgetHandle;
335 JobWidgetInstall::ConfigureResult JobWidgetInstall::ConfigureInstallation(
336 const std::string &widgetSource,
338 const WidgetUpdateInfo &update)
341 "Widget install/update: incoming guid = '" <<
342 update.incomingGUID << "'");
344 "Widget install/update: incoming version = '" <<
345 update.incomingVersion << "'");
348 WidgetUpdateMode::Type updateTypeCheckBit;
350 if (update.existingWidgetInfo.isExist == false) {
351 LogInfo("Widget info does not exist");
352 updateTypeCheckBit = WidgetUpdateMode::NotInstalled;
354 LogInfo("Widget info exists. Handle: " <<
355 update.existingWidgetInfo.existingHandle);
357 DPL::OptionalString pkgname =
358 WidgetDAOReadOnly(update.existingWidgetInfo.existingHandle).getPkgname();
360 if(pkgname.IsNull()) {
361 LogInfo("But package name doesn't exist");
362 return ConfigureResult::Failed;
365 LogInfo("Widget model exists. package name: " << pkgname);
366 if (aul_app_is_running(DPL::ToUTF8String(*pkgname).c_str())) {
367 // Must be deferred when update in progress
368 if (m_jobStruct.updateMode == WidgetUpdateMode::PolicyWac) {
370 "Widget is already running. Policy is update according to WAC");
372 return ConfigureResult::Deferred;
375 "Widget is already running. Policy is not update according to WAC");
376 LogInfo("Installation aborted: " << widgetSource);
378 return ConfigureResult::Failed;
382 m_installerContext.widgetConfig.pkgname = pkgname;
383 OptionalWidgetVersion existingVersion;
384 existingVersion = update.existingWidgetInfo.existingVersion;
385 OptionalWidgetVersion incomingVersion = update.incomingVersion;
387 updateTypeCheckBit = CalcWidgetUpdatePolicy(existingVersion,
392 bool canProceed = (m_jobStruct.updateMode & updateTypeCheckBit) > 0;
394 LogInfo("Whether widget policy allow proceed: " << canProceed);
396 // Init installer context
397 m_installerContext.widgetSource = widgetSource;
398 m_installerContext.tempWidgetPath = std::string();
400 // setup config xml path
402 LogInfo("widgetSource " << widgetSource);
403 size_t slash = widgetSource.find_last_of("\\/");
404 if (std::string::npos != slash) {
405 m_installerContext.tempWidgetPath = widgetSource.substr(0, slash+1);
407 m_installerContext.tempWidgetPath = ".";
411 m_installerContext.installStep = InstallerContext::INSTALL_START;
412 m_installerContext.job = this;
413 m_installerContext.existingWidgetInfo = update.existingWidgetInfo;
414 m_installerContext.widgetConfig.shareHref = std::string();
416 if (m_installerContext.existingWidgetInfo.isExist) {
417 return canProceed ? ConfigureResult::Updated : ConfigureResult::Failed;
421 return canProceed ? ConfigureResult::Ok : ConfigureResult::Failed;
424 WidgetUpdateMode::Type JobWidgetInstall::CalcWidgetUpdatePolicy(
425 const OptionalWidgetVersion &existingVersion,
426 const OptionalWidgetVersion &incomingVersion) const
428 // Widget is installed, check versions
429 if (!existingVersion && !incomingVersion) {
430 return WidgetUpdateMode::ExistingVersionEqual;
431 } else if (!existingVersion && !!incomingVersion) {
432 return WidgetUpdateMode::ExistingVersionNewer;
433 } else if (!!existingVersion && !incomingVersion) {
434 return WidgetUpdateMode::ExistingVersionOlder;
436 LogInfo("Existing widget: version = '" << *existingVersion << "'");
438 if (!existingVersion->IsWac() && !incomingVersion->IsWac()) {
439 return WidgetUpdateMode::BothVersionsNotStd;
440 } else if (!existingVersion->IsWac()) {
441 return WidgetUpdateMode::ExistingVersionNotStd;
442 } else if (!incomingVersion->IsWac()) {
443 return WidgetUpdateMode::IncomingVersionNotStd;
445 // Both versions are WAC-comparable. Do compare.
446 if (*incomingVersion == *existingVersion) {
447 return WidgetUpdateMode::ExistingVersionEqual;
448 } else if (*incomingVersion > *existingVersion) {
449 return WidgetUpdateMode::ExistingVersionOlder;
451 return WidgetUpdateMode::ExistingVersionNewer;
457 ConfigParserData JobWidgetInstall::getWidgetDataFromXML(
458 const std::string &widgetSource,
459 bool fromBrowser, WrtDB::PkgType isOspsvc)
465 ConfigParserData configInfo;
468 parser.Parse(widgetSource,
470 new RootParser<WidgetParser>(configInfo,
471 DPL::FromUTF32String(
476 DPL::ScopedPtr<DPL::ZipInput> zipFile(
477 new DPL::ZipInput(widgetSource));
479 DPL::ScopedPtr<DPL::ZipInput::File> configFile;
481 // Open config.xml file
482 if (isOspsvc == PKG_TYPE_TIZEN_WITHSVCAPP) {
483 configFile.Reset(zipFile->OpenFile(WITH_OSP_XML));
485 configFile.Reset(zipFile->OpenFile(CONFIG_XML));
489 DPL::BinaryQueue buffer;
490 DPL::AbstractWaitableInputAdapter inputAdapter(configFile.Get());
491 DPL::AbstractWaitableOutputAdapter outputAdapter(&buffer);
492 DPL::Copy(&inputAdapter, &outputAdapter);
493 parser.Parse(&buffer,
495 new RootParser<WidgetParser>(configInfo,
496 DPL::FromUTF32String(
502 Catch(DPL::ZipInput::Exception::OpenFailed)
504 LogDebug("Failed to open widget package");
505 return ConfigParserData();
507 Catch(DPL::ZipInput::Exception::OpenFileFailed)
509 LogDebug("Failed to open config.xml file");
510 return ConfigParserData();
512 Catch(DPL::CopyFailed)
514 LogDebug("Failed to extract config.xml file");
515 return ConfigParserData();
517 Catch(ElementParser::Exception::ParseError)
519 LogDebug("Failed to parse config.xml file");
520 return ConfigParserData();
524 WidgetUpdateInfo JobWidgetInstall::detectWidgetUpdate(
525 const ConfigParserData &configInfo)
527 LogInfo("Checking up widget package for config.xml...");
529 DPL::OptionalString widgetGUID;
530 OptionalWidgetVersion widgetVersion;
533 widgetGUID = configInfo.widget_id;
535 if (widgetGUID.IsNull()) {
536 LogDebug("Installed widget has no GUID");
537 return WidgetUpdateInfo();
540 LogDebug("Installed widget GUID: " << *widgetGUID);
542 // Locate widget ID with this GUID
543 // Incoming widget version
544 if (!configInfo.version.IsNull()) {
546 DPL::Optional<WidgetVersion>(
547 WidgetVersion(*configInfo.version));
552 // Search widget handle by GUID
553 WidgetDAOReadOnly dao(widgetGUID);
554 return WidgetUpdateInfo(
557 WidgetUpdateInfo::ExistingWidgetInfo(
558 dao.getHandle(), dao.getVersion()));
560 Catch(WidgetDAOReadOnly::Exception::WidgetNotExist)
562 // GUID isn't installed
563 return WidgetUpdateInfo(
566 WidgetUpdateInfo::ExistingWidgetInfo());
570 void JobWidgetInstall::SendProgress()
572 using namespace PackageManager;
573 if (GetProgressFlag() != false) {
574 if (getInstallerStruct().progressCallback != NULL) {
575 // send progress signal of pkgmgr
576 std::ostringstream percent;
577 percent << static_cast<int>(GetProgressPercent());
578 PkgmgrSignalSingleton::Instance().sendSignal(
582 LogDebug("Call widget install progressCallbak");
583 getInstallerStruct().progressCallback(getInstallerStruct().userParam,
584 GetProgressPercent(),GetProgressDescription());
589 void JobWidgetInstall::SendFinishedSuccess()
591 using namespace PackageManager;
592 // TODO : sync should move to separate task.
595 // remove widget install information file
596 unlink(m_installerContext.installInfo.c_str());
599 JobWidgetInstall::displayWidgetInfo();
601 DPL::Optional<WidgetHandle> handle = getNewWidgetHandle();
603 // send signal of pkgmgr
604 PkgmgrSignalSingleton::Instance().sendSignal(
608 LogDebug("Call widget install successfinishedCallback");
609 getInstallerStruct().finishedCallback(getInstallerStruct().userParam,
610 !!handle ? *handle : WrtDB::INVALID_WIDGET_HANDLE, Exceptions::Success);
613 void JobWidgetInstall::SendFinishedFailure()
615 using namespace PackageManager;
616 // remove widget install information file
617 unlink(m_installerContext.installInfo.c_str());
619 LogError("Error in installation step: " << m_exceptionCaught);
620 LogError("Message: " << m_exceptionMessage);
621 DPL::Optional<WidgetHandle> handle = getNewWidgetHandle();
623 LogDebug("Call widget install failure finishedCallback");
625 // send signal of pkgmgr
626 PkgmgrSignalSingleton::Instance().sendSignal(
630 getInstallerStruct().finishedCallback(getInstallerStruct().userParam,
631 !!handle ? *handle : WrtDB::INVALID_WIDGET_HANDLE, m_exceptionCaught);
634 void JobWidgetInstall::SaveExceptionData(const Jobs::JobExceptionBase &e)
636 m_exceptionCaught = static_cast<Exceptions::Type>(e.getParam());
637 m_exceptionMessage = e.GetMessage();
640 void JobWidgetInstall::displayWidgetInfo()
642 DPL::Optional<WidgetHandle> handle = getNewWidgetHandle();
645 WidgetDAOReadOnly dao(*handle);
647 std::ostringstream out;
648 WidgetLocalizedInfo localizedInfo =
649 W3CFileLocalization::getLocalizedInfo(*handle);
652 "===================================== INSTALLED WIDGET INFO ========="\
653 "============================";
654 out << std::endl << "Name: " << localizedInfo.name;
655 out << std::endl << "PkgName: " << dao.getPkgname();
656 WidgetSize size = dao.getPreferredSize();
657 out << std::endl << "Width: " << size.width;
658 out << std::endl << "Height: " << size.height;
659 out << std::endl << "Start File: " <<
660 W3CFileLocalization::getStartFile(*handle);
661 out << std::endl << "Version: " << dao.getVersion();
662 out << std::endl << "Licence: " <<
663 localizedInfo.license;
664 out << std::endl << "Licence Href: " <<
665 localizedInfo.licenseHref;
666 out << std::endl << "Description: " <<
667 localizedInfo.description;
668 out << std::endl << "Widget Id: " << dao.getGUID();
669 out << std::endl << "Widget recognized: " << dao.isRecognized();
670 out << std::endl << "Widget wac signed: " << dao.isWacSigned();
671 out << std::endl << "Widget distributor signed: " <<
672 dao.isDistributorSigned();
673 out << std::endl << "Widget trusted: " << dao.isTrusted();
675 OptionalWidgetIcon icon = W3CFileLocalization::getIcon(*handle);
676 DPL::OptionalString iconSrc =
677 !!icon ? icon->src : DPL::OptionalString::Null;
678 out << std::endl << "Icon: " << iconSrc;
680 out << std::endl << "Preferences:";
682 PropertyDAOReadOnly::WidgetPreferenceList list = dao.getPropertyList();
685 out << std::endl << " Key: " <<
687 out << std::endl << " Readonly: " <<
692 out << std::endl << "Features:";
694 WidgetFeatureSet list = dao.getFeaturesList();
697 out << std::endl << " Name: " << it->name;
698 out << std::endl << " Required: " << it->required;
699 out << std::endl << " Params:";
708 WrtDB::PackagingType JobWidgetInstall::checkPackageType(
709 const std::string &widgetSource)
711 using namespace WrtDB;
713 PackagingType pType = PKG_TYPE_UNKNOWN;
714 DPL::ScopedPtr<DPL::ZipInput> zipFile;
719 zipFile.Reset(new DPL::ZipInput(widgetSource));
722 Catch(DPL::ZipInput::Exception::OpenFailed)
724 LogDebug("Failed to open widget package");
725 return PKG_TYPE_UNKNOWN;
730 // Open config.xml file in package root
731 DPL::ScopedPtr<DPL::ZipInput::File> configFile(
732 zipFile->OpenFile(CONFIG_XML));
733 pType = PKG_TYPE_TIZEN_WEBAPP;
735 Catch(DPL::ZipInput::Exception::OpenFileFailed)
737 LogDebug("Could not find config.xml");
742 // Open config.xml file in package root
743 DPL::ScopedPtr<DPL::ZipInput::File> configFile(
744 zipFile->OpenFile(WITH_OSP_XML));
745 if (pType == PKG_TYPE_TIZEN_WEBAPP) {
746 return PKG_TYPE_UNKNOWN;
749 pType = PKG_TYPE_TIZEN_WITHSVCAPP;
751 Catch(DPL::ZipInput::Exception::OpenFileFailed)
753 LogDebug("Could not find wgt/config.xml");
754 return PKG_TYPE_UNKNOWN;
760 } //namespace WidgetInstall