ed3d59baf6d498f784b8a62b594c1fe6f01c722f
[platform/framework/web/crosswalk.git] / src / xwalk / application / common / installer / 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/common/installer/package_installer.h"
6
7 #include <sys/types.h>
8 #include <algorithm>
9 #include <map>
10 #include <string>
11
12 #include "base/file_util.h"
13 #include "base/files/file_enumerator.h"
14 #include "base/logging.h"
15 #include "base/path_service.h"
16 #include "base/command_line.h"
17 #include "base/process/launch.h"
18 #include "base/version.h"
19 #include "xwalk/application/common/application_data.h"
20 #include "xwalk/application/common/application_file_util.h"
21 #include "xwalk/application/common/application_manifest_constants.h"
22 #include "xwalk/application/common/permission_policy_manager.h"
23 #include "xwalk/application/common/application_storage.h"
24 #include "xwalk/application/common/installer/tizen/packageinfo_constants.h"
25 #include "xwalk/runtime/common/xwalk_paths.h"
26
27 #if defined(OS_TIZEN)
28 #include "xwalk/application/common/installer/package_installer_tizen.h"
29 #endif
30
31 namespace xwalk {
32 namespace application {
33
34 namespace {
35
36 bool CopyDirectoryContents(const base::FilePath& from,
37     const base::FilePath& to) {
38   base::FileEnumerator iter(from, false,
39       base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES);
40   for (base::FilePath path = iter.Next(); !path.empty(); path = iter.Next()) {
41     if (iter.GetInfo().IsDirectory()) {
42       if (!base::CopyDirectory(path, to, true))
43         return false;
44     } else if (!base::CopyFile(path, to.Append(path.BaseName()))) {
45         return false;
46     }
47   }
48
49   return true;
50 }
51
52 const base::FilePath::CharType kApplicationsDir[] =
53     FILE_PATH_LITERAL("applications");
54
55 }  // namespace
56
57 PackageInstaller::PackageInstaller(ApplicationStorage* storage)
58     : storage_(storage) {
59 }
60
61 PackageInstaller::~PackageInstaller() {
62 }
63
64 scoped_ptr<PackageInstaller> PackageInstaller::Create(
65     ApplicationStorage* storage) {
66 #if defined(OS_TIZEN)
67   return scoped_ptr<PackageInstaller>(new PackageInstallerTizen(storage));
68 #else
69   return scoped_ptr<PackageInstaller>(new PackageInstaller(storage));
70 #endif
71 }
72
73 bool PackageInstaller::PlatformInstall(ApplicationData* data) {
74   return true;
75 }
76
77 bool PackageInstaller::PlatformUninstall(ApplicationData* data) {
78   return true;
79 }
80
81 bool PackageInstaller::PlatformUpdate(ApplicationData* updated_data) {
82   return true;
83 }
84
85 bool PackageInstaller::Install(const base::FilePath& path, std::string* id) {
86   // FIXME(leandro): Installation is not robust enough -- should any step
87   // fail, it can't roll back to a consistent state.
88   if (!base::PathExists(path))
89     return false;
90
91   base::FilePath data_dir;
92   CHECK(PathService::Get(xwalk::DIR_DATA_PATH, &data_dir));
93   data_dir = data_dir.Append(kApplicationsDir);
94
95   // Make sure the kApplicationsDir exists under data_path, otherwise,
96   // the installation will always fail because of moving application
97   // resources into an invalid directory.
98   if (!base::DirectoryExists(data_dir) &&
99       !base::CreateDirectory(data_dir))
100     return false;
101
102   std::string app_id;
103   base::FilePath unpacked_dir;
104   scoped_ptr<Package> package;
105   if (!base::DirectoryExists(path)) {
106     package = Package::Create(path);
107     package->Extract(&unpacked_dir);
108     app_id = package->Id();
109   } else {
110     unpacked_dir = path;
111   }
112
113   std::string error;
114   scoped_refptr<ApplicationData> app_data = LoadApplication(
115       unpacked_dir, app_id, Manifest::COMMAND_LINE,
116       package->type(), &error);
117   if (!app_data) {
118     LOG(ERROR) << "Error during application installation: " << error;
119     return false;
120   }
121
122   // FIXME: Probably should be removed, as we should not handle permissions
123   // inside XWalk.
124   PermissionPolicyManager permission_policy_handler;
125   if (!permission_policy_handler.
126       InitApplicationPermission(app_data)) {
127     LOG(ERROR) << "Application permission data is invalid";
128     return false;
129   }
130
131   if (storage_->Contains(app_data->ID())) {
132     *id = app_data->ID();
133     LOG(INFO) << "Already installed: " << *id;
134     return false;
135   }
136
137   base::FilePath app_dir = data_dir.AppendASCII(app_data->ID());
138   if (base::DirectoryExists(app_dir)) {
139     if (!base::DeleteFile(app_dir, true))
140       return false;
141   }
142   if (!package) {
143     if (!base::CreateDirectory(app_dir))
144       return false;
145     if (!CopyDirectoryContents(unpacked_dir, app_dir))
146       return false;
147   } else {
148     if (!base::Move(unpacked_dir, app_dir))
149       return false;
150   }
151
152   app_data->SetPath(app_dir);
153
154   if (!storage_->AddApplication(app_data)) {
155     LOG(ERROR) << "Application with id " << app_data->ID()
156                << " couldn't be installed due to a Storage error";
157     base::DeleteFile(app_dir, true);
158     return false;
159   }
160
161   if (!PlatformInstall(app_data)) {
162     LOG(ERROR) << "Application with id " << app_data->ID()
163                << " couldn't be installed due to a platform error";
164     storage_->RemoveApplication(app_data->ID());
165     base::DeleteFile(app_dir, true);
166     return false;
167   }
168
169   LOG(INFO) << "Installed application with id: " << app_data->ID()
170             << "to" << app_dir.MaybeAsASCII() << " successfully.";
171   *id = app_data->ID();
172
173   return true;
174 }
175
176 bool PackageInstaller::Update(const std::string& app_id,
177                               const base::FilePath& path) {
178   if (!ApplicationData::IsIDValid(app_id)) {
179     LOG(ERROR) << "The given application id " << app_id << " is invalid.";
180     return false;
181   }
182
183   if (!base::PathExists(path)) {
184     LOG(ERROR) << "The XPK/WGT package file " << path.value() << " is invalid.";
185     return false;
186   }
187
188   if (base::DirectoryExists(path)) {
189     LOG(WARNING) << "Cannot update an unpacked XPK/WGT package.";
190     return false;
191   }
192
193   base::FilePath unpacked_dir;
194   scoped_ptr<Package> package = Package::Create(path);
195   if (!package) {
196     LOG(ERROR) << "XPK/WGT file is invalid.";
197     return false;
198   }
199
200   if (app_id.compare(package->Id()) != 0) {
201     LOG(ERROR) << "The XPK/WGT file is invalid, the application id is not the"
202                << "same as the installed application has.";
203     return false;
204   }
205
206   if (!package->Extract(&unpacked_dir))
207     return false;
208
209   std::string error;
210   scoped_refptr<ApplicationData> new_app_data =
211       LoadApplication(unpacked_dir,
212                       app_id,
213                       Manifest::COMMAND_LINE,
214                       package->type(),
215                       &error);
216   if (!new_app_data) {
217     LOG(ERROR) << "An error occurred during application updating: " << error;
218     return false;
219   }
220
221   scoped_refptr<ApplicationData> old_app_data =
222       storage_->GetApplicationData(app_id);
223   if (!old_app_data) {
224     LOG(INFO) << "Application haven't installed yet: " << app_id;
225     return false;
226   }
227
228   if (
229 #if defined(OS_TIZEN)
230       // For Tizen WGT package, downgrade to a lower version or reinstall
231       // is permitted when using Tizen WRT, Crosswalk runtime need to follow
232       // this behavior on Tizen platform.
233       package->type() != Package::WGT &&
234 #endif
235       old_app_data->Version()->CompareTo(
236           *(new_app_data->Version())) >= 0) {
237     LOG(INFO) << "The version number of new XPK/WGT package "
238                  "should be higher than "
239               << old_app_data->VersionString();
240     return false;
241   }
242
243   const base::FilePath& app_dir = old_app_data->Path();
244   const base::FilePath tmp_dir(app_dir.value()
245                                + FILE_PATH_LITERAL(".tmp"));
246
247   if (!base::Move(app_dir, tmp_dir) ||
248       !base::Move(unpacked_dir, app_dir))
249     return false;
250
251   new_app_data = LoadApplication(app_dir,
252                                     app_id,
253                                     Manifest::COMMAND_LINE,
254                                     package->type(),
255                                     &error);
256   if (!new_app_data) {
257     LOG(ERROR) << "Error during loading new package: " << error;
258     base::DeleteFile(app_dir, true);
259     base::Move(tmp_dir, app_dir);
260     return false;
261   }
262
263   if (!storage_->UpdateApplication(new_app_data)) {
264     LOG(ERROR) << "Fail to update application, roll back to the old one.";
265     base::DeleteFile(app_dir, true);
266     base::Move(tmp_dir, app_dir);
267     return false;
268   }
269
270   if (!PlatformUpdate(new_app_data)) {
271     LOG(ERROR) << "Fail to update application, roll back to the old one.";
272     base::DeleteFile(app_dir, true);
273     if (!storage_->UpdateApplication(old_app_data)) {
274       LOG(ERROR) << "Fail to revert old application info, "
275                  << "remove the application as a last resort.";
276       storage_->RemoveApplication(old_app_data->ID());
277       base::DeleteFile(tmp_dir, true);
278       return false;
279     }
280     base::Move(tmp_dir, app_dir);
281     return false;
282   }
283
284   base::DeleteFile(tmp_dir, true);
285
286   return true;
287 }
288
289 bool PackageInstaller::Uninstall(const std::string& id) {
290   bool result = true;
291   scoped_refptr<ApplicationData> app_data = storage_->GetApplicationData(id);
292   if (!app_data) {
293     LOG(ERROR) << "Failed to find application with id " << id
294                << " among the installed ones.";
295     result = false;
296   }
297
298   if (!storage_->RemoveApplication(id)) {
299     LOG(ERROR) << "Cannot uninstall application with id " << id
300                << "; application is not installed.";
301     result = false;
302   }
303
304   base::FilePath resources;
305   CHECK(PathService::Get(xwalk::DIR_DATA_PATH, &resources));
306   resources = resources.Append(kApplicationsDir).AppendASCII(id);
307   if (base::DirectoryExists(resources) &&
308       !base::DeleteFile(resources, true)) {
309     LOG(ERROR) << "Error occurred while trying to remove application with id "
310                << id << "; Cannot remove all resources.";
311     result = false;
312   }
313
314   if (!PlatformUninstall(app_data))
315     result = false;
316
317   return result;
318 }
319
320 }  // namespace application
321 }  // namespace xwalk