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