Upstream version 7.35.138.0
[platform/framework/web/crosswalk.git] / src / xwalk / application / browser / installer / tizen / package_installer.cc
1 // Copyright (c) 2014 Intel Corporation. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "xwalk/application/browser/installer/tizen/package_installer.h"
6
7 #include <sys/types.h>
8 #include <pwd.h>
9 #include <unistd.h>
10 #include <pkgmgr/pkgmgr_parser.h>
11 #include <algorithm>
12 #include <map>
13 #include <string>
14 #include "base/file_util.h"
15 #include "base/files/file_enumerator.h"
16 #include "base/logging.h"
17 #include "base/path_service.h"
18 #include "base/command_line.h"
19 #include "base/process/launch.h"
20 #include "third_party/libxml/chromium/libxml_utils.h"
21 #include "xwalk/application/common/application_data.h"
22 #include "xwalk/application/common/application_manifest_constants.h"
23 #include "xwalk/application/common/manifest_handlers/tizen_metadata_handler.h"
24 #include "xwalk/application/browser/application_storage.h"
25 #include "xwalk/application/browser/installer/tizen/packageinfo_constants.h"
26 #include "xwalk/runtime/browser/xwalk_runner.h"
27
28 namespace info = xwalk::application_packageinfo_constants;
29
30 namespace {
31
32 namespace widget_keys = xwalk::application_widget_keys;
33
34 const base::FilePath kPkgHelper("/usr/bin/xwalk-pkg-helper");
35
36 const base::FilePath kXWalkLauncherBinary("/usr/bin/xwalk-launcher");
37
38 const base::FilePath kDefaultIcon(
39     "/usr/share/icons/default/small/crosswalk.png");
40
41 const std::string kServicePrefix("xwalk-service.");
42 const std::string kAppIdPrefix("xwalk.");
43
44 class FileDeleter {
45  public:
46   FileDeleter(const base::FilePath& path, bool recursive)
47       : path_(path),
48         recursive_(recursive) {}
49
50   ~FileDeleter() {
51     if (path_.empty())
52       return;
53
54     base::DeleteFile(path_, recursive_);
55   }
56
57   void Dismiss() {
58     path_.clear();
59   }
60
61  private:
62   base::FilePath path_;
63   bool recursive_;
64 };
65
66 void WriteMetaDataElement(
67     XmlWriter& writer, // NOLINT
68     xwalk::application::TizenMetaDataInfo* info) {
69   if (!info)
70     return;
71
72   const std::map<std::string, std::string>& metadata = info->metadata();
73   std::map<std::string, std::string>::const_iterator it;
74   for (it = metadata.begin(); it != metadata.end(); ++it) {
75     writer.StartElement("metadata");
76     writer.AddAttribute("key", it->first);
77     writer.AddAttribute("value", it->second);
78     writer.EndElement();
79   }
80 }
81
82 bool GeneratePkgInfoXml(xwalk::application::ApplicationData* application,
83                         const std::string& icon_name,
84                         const base::FilePath& app_dir,
85                         const base::FilePath& xml_path) {
86   if (!base::PathExists(app_dir) &&
87       !base::CreateDirectory(app_dir))
88     return false;
89
90   std::string package_id = application->ID();
91   std::string tizen_app_id = kAppIdPrefix + package_id;
92   base::FilePath execute_path =
93       app_dir.AppendASCII("bin/").AppendASCII(tizen_app_id);
94   std::string stripped_name = application->Name();
95
96   FILE* file = base::OpenFile(xml_path, "w");
97
98   XmlWriter xml_writer;
99   xml_writer.StartWriting();
100   xml_writer.StartElement("manifest");
101   xml_writer.AddAttribute("xmlns", "http://tizen.org/ns/packages");
102   xml_writer.AddAttribute("package", package_id);
103   xml_writer.AddAttribute("version", application->VersionString());
104   xml_writer.WriteElement("label", application->Name());
105   xml_writer.WriteElement("description", application->Description());
106
107   xml_writer.StartElement("ui-application");
108   xml_writer.AddAttribute("appid", tizen_app_id);
109   xml_writer.AddAttribute("exec", execute_path.MaybeAsASCII());
110   xml_writer.AddAttribute("type", "c++app");
111   xml_writer.AddAttribute("taskmanage", "true");
112   xml_writer.WriteElement("label", application->Name());
113
114   xwalk::application::TizenMetaDataInfo* info =
115       static_cast<xwalk::application::TizenMetaDataInfo*>(
116       application->GetManifestData(widget_keys::kTizenMetaDataKey));
117   WriteMetaDataElement(xml_writer, info);
118
119   if (icon_name.empty())
120     xml_writer.WriteElement("icon", info::kDefaultIconName);
121   else
122     xml_writer.WriteElement("icon", kServicePrefix + package_id + ".png");
123   xml_writer.EndElement();  // Ends "ui-application"
124
125   xml_writer.EndElement();  // Ends "manifest" element.
126   xml_writer.StopWriting();
127
128   base::WriteFile(xml_path,
129                   xml_writer.GetWrittenString().c_str(),
130                   xml_writer.GetWrittenString().size());
131
132   base::CloseFile(file);
133   LOG(INFO) << "Converting manifest.json into "
134             << xml_path.BaseName().MaybeAsASCII()
135             << " for installation. [DONE]";
136   return true;
137 }
138
139 bool CheckRunningMode() {
140   if (xwalk::XWalkRunner::GetInstance()->is_running_as_service())
141     return true;
142   LOG(ERROR) << "Package manipulation on Tizen is only possible in"
143              << "service mode using 'xwalkctl' utility.";
144   return false;
145 }
146
147 }  // namespace
148
149 namespace xwalk {
150 namespace application {
151
152 bool PackageInstaller::InstallApplication(
153     ApplicationData* application, const base::FilePath& data_dir) {
154   if (!CheckRunningMode())
155     return false;
156
157   std::string package_id = application->ID();
158   std::string tizen_app_id = kAppIdPrefix + package_id;
159   base::FilePath app_dir =
160       data_dir.AppendASCII(info::kAppDir).AppendASCII(package_id);
161   base::FilePath xml_path = data_dir.AppendASCII(info::kAppDir).AppendASCII(
162       package_id + std::string(info::kXmlExtension));
163
164   std::string icon_name;
165   if (!application->GetManifest()->GetString(info::kIconKey, &icon_name)) {
166     LOG(WARNING) << "'icon' not included in manifest";
167   }
168   // This will clean everything inside '<data dir>/<app id>'.
169   FileDeleter app_dir_cleaner(app_dir, true);
170
171   if (!GeneratePkgInfoXml(application, icon_name, app_dir, xml_path)) {
172     LOG(ERROR) << "Could not create XML metadata file '"
173                << xml_path.value() << "'.";
174     return false;
175   }
176
177   base::FilePath execute_path =
178       app_dir.AppendASCII("bin/").AppendASCII(tizen_app_id);
179
180   if (!base::CreateDirectory(execute_path.DirName())) {
181     LOG(ERROR) << "Could not create directory '"
182                << execute_path.DirName().value() << "'.";
183     return false;
184   }
185
186   if (!base::CreateSymbolicLink(kXWalkLauncherBinary, execute_path)) {
187     LOG(ERROR) << "Could not create symbolic link to launcher from '"
188                << execute_path.value() << "'.";
189     return false;
190   }
191
192   base::FilePath icon =
193       icon_name.empty() ? kDefaultIcon : app_dir.AppendASCII(icon_name);
194
195   CommandLine cmdline(kPkgHelper);
196   cmdline.AppendSwitch("--install");
197   cmdline.AppendArg(package_id);
198   cmdline.AppendArgPath(xml_path);
199   cmdline.AppendArgPath(icon);
200
201   int exit_code;
202   std::string output;
203
204   if (!base::GetAppOutputWithExitCode(cmdline, &output, &exit_code)) {
205     LOG(ERROR) << "Could not launch installer helper.";
206     return false;
207   }
208
209   if (exit_code != 0) {
210     LOG(ERROR) << "Could not install application: "
211                << output << " (" << exit_code << ")";
212     return false;
213   }
214
215   app_dir_cleaner.Dismiss();
216
217   return true;
218 }
219
220 bool PackageInstaller::UninstallApplication(
221     ApplicationData* application, const base::FilePath& data_dir) {
222   if (!CheckRunningMode())
223     return false;
224
225   std::string package_id = application->ID();
226   bool result = true;
227
228   CommandLine cmdline(kPkgHelper);
229   cmdline.AppendSwitch("--uninstall");
230   cmdline.AppendArg(package_id);
231
232   int exit_code;
233   std::string output;
234
235   if (!base::GetAppOutputWithExitCode(cmdline, &output, &exit_code)) {
236     LOG(ERROR) << "Could not launch installer helper";
237     result = false;
238   }
239
240   if (exit_code != 0) {
241     LOG(ERROR) << "Could not uninstall application: "
242                << output << " (" << exit_code << ")";
243     result = false;
244   }
245
246   base::FilePath app_dir =
247       data_dir.AppendASCII(info::kAppDir).AppendASCII(package_id);
248   if (!base::DeleteFile(app_dir, true)) {
249     LOG(ERROR) << "Could not remove directory '" << app_dir.value() << "'";
250     result = false;
251   }
252
253   base::FilePath xml_path = data_dir.AppendASCII(
254       package_id + std::string(info::kXmlExtension));
255   if (!base::DeleteFile(xml_path, false)) {
256     LOG(ERROR) << "Could not remove file '" << xml_path.value() << "'";
257     result = false;
258   }
259
260   return result;
261 }
262
263 bool PackageInstaller::UpdateApplication(
264     ApplicationData* new_application, const base::FilePath& data_dir) {
265   if (!CheckRunningMode())
266     return false;
267
268   std::string package_id = new_application->ID();
269   std::string tizen_app_id = kAppIdPrefix + package_id;
270   base::FilePath app_dir =
271       data_dir.AppendASCII(info::kAppDir).AppendASCII(package_id);
272   base::FilePath new_xml_path = data_dir.AppendASCII(info::kAppDir).AppendASCII(
273       package_id + ".new" + std::string(info::kXmlExtension));
274
275   std::string icon_name;
276   if (!new_application->GetManifest()->GetString(info::kIconKey, &icon_name)) {
277     LOG(WARNING) << "'icon' not included in manifest";
278   }
279   // This will clean everything inside '<data dir>/<app id>' and the new XML.
280   FileDeleter app_dir_cleaner(app_dir, true);
281   FileDeleter new_xml_cleaner(new_xml_path, true);
282
283   if (!GeneratePkgInfoXml(new_application, icon_name, app_dir, new_xml_path)) {
284     LOG(ERROR) << "Could not create new XML metadata file '"
285                << new_xml_path.value() << "'.";
286     return false;
287   }
288
289   base::FilePath icon =
290       icon_name.empty() ? kDefaultIcon : app_dir.AppendASCII(icon_name);
291
292   CommandLine cmdline(kPkgHelper);
293   cmdline.AppendSwitch("--update");
294   cmdline.AppendArg(package_id);
295   cmdline.AppendArgPath(new_xml_path);
296   cmdline.AppendArgPath(icon);
297
298   int exit_code;
299   std::string output;
300
301   if (!base::GetAppOutputWithExitCode(cmdline, &output, &exit_code)) {
302     LOG(ERROR) << "Could not launch installer helper";
303     return false;
304   }
305
306   if (exit_code != 0) {
307     LOG(ERROR) << "Could not update application: "
308                << output << " (" << exit_code << ")";
309     return false;
310   }
311
312   base::FilePath old_xml_path = data_dir.AppendASCII(info::kAppDir).AppendASCII(
313       package_id + std::string(info::kXmlExtension));
314   base::Move(new_xml_path, old_xml_path);
315   app_dir_cleaner.Dismiss();
316   return true;
317 }
318
319 }  // namespace application
320 }  // namespace xwalk