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