Fix in StepWgtPatchStorageDirectory
[platform/core/appfw/wgt-backend.git] / src / hybrid / step / pkgmgr / step_generate_xml.cc
1 // Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
2 // Use of this source code is governed by an apache-2.0 license that can be
3 // found in the LICENSE file.
4
5 #include "hybrid/step/pkgmgr/step_generate_xml.h"
6
7 #include <boost/filesystem/operations.hpp>
8 #include <boost/filesystem/path.hpp>
9 #include <boost/system/error_code.hpp>
10
11 #include <libxml/parser.h>
12 #include <libxml/tree.h>
13 #include <libxml/xpath.h>
14 #include <pkgmgr_parser.h>
15
16 #include <common/utils/file_util.h>
17 #include <common/utils/glist_range.h>
18
19 #include <string>
20 #include <vector>
21
22 #include "hybrid/hybrid_backend_data.h"
23
24 namespace ci = common_installer;
25 namespace bf = boost::filesystem;
26 namespace bs = boost::system;
27
28 namespace {
29
30 const std::vector<std::string> kBlackListNodes = {
31   {"author"},
32   {"description"},
33   {"profile"},
34   {"privileges"},
35 };
36 const std::vector<std::string> kNeedMergeNodes = {
37   {"manifest"},
38 };
39 const std::vector<std::pair<std::string, std::string>> kApplicationNodeNames = {
40   {"ui-application", "uiapp"},
41   {"service-application", "svcapp"},
42   {"widget-application", "widgetapp"},
43   {"watch-application", "watchapp"},
44 };
45
46 }  // namespace
47
48 namespace hybrid {
49 namespace pkgmgr {
50
51 bool StepGenerateXml::LoadXmlDocument(const bf::path& wgt_xml_path,
52     const bf::path& tpk_xml_path) {
53   // trim blanks
54   int keep_blanks = xmlKeepBlanksDefault(0);
55   wgt_doc_ = xmlParseFile(wgt_xml_path.string().c_str());
56   if (!wgt_doc_) {
57     LOG(ERROR) << "Failed to parse file: " << wgt_xml_path;
58     xmlKeepBlanksDefault(keep_blanks);
59     return false;
60   }
61   tpk_doc_ = xmlParseFile(tpk_xml_path.string().c_str());
62   if (!tpk_doc_) {
63     LOG(ERROR) << "Failed to parse file: " << tpk_xml_path;
64     xmlKeepBlanksDefault(keep_blanks);
65     return false;
66   }
67   xmlKeepBlanksDefault(keep_blanks);
68   return true;
69 }
70
71 xmlNodePtr StepGenerateXml::GetXmlNode(const xmlDocPtr doc,
72     const std::string& name, const std::string& attr,
73     const std::string& attr_val) {
74   xmlXPathContextPtr ctxt = xmlXPathNewContext(doc);
75   if (!ctxt) {
76     LOG(ERROR) << "Failed to create XPath context";
77     return nullptr;
78   }
79
80   std::string expr = "//*[local-name()='" + name + "']";
81   if (!attr.empty() && !attr_val.empty())
82     expr.append("[@" + attr + "='" + attr_val + "']");
83
84   xmlXPathObjectPtr obj =
85       xmlXPathEvalExpression((const xmlChar*)expr.c_str(), ctxt);
86   if (!obj || !obj->nodesetval || !obj->nodesetval->nodeNr) {
87     xmlXPathFreeContext(ctxt);
88     return nullptr;
89   }
90
91   xmlNodePtr node = obj->nodesetval->nodeTab[0];
92   xmlXPathFreeObject(obj);
93   xmlXPathFreeContext(ctxt);
94
95   return node;
96 }
97
98 void StepGenerateXml::MergeXmlNode(xmlNodePtr node1, xmlNodePtr node2) {
99   xmlNodePtr last = xmlGetLastChild(node1);
100   xmlNodePtr next;
101   // merge node2's child into node1
102   for (xmlNodePtr cur = node2->children; cur; cur = next) {
103     next = cur->next;
104     if (std::find(kBlackListNodes.begin(), kBlackListNodes.end(),
105         reinterpret_cast<const char*>(cur->name)) != kBlackListNodes.end())
106       continue;
107     // to avoid duplicate
108     if (std::find(kNeedMergeNodes.begin(), kNeedMergeNodes.end(),
109         reinterpret_cast<const char*>(cur->name)) != kNeedMergeNodes.end())
110       continue;
111     xmlAddNextSibling(last, cur);
112     last = last->next;
113   }
114 }
115
116 void StepGenerateXml::SetXmlNodeAttribute(xmlNodePtr node,
117     const std::string& attr, const std::string& attr_val) {
118   xmlSetProp(node, reinterpret_cast<const xmlChar*>(attr.c_str()),
119       reinterpret_cast<const xmlChar*>(attr_val.c_str()));
120 }
121
122 ci::Step::Status StepGenerateXml::process() {
123   bf::path wgt_xml_path = context_->xml_path.get();
124   bf::path tpk_xml_path = context_->pkg_path.get() / "tizen-manifest.xml";
125
126   if (!LoadXmlDocument(wgt_xml_path, tpk_xml_path))
127     return Step::Status::MANIFEST_ERROR;
128
129   for (auto& entry : kNeedMergeNodes) {
130     xmlNodePtr tpk_node = GetXmlNode(tpk_doc_, entry);
131     if (tpk_node == nullptr)
132       continue;
133     // TODO(s89.jang): If cannot find node from wgt doc, merge tpk node itself
134     // instead of merging its child nodes.
135     xmlNodePtr wgt_node = GetXmlNode(wgt_doc_, entry);
136     MergeXmlNode(wgt_node, tpk_node);
137   }
138
139   // set app's executable path
140   HybridBackendData* data =
141       static_cast<HybridBackendData*>(context_->backend_data.get());
142   manifest_x* tpk_data = data->tpk_manifest_data.get();
143   for (application_x* app :
144       GListRange<application_x*>(tpk_data->application)) {
145     auto r = std::find_if(kApplicationNodeNames.begin(),
146         kApplicationNodeNames.end(),
147         [app](const std::pair<std::string, std::string>& e) {
148           return strcmp(e.second.c_str(), app->component_type) == 0;
149         });
150     if (r == kApplicationNodeNames.end()) {
151       LOG(WARNING) << "Cannot find component type!";
152       continue;
153     }
154     xmlNodePtr node = GetXmlNode(wgt_doc_, (*r).first, "appid", app->appid);
155     SetXmlNodeAttribute(node, "exec", app->exec);
156   }
157
158   if (xmlSaveFormatFile(wgt_xml_path.string().c_str(), wgt_doc_, 1) == -1) {
159     LOG(ERROR) << "Cannot write xml file";
160     return Step::Status::MANIFEST_ERROR;
161   }
162
163   if (pkgmgr_parser_check_manifest_validation(
164       wgt_xml_path.string().c_str()) != 0) {
165     LOG(ERROR) << "Merged manifest is not valid";
166     return Step::Status::MANIFEST_ERROR;
167   }
168
169   return Status::OK;
170 }
171
172 ci::Step::Status StepGenerateXml::precheck() {
173   bf::path wgt_xml_path = context_->xml_path.get();
174   if (!bf::exists(wgt_xml_path)) {
175     LOG(ERROR) << "Converted config file not found: " << wgt_xml_path;
176     return Step::Status::MANIFEST_ERROR;
177   }
178
179   bf::path tpk_xml_path = context_->pkg_path.get() / "tizen-manifest.xml";
180   if (!bf::exists(tpk_xml_path)) {
181     LOG(ERROR) << "Native manifest file not found: " << tpk_xml_path;
182     return Step::Status::MANIFEST_ERROR;
183   }
184
185   return Status::OK;
186 }
187
188 }  // namespace pkgmgr
189 }  // namespace hybrid