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