52f305ad163d8fb25cac04bd74e2b54f764ece4e
[platform/framework/web/crosswalk.git] / src / xwalk / application / browser / installer / tizen / service_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/service_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 <string>
13 #include "base/file_util.h"
14 #include "base/files/file_enumerator.h"
15 #include "base/logging.h"
16 #include "base/path_service.h"
17 #include "base/command_line.h"
18 #include "base/process/launch.h"
19 #include "third_party/libxml/chromium/libxml_utils.h"
20 #include "xwalk/application/common/application_data.h"
21 #include "xwalk/application/browser/application_storage.h"
22 #include "xwalk/application/browser/installer/tizen/packageinfo_constants.h"
23
24 namespace info = xwalk::application_packageinfo_constants;
25
26 namespace {
27
28 const base::FilePath kPkgHelper("/usr/bin/xwalk-pkg-helper");
29
30 const base::FilePath kXWalkLauncherBinary("/usr/bin/xwalk-launcher");
31
32 const base::FilePath kDefaultIcon(
33     "/usr/share/icons/default/small/crosswalk.png");
34
35 const std::string kServicePrefix("xwalk-service.");
36 const std::string kAppIdPrefix("xwalk.");
37
38 class FileDeleter {
39  public:
40   FileDeleter(const base::FilePath& path, bool recursive)
41       : path_(path),
42         recursive_(recursive) {}
43
44   ~FileDeleter() {
45     if (path_.empty())
46       return;
47
48     base::DeleteFile(path_, recursive_);
49   }
50
51   void Dismiss() {
52     path_.clear();
53   }
54
55  private:
56   base::FilePath path_;
57   bool recursive_;
58 };
59
60 bool GeneratePkgInfoXml(xwalk::application::ApplicationData* application,
61                         const std::string& icon_name,
62                         const base::FilePath& app_dir,
63                         const base::FilePath& xml_path) {
64   if (!base::PathExists(app_dir) &&
65       !base::CreateDirectory(app_dir))
66     return false;
67
68   std::string package_id = application->ID();
69   std::string tizen_app_id = kAppIdPrefix + package_id;
70   base::FilePath execute_path =
71       app_dir.AppendASCII("bin/").AppendASCII(tizen_app_id);
72   std::string stripped_name = application->Name();
73
74   FILE* file = base::OpenFile(xml_path, "w");
75
76   XmlWriter xml_writer;
77   xml_writer.StartWriting();
78   xml_writer.StartElement("manifest");
79   xml_writer.AddAttribute("xmlns", "http://tizen.org/ns/packages");
80   xml_writer.AddAttribute("package", package_id);
81   xml_writer.AddAttribute("version", application->VersionString());
82   xml_writer.WriteElement("label", application->Name());
83   xml_writer.WriteElement("description", application->Description());
84
85   xml_writer.StartElement("ui-application");
86   xml_writer.AddAttribute("appid", tizen_app_id);
87   xml_writer.AddAttribute("exec", execute_path.MaybeAsASCII());
88   xml_writer.AddAttribute("type", "c++app");
89   xml_writer.AddAttribute("taskmanage", "true");
90   xml_writer.WriteElement("label", application->Name());
91
92   if (icon_name.empty())
93     xml_writer.WriteElement("icon", info::kDefaultIconName);
94   else
95     xml_writer.WriteElement("icon", kServicePrefix + package_id + ".png");
96   xml_writer.EndElement();  // Ends "ui-application"
97
98   xml_writer.EndElement();  // Ends "manifest" element.
99   xml_writer.StopWriting();
100
101   base::WriteFile(xml_path,
102                   xml_writer.GetWrittenString().c_str(),
103                   xml_writer.GetWrittenString().size());
104
105   base::CloseFile(file);
106   LOG(INFO) << "Converting manifest.json into "
107             << xml_path.BaseName().MaybeAsASCII()
108             << " for installation. [DONE]";
109   return true;
110 }
111
112 }  // namespace
113
114 namespace xwalk {
115 namespace application {
116
117 bool InstallApplicationForTizen(
118     ApplicationData* application, const base::FilePath& data_dir) {
119   std::string package_id = application->ID();
120   std::string tizen_app_id = kAppIdPrefix + package_id;
121   base::FilePath app_dir =
122       data_dir.AppendASCII(info::kAppDir).AppendASCII(package_id);
123   base::FilePath xml_path = data_dir.AppendASCII(info::kAppDir).AppendASCII(
124       package_id + std::string(info::kXmlExtension));
125
126   std::string icon_name;
127   if (!application->GetManifest()->GetString(info::kIconKey, &icon_name)) {
128     LOG(WARNING) << "'icon' not included in manifest";
129   }
130   // This will clean everything inside '<data dir>/<app id>'.
131   FileDeleter app_dir_cleaner(app_dir, true);
132
133   if (!GeneratePkgInfoXml(application, icon_name, app_dir, xml_path)) {
134     LOG(ERROR) << "Could not create XML metadata file '"
135                << xml_path.value() << "'.";
136     return false;
137   }
138
139   base::FilePath execute_path =
140       app_dir.AppendASCII("bin/").AppendASCII(tizen_app_id);
141
142   if (!base::CreateDirectory(execute_path.DirName())) {
143     LOG(ERROR) << "Could not create directory '"
144                << execute_path.DirName().value() << "'.";
145     return false;
146   }
147
148   if (!base::CreateSymbolicLink(kXWalkLauncherBinary, execute_path)) {
149     LOG(ERROR) << "Could not create symbolic link to launcher from '"
150                << execute_path.value() << "'.";
151     return false;
152   }
153
154   base::FilePath icon;
155   if (icon_name.empty())
156     icon = kDefaultIcon;
157   else
158     icon = app_dir.AppendASCII(icon_name);
159
160   CommandLine cmdline(kPkgHelper);
161   cmdline.AppendSwitch("--install");
162   cmdline.AppendArg(package_id);
163   cmdline.AppendArgPath(xml_path);
164   cmdline.AppendArgPath(icon);
165
166   int exit_code;
167   std::string output;
168
169   if (!base::GetAppOutputWithExitCode(cmdline, &output, &exit_code)) {
170     LOG(ERROR) << "Could not launch installer helper.";
171     return false;
172   }
173
174   if (exit_code != 0) {
175     LOG(ERROR) << "Could not install application: "
176                << output << " (" << exit_code << ")";
177     return false;
178   }
179
180   app_dir_cleaner.Dismiss();
181
182   return true;
183 }
184
185 bool UninstallApplicationForTizen(ApplicationData* application,
186                                   const base::FilePath& data_dir) {
187   std::string package_id = application->ID();
188   bool result = true;
189
190   CommandLine cmdline(kPkgHelper);
191   cmdline.AppendSwitch("--uninstall");
192   cmdline.AppendArg(package_id);
193
194   int exit_code;
195   std::string output;
196
197   if (!base::GetAppOutputWithExitCode(cmdline, &output, &exit_code)) {
198     LOG(ERROR) << "Could not launch installer helper";
199     result = false;
200   }
201
202   if (exit_code != 0) {
203     LOG(ERROR) << "Could not uninstall application: "
204                << output << " (" << exit_code << ")";
205     result = false;
206   }
207
208   base::FilePath app_dir =
209       data_dir.AppendASCII(info::kAppDir).AppendASCII(package_id);
210   if (!base::DeleteFile(app_dir, true)) {
211     LOG(ERROR) << "Could not remove directory '" << app_dir.value() << "'";
212     result = false;
213   }
214
215   base::FilePath xml_path = data_dir.AppendASCII(
216       package_id + std::string(info::kXmlExtension));
217   if (!base::DeleteFile(xml_path, false)) {
218     LOG(ERROR) << "Could not remove file '" << xml_path.value() << "'";
219     result = false;
220   }
221
222   return result;
223 }
224
225 }  // namespace application
226 }  // namespace xwalk