Upstream version 7.36.149.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 bool CreateAppSymbolicLink(const base::FilePath& app_dir,
148                            const std::string& tizen_app_id) {
149   base::FilePath execute_path =
150       app_dir.AppendASCII("bin/").AppendASCII(tizen_app_id);
151
152   if (!base::CreateDirectory(execute_path.DirName())) {
153     LOG(ERROR) << "Could not create directory '"
154                << execute_path.DirName().value() << "'.";
155     return false;
156   }
157
158   if (!base::CreateSymbolicLink(kXWalkLauncherBinary, execute_path)) {
159     LOG(ERROR) << "Could not create symbolic link to launcher from '"
160                << execute_path.value() << "'.";
161     return false;
162   }
163   return true;
164 }
165
166 }  // namespace
167
168 namespace xwalk {
169 namespace application {
170
171 bool PackageInstaller::InstallApplication(
172     ApplicationData* application, const base::FilePath& data_dir) {
173   if (!CheckRunningMode())
174     return false;
175
176   std::string package_id = application->ID();
177   std::string tizen_app_id = kAppIdPrefix + package_id;
178   base::FilePath app_dir =
179       data_dir.AppendASCII(info::kAppDir).AppendASCII(package_id);
180   base::FilePath xml_path = data_dir.AppendASCII(info::kAppDir).AppendASCII(
181       package_id + std::string(info::kXmlExtension));
182
183   std::string icon_name;
184   if (!application->GetManifest()->GetString(info::kIconKey, &icon_name)) {
185     LOG(WARNING) << "'icon' not included in manifest";
186   }
187   // This will clean everything inside '<data dir>/<app id>'.
188   FileDeleter app_dir_cleaner(app_dir, true);
189
190   if (!GeneratePkgInfoXml(application, icon_name, app_dir, xml_path)) {
191     LOG(ERROR) << "Could not create XML metadata file '"
192                << xml_path.value() << "'.";
193     return false;
194   }
195
196   if (!CreateAppSymbolicLink(app_dir, tizen_app_id))
197     return false;
198
199   base::FilePath icon =
200       icon_name.empty() ? kDefaultIcon : app_dir.AppendASCII(icon_name);
201
202   CommandLine cmdline(kPkgHelper);
203   cmdline.AppendSwitch("--install");
204   cmdline.AppendArg(package_id);
205   cmdline.AppendArgPath(xml_path);
206   cmdline.AppendArgPath(icon);
207
208   int exit_code;
209   std::string output;
210
211   if (!base::GetAppOutputWithExitCode(cmdline, &output, &exit_code)) {
212     LOG(ERROR) << "Could not launch installer helper.";
213     return false;
214   }
215
216   if (exit_code != 0) {
217     LOG(ERROR) << "Could not install application: "
218                << output << " (" << exit_code << ")";
219     return false;
220   }
221
222   app_dir_cleaner.Dismiss();
223
224   return true;
225 }
226
227 bool PackageInstaller::UninstallApplication(
228     ApplicationData* application, const base::FilePath& data_dir) {
229   if (!CheckRunningMode())
230     return false;
231
232   std::string package_id = application->ID();
233   bool result = true;
234
235   CommandLine cmdline(kPkgHelper);
236   cmdline.AppendSwitch("--uninstall");
237   cmdline.AppendArg(package_id);
238
239   int exit_code;
240   std::string output;
241
242   if (!base::GetAppOutputWithExitCode(cmdline, &output, &exit_code)) {
243     LOG(ERROR) << "Could not launch installer helper";
244     result = false;
245   }
246
247   if (exit_code != 0) {
248     LOG(ERROR) << "Could not uninstall application: "
249                << output << " (" << exit_code << ")";
250     result = false;
251   }
252
253   base::FilePath app_dir =
254       data_dir.AppendASCII(info::kAppDir).AppendASCII(package_id);
255   if (!base::DeleteFile(app_dir, true)) {
256     LOG(ERROR) << "Could not remove directory '" << app_dir.value() << "'";
257     result = false;
258   }
259
260   base::FilePath xml_path = data_dir.AppendASCII(
261       package_id + std::string(info::kXmlExtension));
262   if (!base::DeleteFile(xml_path, false)) {
263     LOG(ERROR) << "Could not remove file '" << xml_path.value() << "'";
264     result = false;
265   }
266
267   return result;
268 }
269
270 bool PackageInstaller::UpdateApplication(
271     ApplicationData* new_application, const base::FilePath& data_dir) {
272   if (!CheckRunningMode())
273     return false;
274
275   std::string package_id = new_application->ID();
276   std::string tizen_app_id = kAppIdPrefix + package_id;
277   base::FilePath app_dir =
278       data_dir.AppendASCII(info::kAppDir).AppendASCII(package_id);
279   base::FilePath new_xml_path = data_dir.AppendASCII(info::kAppDir).AppendASCII(
280       package_id + ".new" + std::string(info::kXmlExtension));
281
282   std::string icon_name;
283   if (!new_application->GetManifest()->GetString(info::kIconKey, &icon_name)) {
284     LOG(WARNING) << "'icon' not included in manifest";
285   }
286   // This will clean everything inside '<data dir>/<app id>' and the new XML.
287   FileDeleter app_dir_cleaner(app_dir, true);
288   FileDeleter new_xml_cleaner(new_xml_path, true);
289
290   if (!GeneratePkgInfoXml(new_application, icon_name, app_dir, new_xml_path)) {
291     LOG(ERROR) << "Could not create new XML metadata file '"
292                << new_xml_path.value() << "'.";
293     return false;
294   }
295
296   if (!CreateAppSymbolicLink(app_dir, tizen_app_id))
297     return false;
298
299   base::FilePath icon =
300       icon_name.empty() ? kDefaultIcon : app_dir.AppendASCII(icon_name);
301
302   CommandLine cmdline(kPkgHelper);
303   cmdline.AppendSwitch("--update");
304   cmdline.AppendArg(package_id);
305   cmdline.AppendArgPath(new_xml_path);
306   cmdline.AppendArgPath(icon);
307
308   int exit_code;
309   std::string output;
310
311   if (!base::GetAppOutputWithExitCode(cmdline, &output, &exit_code)) {
312     LOG(ERROR) << "Could not launch installer helper";
313     return false;
314   }
315
316   if (exit_code != 0) {
317     LOG(ERROR) << "Could not update application: "
318                << output << " (" << exit_code << ")";
319     return false;
320   }
321
322   base::FilePath old_xml_path = data_dir.AppendASCII(info::kAppDir).AppendASCII(
323       package_id + std::string(info::kXmlExtension));
324   base::Move(new_xml_path, old_xml_path);
325   app_dir_cleaner.Dismiss();
326   return true;
327 }
328
329 }  // namespace application
330 }  // namespace xwalk