upload tizen1.0 source
[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 <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>
29 #include <dpl/copy.h>
30 #include <dpl/assert.h>
31 #include <dpl/sstream.h>
32 #include "root_parser.h"
33 #include "widget_parser.h"
34 #include "parser_runner.h"
35 #include <widget_install/job_widget_install.h>
36 #include <widget_install/task_unzip.h>
37 #include <widget_install/task_certify.h>
38 #include <widget_install/task_widget_config.h>
39 #include <widget_install/task_db_update.h>
40 #include <widget_install/task_ace_check.h>
41 #include <widget_install/task_smack.h>
42 #include <widget_install/task_desktop_file.h>
43 #include <widget_install/task_private_storage.h>
44 #include <widget_install/task_prepare_files.h>
45 #include <widget_install/widget_install_errors.h>
46 #include <widget_install/widget_install_context.h>
47 #include <string>
48 #include <dpl/wrt-dao-rw/widget_dao.h> //TODO remove
49 #include <dpl/wrt-dao-ro/widget_dao_read_only.h>
50 #include <dpl/wrt-dao-rw/global_dao.h> // TODO remove
51 #include <aul.h>
52 #include <dpl/localization/w3c_file_localization.h>
53 #include <libiriwrapper.h>
54
55 using namespace WrtDB;
56
57 namespace // anonymous
58 {
59 const char * const CONFIG_XML = "config.xml";
60
61 class InstallerTaskFail :
62     public DPL::TaskDecl<InstallerTaskFail>
63 {
64   private:
65     bool m_deferred;
66
67     void StepFail()
68     {
69         if (m_deferred) {
70             ThrowMsg(Jobs::WidgetInstall::Exceptions::Deferred,
71                      "Widget installation or update deferred!");
72         } else {
73             ThrowMsg(Jobs::WidgetInstall::Exceptions::NotAllowed,
74                      "Widget installation or update not allowed!");
75         }
76     }
77
78   public:
79     InstallerTaskFail(bool deferred) :
80         DPL::TaskDecl<InstallerTaskFail>(this),
81         m_deferred(deferred)
82     {
83         AddStep(&InstallerTaskFail::StepFail);
84     }
85 };
86 } // namespace anonymous
87
88 namespace Jobs {
89 namespace WidgetInstall {
90 JobWidgetInstall::JobWidgetInstall(std::string const &widgetPath,
91         const WidgetInstallationStruct &installerStruct) :
92     Job(Installation),
93     JobContextBase<WidgetInstallationStruct>(installerStruct),
94     m_exceptionCaught(Exceptions::Success)
95 {
96     // Configure installation
97     ConfigureResult result = ConfigureInstallation(widgetPath, false);
98
99     if (result == ConfigureResult::Ok) {
100         LogInfo("Configure installation succeeded");
101
102         // Create installation tasks
103         AddTask(new TaskUnzip(m_installerContext));
104         AddTask(new TaskWidgetConfig(m_installerContext));
105         AddTask(new TaskCertify(m_installerContext));
106         AddTask(new TaskDbUpdate(m_installerContext));
107         // TODO: Update progress information for this task
108
109         AddTask(new TaskAceCheck(m_installerContext));
110         //This is sort of quick solution, because ACE verdicts are based upon
111         //data from DAO (DB). So AceCheck for now has to be AFTER DbUpdate
112         //task.
113         AddTask(new TaskSmack(m_installerContext));
114
115         AddTask(new TaskDesktopFile(m_installerContext));
116         AddTask(new TaskPrivateStorage(m_installerContext));
117     } else if (result == ConfigureResult::Deferred) {
118         // Installation is deferred
119         LogInfo("Configure installation deferred");
120
121         AddTask(new InstallerTaskFail(true));
122     } else if (result == ConfigureResult::Failed) {
123         // Installation is not allowed to proceed due to widget update policy
124         LogWarning("Configure installation failed!");
125
126         AddTask(new InstallerTaskFail(false));
127     } else {
128         Assert(false && "Invalid configure result!");
129     }
130 }
131
132 JobWidgetInstall::JobWidgetInstall(
133         std::string const & widgetUrl,
134         std::string const & iconPath,
135         const WidgetInstallationStruct &installerStruct) :
136     Job(Installation),
137     JobContextBase<WidgetInstallationStruct>(installerStruct),
138     m_exceptionCaught(Exceptions::Success)
139 {
140     // Configure installation
141     ConfigureResult result = ConfigureInstallation(widgetUrl, true);
142
143     if (result == ConfigureResult::Ok) {
144         LogInfo("Configure installation succeeded");
145
146         // Check web app url
147         LibIri::Wrapper iri(widgetUrl.c_str());
148         if (!iri.Validate()) {
149             ThrowMsg(Exceptions::InvalidWidgetUrl,
150                      "Web app url must be a valid iri/uri/url");
151         }
152
153         m_installerContext.widgetConfig.configInfo.startFile =
154                 DPL::FromUTF8String(widgetUrl);
155
156         m_installerContext.iconPath = iconPath;
157
158         // Create installation tasks
159         AddTask(new TaskPrepareFiles(m_installerContext));
160         AddTask(new TaskWidgetConfig(m_installerContext));
161         AddTask(new TaskCertify(m_installerContext));
162         AddTask(new TaskDbUpdate(m_installerContext));
163         // TODO: Update progress information for this task
164
165         AddTask(new TaskAceCheck(m_installerContext));
166         //This is sort of quick solution, because ACE verdicts are based upon
167         //data from DAO (DB). So AceCheck for now has to be AFTER DbUpdate
168         //task.
169         AddTask(new TaskSmack(m_installerContext));
170
171         AddTask(new TaskDesktopFile(m_installerContext));
172         AddTask(new TaskPrivateStorage(m_installerContext));
173     } else if (result == ConfigureResult::Deferred) {
174         // Installation is deferred
175         LogInfo("Configure installation deferred");
176
177         AddTask(new InstallerTaskFail(true));
178     } else if (result == ConfigureResult::Failed) {
179         // Installation is not allowed to proceed due to widget update policy
180         LogWarning("Configure installation failed!");
181
182         AddTask(new InstallerTaskFail(false));
183     } else {
184         Assert(false && "Invalid configure result!");
185     }
186 }
187
188 DPL::Optional<WidgetHandle> JobWidgetInstall::getNewWidgetHandle() const
189 {
190     return m_installerContext.widgetHandle;
191 }
192
193 JobWidgetInstall::ConfigureResult JobWidgetInstall::ConfigureInstallation(
194         const std::string &widgetSource, bool fromBrowser)
195 {
196     // Detect widget update
197     WidgetUpdateInfo update = detectWidgetUpdate(widgetSource, fromBrowser);
198
199     LogInfo(
200         "Widget install/update: incoming guid = '" <<
201         update.incomingGUID << "'");
202     LogInfo(
203         "Widget install/update: incoming version = '" <<
204         update.incomingVersion << "'");
205
206     // Check policy
207     WidgetUpdateMode::Type updateTypeCheckBit;
208
209     if (update.existingWidgetInfo.isExist == false) {
210         LogInfo("Widget info does not exist");
211         updateTypeCheckBit = WidgetUpdateMode::NotInstalled;
212     } else {
213         LogInfo("Widget info exists. Handle: " <<
214                 update.existingWidgetInfo.existingHandle);
215
216         DPL::OStringStream pkgName;
217         DPL::OptionalString pkgname =
218             WidgetDAOReadOnly(update.existingWidgetInfo.existingHandle).getPkgname();
219
220         if(pkgname.IsNull()) {
221             LogInfo("But widget package name doesn't exist");
222             return ConfigureResult::Failed;
223         }
224
225         LogInfo("Widget model exists. Package name: " << pkgName);
226         if (aul_app_is_running(DPL::ToUTF8String(*pkgname).c_str())) {
227             // Must be deferred when update in progress
228             if (m_jobStruct.updateMode == WidgetUpdateMode::PolicyWac) {
229                 LogInfo(
230                     "Widget is already running. Policy is update according to WAC");
231                 LogInfo("Installation deferred: " << widgetSource);
232
233                 GlobalDAO::AddDefferedWidgetPackageInstallation(
234                     DPL::FromUTF8String(widgetSource));
235
236                 return ConfigureResult::Deferred;
237             } else {
238                 LogInfo(
239                     "Widget is already running. Policy is not update according to WAC");
240                 LogInfo("Installation aborted: " << widgetSource);
241
242                 return ConfigureResult::Failed;
243             }
244         }
245
246         OptionalWidgetVersion existingVersion;
247         existingVersion = update.existingWidgetInfo.existingVersion;
248         OptionalWidgetVersion incomingVersion = update.incomingVersion;
249
250         updateTypeCheckBit = CalcWidgetUpdatePolicy(existingVersion,
251                                                     incomingVersion);
252     }
253
254     // Calc proceed flag
255     bool canProceed = (m_jobStruct.updateMode & updateTypeCheckBit) > 0;
256
257     LogInfo("Whether widget policy allow proceed: " << canProceed);
258
259     // Init installer context
260     m_installerContext.widgetSource = widgetSource;
261     m_installerContext.tempWidgetPath = std::string();
262     m_installerContext.widgetConfig = WidgetRegisterInfo();
263     m_installerContext.installStep = InstallerContext::INSTALL_START;
264     m_installerContext.job = this;
265     m_installerContext.existingWidgetInfo = update.existingWidgetInfo;
266     m_installerContext.widgetConfig.shareHref = std::string();
267
268     // Return result
269     return canProceed ? ConfigureResult::Ok : ConfigureResult::Failed;
270 }
271
272 WidgetUpdateMode::Type JobWidgetInstall::CalcWidgetUpdatePolicy(
273         const OptionalWidgetVersion &existingVersion,
274         const OptionalWidgetVersion &incomingVersion) const
275 {
276     // Widget is installed, check versions
277     if (!existingVersion && !incomingVersion) {
278         return WidgetUpdateMode::ExistingVersionEqual;
279     } else if (!existingVersion && !!incomingVersion) {
280         return WidgetUpdateMode::ExistingVersionNewer;
281     } else if (!!existingVersion && !incomingVersion) {
282         return WidgetUpdateMode::ExistingVersionOlder;
283     } else {
284         LogInfo("Existing widget: version = '" << *existingVersion << "'");
285
286         if (!existingVersion->IsWac() && !incomingVersion->IsWac()) {
287             return WidgetUpdateMode::BothVersionsNotStd;
288         } else if (!existingVersion->IsWac()) {
289             return WidgetUpdateMode::ExistingVersionNotStd;
290         } else if (!incomingVersion->IsWac()) {
291             return WidgetUpdateMode::IncomingVersionNotStd;
292         } else {
293             // Both versions are WAC-comparable. Do compare.
294             if (*incomingVersion == *existingVersion) {
295                 return WidgetUpdateMode::ExistingVersionEqual;
296             } else if (*incomingVersion > *existingVersion) {
297                 return WidgetUpdateMode::ExistingVersionOlder;
298             } else {
299                 return WidgetUpdateMode::ExistingVersionNewer;
300             }
301         }
302     }
303 }
304
305 WidgetUpdateInfo JobWidgetInstall::detectWidgetUpdate(
306         const std::string &widgetSource, bool fromBrowser)
307 {
308     LogInfo("Checking up widget package for config.xml...");
309
310     Try
311     {
312         DPL::OptionalString widgetGUID;
313         OptionalWidgetVersion widgetVersion;
314         if (fromBrowser) {
315             widgetGUID = DPL::FromUTF8String(widgetSource);
316         }
317         else {
318             // Open zip file
319             DPL::ScopedPtr<DPL::ZipInput> zipFile(
320                 new DPL::ZipInput(widgetSource));
321
322             // Open config.xml file
323             DPL::ScopedPtr<DPL::ZipInput::File> configFile(
324                 zipFile->OpenFile(CONFIG_XML));
325
326             // Extract config
327             DPL::BinaryQueue buffer;
328             DPL::AbstractWaitableInputAdapter inputAdapter(configFile.Get());
329             DPL::AbstractWaitableOutputAdapter outputAdapter(&buffer);
330             DPL::Copy(&inputAdapter, &outputAdapter);
331
332             // Parse config
333             ParserRunner parser;
334             ConfigParserData configInfo;
335
336             parser.Parse(&buffer,
337                          ElementParserPtr(
338                              new RootParser<WidgetParser>(configInfo,
339                                                           DPL::FromUTF32String(
340                                                               L"widget"))));
341
342             // Check widget id
343             widgetGUID = configInfo.widget_id;
344
345             if (widgetGUID.IsNull()) {
346                 LogDebug("Installed widget has no GUID");
347                 return WidgetUpdateInfo();
348             }
349
350             LogDebug("Installed widget GUID: " << *widgetGUID);
351
352             // Locate widget ID with this GUID
353             // Incoming widget version
354             if (!configInfo.version.IsNull()) {
355                 widgetVersion =
356                     DPL::Optional<WidgetVersion>(
357                         WidgetVersion(*configInfo.version));
358             }
359         }
360
361         try
362         {
363             // Search widget handle by GUID
364             WidgetDAO dao(widgetGUID);
365             return WidgetUpdateInfo(
366                 widgetGUID,
367                 widgetVersion,
368                 WidgetUpdateInfo::ExistingWidgetInfo(
369                     dao.getHandle(), dao.getVersion()));
370         }
371         Catch(WidgetDAOReadOnly::Exception::WidgetNotExist){
372             // GUID isn't installed
373             return WidgetUpdateInfo(
374                 widgetGUID,
375                 widgetVersion,
376                 WidgetUpdateInfo::ExistingWidgetInfo());
377         }
378     }
379     Catch(DPL::ZipInput::Exception::OpenFailed)
380     {
381         LogDebug("Failed to open widget package");
382         return WidgetUpdateInfo();
383     }
384     Catch(DPL::ZipInput::Exception::OpenFileFailed)
385     {
386         LogDebug("Failed to open config.xml file");
387         return WidgetUpdateInfo();
388     }
389     Catch(DPL::CopyFailed)
390     {
391         LogDebug("Failed to extract config.xml file");
392         return WidgetUpdateInfo();
393     }
394     Catch(ElementParser::Exception::ParseError)
395     {
396         LogDebug("Failed to parse config.xml file");
397         return WidgetUpdateInfo();
398     }
399 }
400
401 void JobWidgetInstall::SendProgress()
402 {
403     if (GetProgressFlag() != false) {
404         if (getInstallerStruct().progressCallback != NULL) {
405
406             LogDebug("Call widget install progressCallbak");
407             getInstallerStruct().progressCallback(getInstallerStruct().userParam,
408                     GetProgressPercent(),GetProgressDescription());
409         }
410     }
411 }
412
413 void JobWidgetInstall::SendFinishedSuccess()
414 {
415     //inform widget info
416     JobWidgetInstall::displayWidgetInfo();
417
418     DPL::Optional<WidgetHandle> handle = getNewWidgetHandle();
419     const WidgetHandle INVALID_WIDGET_HANDLE = 0;
420
421     LogDebug("Call widget install successfinishedCallback");
422     getInstallerStruct().finishedCallback(getInstallerStruct().userParam,
423             !!handle ? *handle : INVALID_WIDGET_HANDLE, Exceptions::Success);
424 }
425
426 void JobWidgetInstall::SendFinishedFailure()
427 {
428     LogError("Error in installation step: " << m_exceptionCaught);
429     LogError("Message: " << m_exceptionMessage);
430     DPL::Optional<WidgetHandle> handle = getNewWidgetHandle();
431     const WidgetHandle INVALID_WIDGET_HANDLE = 0;
432
433     LogDebug("Call widget install failure finishedCallback");
434     getInstallerStruct().finishedCallback(getInstallerStruct().userParam,
435             !!handle ? *handle : INVALID_WIDGET_HANDLE, m_exceptionCaught);
436 }
437
438 void JobWidgetInstall::SaveExceptionData(const Jobs::JobExceptionBase &e)
439 {
440     m_exceptionCaught = static_cast<Exceptions::Type>(e.getParam());
441     m_exceptionMessage = e.GetMessage();
442 }
443
444 void JobWidgetInstall::displayWidgetInfo()
445 {
446     DPL::Optional<WidgetHandle> handle = getNewWidgetHandle();
447     Assert(!!handle);
448
449     WidgetDAOReadOnly dao(*handle);
450
451     std::ostringstream out;
452     WidgetLocalizedInfo localizedInfo =
453         W3CFileLocalization::getLocalizedInfo(*handle);
454
455     out << std::endl <<
456         "===================================== INSTALLED WIDGET INFO ========="\
457         "============================";
458     out << std::endl << "Name:                        " << localizedInfo.name;
459     WidgetSize size = dao.getPreferredSize();
460     out << std::endl << "Width:                       " << size.width;
461     out << std::endl << "Height:                      " << size.height;
462     out << std::endl << "Start File:                  " <<
463         W3CFileLocalization::getStartFile(*handle);
464     out << std::endl << "Version:                     " << dao.getVersion();
465     out << std::endl << "Licence:                     " <<
466         localizedInfo.license;
467     out << std::endl << "Licence Href:                " <<
468         localizedInfo.licenseHref;
469     out << std::endl << "Description:                 " <<
470         localizedInfo.description;
471     out << std::endl << "Widget Id:                   " << dao.getGUID();
472     out << std::endl << "Widget recognized:           " << dao.isRecognized();
473     out << std::endl << "Widget wac signed:           " << dao.isWacSigned();
474     out << std::endl << "Widget distributor signed:   " <<
475         dao.isDistributorSigned();
476     out << std::endl << "Widget trusted:              " << dao.isTrusted();
477
478     OptionalWidgetIcon icon = W3CFileLocalization::getIcon(*handle);
479     DPL::OptionalString iconSrc =
480         !!icon ? icon->src : DPL::OptionalString::Null;
481     out << std::endl << "Icon:                        " << iconSrc;
482
483     out << std::endl << "Preferences:";
484     {
485         PropertyDAOReadOnly::WidgetPreferenceList list = dao.getPropertyList();
486         FOREACH(it, list)
487         {
488             out << std::endl << "  Key:                       " <<
489                 it->key_name;
490             out << std::endl << "      Readonly:              " <<
491                 it->readonly;
492         }
493     }
494
495     out << std::endl << "Features:";
496     {
497         WidgetFeatureSet list = dao.getFeaturesList();
498         FOREACH(it, list)
499         {
500             out << std::endl << "  Name:                      " << it->name;
501             out << std::endl << "      Required:              " << it->required;
502             out << std::endl << "      Params:";
503         }
504     }
505
506     out << std::endl;
507
508     LogInfo(out.str());
509 }
510
511 } //namespace WidgetInstall
512 } //namespace Jobs