c11a577a039e1c277ebd63e08cd64d2106d4149c
[platform/core/appfw/app-installers.git] / src / common / plugins / metadata_plugin.cc
1 // Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
2 // Use of this source code is governed by an apache-2.0 license that can be
3 // found in the LICENSE file.
4
5 #include "common/plugins/metadata_plugin.h"
6
7 #include <pkgmgr-info.h>
8 #include <pkgmgr_parser.h>
9
10 #include <algorithm>
11 #include <map>
12 #include <set>
13 #include <utility>
14
15 #include "common/utils/glist_range.h"
16
17 namespace {
18
19 std::string GetMetadataTag(const std::string& url) {
20   return url.substr(url.find_last_of('/') + 1);
21 }
22
23 void ClearMetadataDetail(gpointer data) {
24   __metadata_t* meta = reinterpret_cast<__metadata_t*>(data);
25   if (meta->key)
26     free(const_cast<char*>(meta->key));
27   if (meta->value)
28     free(const_cast<char*>(meta->value));
29   free(meta);
30 }
31
32 GList* GetMetadataListForKey(GList* list, const std::string& key) {
33   // pack all metadata starting with key to list that will
34   // be sent to the plugin.
35   // e.g. all http://developer.samsung.com/tizen/metadata/profile/*
36   //   will be packed for http://developer.samsung.com/tizen/metadata/profile
37   GList* md_list = nullptr;
38   for (metadata_x* meta : GListRange<metadata_x*>(list)) {
39     if (meta->key && meta->value &&
40         std::string(meta->key).find(key) == 0) {
41       __metadata_t* md = reinterpret_cast<__metadata_t*>(
42           calloc(1, sizeof(__metadata_t)));
43       if (!md) {
44         LOG(ERROR) << "Out of memory";
45         g_list_free_full(md_list, &ClearMetadataDetail);
46         return nullptr;
47       }
48       md->key = strdup(meta->key);
49       if (!md->key) {
50         LOG(ERROR) << "Out of memory";
51         free(md);
52         g_list_free_full(md_list, &ClearMetadataDetail);
53         return nullptr;
54       }
55       md->value = strdup(meta->value);
56       if (!md->value) {
57         LOG(ERROR) << "Out of memory";
58         ClearMetadataDetail(md);
59         g_list_free_full(md_list, &ClearMetadataDetail);
60         return nullptr;
61       }
62       md_list = g_list_append(md_list, md);
63     }
64   }
65   return md_list;
66 }
67
68 }  // namespace
69
70 namespace common_installer {
71
72 const char MetadataPlugin::kType[] = "metadata";
73
74 std::unique_ptr<MetadataPlugin> MetadataPlugin::Create(
75     const PluginInfo& plugin_info) {
76   std::unique_ptr<MetadataPlugin> plugin(new MetadataPlugin(plugin_info));
77   if (!plugin->Load())
78     return nullptr;
79   return plugin;
80 }
81
82 bool MetadataPlugin::AddPluginInfo(manifest_x* manifest, const char* appid) {
83   plugin_x* plugin = static_cast<plugin_x*>(calloc(1, sizeof(plugin_x)));
84   if (!plugin) {
85     LOG(ERROR) << "out of memory";
86     return false;
87   }
88   plugin->pkgid = strdup(manifest->package);
89   plugin->appid = appid ? strdup(appid) : strdup("");
90   plugin->plugin_type = strdup(plugin_info_.type().c_str());
91   plugin->plugin_name = strdup(plugin_info_.name().c_str());
92   if (!plugin->pkgid || !plugin->appid |
93       !plugin->plugin_type || !plugin->plugin_name) {
94     LOG(ERROR) << "out of memory";
95     free(plugin->pkgid);
96     free(plugin->appid);
97     free(plugin->plugin_type);
98     free(plugin->plugin_name);
99     free(plugin);
100     return false;
101   }
102   manifest->plugin = g_list_append(manifest->plugin, plugin);
103   return true;
104 }
105
106 std::string MetadataPlugin::GetFunctionName(ActionType action) const {
107   static std::map<ActionType, std::string> names {
108     {ActionType::Install,  "PKGMGR_MDPARSER_PLUGIN_INSTALL"},
109     {ActionType::Upgrade,  "PKGMGR_MDPARSER_PLUGIN_UPGRADE"},
110     {ActionType::Uninstall,  "PKGMGR_MDPARSER_PLUGIN_UNINSTALL"},
111     {ActionType::RecoverInstall,  "PKGMGR_MDPARSER_PLUGIN_RECOVERINSTALL"},
112     {ActionType::RecoverUpgrade,  "PKGMGR_MDPARSER_PLUGIN_RECOVERUPGRADE"},
113     {ActionType::RecoverUninstall,  "PKGMGR_MDPARSER_PLUGIN_RECOVERUNINSTALL"},
114     {ActionType::Clean, "PKGMGR_MDPARSER_PLUGIN_CLEAN"},
115     {ActionType::Undo, "PKGMGR_MDPARSER_PLUGIN_UNDO"},
116     {ActionType::Removed, "PKGMGR_MDPARSER_PLUGIN_REMOVED"},
117   };
118
119   auto pos = names.find(action);
120   if (pos == names.end()) {
121     LOG(INFO) << "Function name not defined";
122     return "";
123   }
124   return pos->second;
125 }
126
127 bool MetadataPlugin::LoadPluginInfo(manifest_x* manifest) {
128   if (pkgmgrinfo_plugininfo_foreach_plugininfo(manifest->package,
129       MetadataPlugin::kType,
130       plugin_info_.name().c_str(),
131       [](const char* pkgid, const char* appid, const char*,
132           const char*, void* user_data) -> int {
133         auto* set = static_cast<std::set<PkgAppPair>*>(user_data);
134         set->emplace(std::string(pkgid), std::string(appid));
135         return PMINFO_R_OK;
136       },
137       &pkg_app_set_) != PMINFO_R_OK) {
138     LOG(ERROR) << "Failed to get previous execution info";
139     return false;
140   }
141   return true;
142 }
143
144 bool MetadataPlugin::ExecutePlugin(const std::string& name,
145     const char* pkgid, const char* appid, GList* md_list) {
146   int result = 0;
147   Exec(name, &result, pkgid, appid, md_list);
148   if (result) {
149     LOG(ERROR) << "Function: " << name << " of plugin "
150         << plugin_info_.path() << " failed";
151     return false;
152   }
153   return true;
154 }
155
156 bool MetadataPlugin::Run(xmlDocPtr /*doc_ptr*/, manifest_x* manifest,
157                          ActionType action_type) {
158   std::string name;
159   std::string tag = GetMetadataTag(plugin_info_.name());
160   if (tag.empty())
161     return false;
162
163   if (action_type == ActionType::Upgrade) {
164     // Load previous execution info. If some element left in pkg_app_set_
165     // at the end of this method, it means that the plugin was not executed for
166     // this pkg or app. (need to invoke PKGMGR_MDPARSER_PLUGIN_REMOVED)
167     if (!LoadPluginInfo(manifest))
168       return false;
169   }
170
171   bool result;
172   GList* md_list = GetMetadataListForKey(manifest->metadata,
173       plugin_info_.name());
174   if (md_list) {
175     name = GetFunctionName(action_type);
176     if (!AddPluginInfo(manifest, nullptr)) {
177       g_list_free_full(md_list, &ClearMetadataDetail);
178       return false;
179     }
180     pkg_app_set_.erase(
181         std::make_pair<std::string, std::string>(manifest->package, ""));
182     result = ExecutePlugin(name, manifest->package, nullptr, md_list);
183     g_list_free_full(md_list, &ClearMetadataDetail);
184     if (!result)
185       return false;
186   }
187
188   for (application_x* app : GListRange<application_x*>(manifest->application)) {
189     md_list = GetMetadataListForKey(app->metadata, plugin_info_.name());
190     // skip application if it has no given metadata
191     if (!md_list) {
192       continue;
193     } else {
194       name = GetFunctionName(action_type);
195       if (!AddPluginInfo(manifest, app->appid)) {
196         g_list_free_full(md_list, &ClearMetadataDetail);
197         return false;
198       }
199       pkg_app_set_.erase(
200           std::make_pair<std::string, std::string>(
201               manifest->package, app->appid));
202     }
203     result = ExecutePlugin(name, manifest->package, app->appid, md_list);
204     g_list_free_full(md_list, &ClearMetadataDetail);
205     if (!result)
206       return false;
207   }
208
209   if (action_type == ActionType::Clean || action_type == ActionType::Undo)
210     name = GetFunctionName(action_type);
211   else
212     name = GetFunctionName(ActionType::Removed);
213
214   for (const auto& pkg_app : pkg_app_set_) {
215     result = ExecutePlugin(name, pkg_app.first.c_str(),
216         pkg_app.second.empty() ? nullptr : pkg_app.second.c_str(), nullptr);
217     if (!result)
218       return false;
219   }
220   return true;
221 }
222
223 }  // namespace common_installer