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