Fix CategoryPlugin, MetadataPlugin
[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   GList* md_list = nullptr;
34   for (metadata_x* meta : GListRange<metadata_x*>(list)) {
35     // key and val should not be null (at least empty string)
36     if (!meta->key || !meta->val) {
37       LOG(ERROR) << "Metadata key or val is null";
38       continue;
39     }
40
41     if (std::string(meta->key).find(key) != 0)
42       continue;
43
44     __metadata_t* md = reinterpret_cast<__metadata_t*>(
45         calloc(1, sizeof(__metadata_t)));
46     if (!md) {
47       LOG(ERROR) << "Out of memory";
48       g_list_free_full(md_list, &ClearMetadataDetail);
49       return nullptr;
50     }
51     md->key = strdup(meta->key);
52     if (!md->key) {
53       LOG(ERROR) << "Out of memory";
54       free(md);
55       g_list_free_full(md_list, &ClearMetadataDetail);
56       return nullptr;
57     }
58     md->value = strdup(meta->value);
59     if (!md->value) {
60       LOG(ERROR) << "Out of memory";
61       ClearMetadataDetail(md);
62       g_list_free_full(md_list, &ClearMetadataDetail);
63       return nullptr;
64     }
65     md_list = g_list_append(md_list, md);
66   }
67   return md_list;
68 }
69
70 }  // namespace
71
72 namespace common_installer {
73
74 const char MetadataPlugin::kType[] = "metadata";
75
76 std::unique_ptr<MetadataPlugin> MetadataPlugin::Create(
77     const PluginInfo& plugin_info) {
78   std::unique_ptr<MetadataPlugin> plugin(new MetadataPlugin(plugin_info));
79   if (!plugin->Load())
80     return nullptr;
81   return plugin;
82 }
83
84 bool MetadataPlugin::AddPluginInfo(manifest_x* manifest, const char* appid) {
85   plugin_x* plugin = static_cast<plugin_x*>(calloc(1, sizeof(plugin_x)));
86   if (!plugin) {
87     LOG(ERROR) << "out of memory";
88     return false;
89   }
90   plugin->pkgid = strdup(manifest->package);
91   plugin->appid = appid ? strdup(appid) : strdup("");
92   plugin->plugin_type = strdup(plugin_info_.type().c_str());
93   plugin->plugin_name = strdup(plugin_info_.name().c_str());
94   if (!plugin->pkgid || !plugin->appid |
95       !plugin->plugin_type || !plugin->plugin_name) {
96     LOG(ERROR) << "out of memory";
97     free(plugin->pkgid);
98     free(plugin->appid);
99     free(plugin->plugin_type);
100     free(plugin->plugin_name);
101     free(plugin);
102     return false;
103   }
104   manifest->plugin = g_list_append(manifest->plugin, plugin);
105   return true;
106 }
107
108 std::string MetadataPlugin::GetFunctionName(ActionType action) const {
109   static std::map<ActionType, std::string> names {
110     {ActionType::Install,  "PKGMGR_MDPARSER_PLUGIN_INSTALL"},
111     {ActionType::Upgrade,  "PKGMGR_MDPARSER_PLUGIN_UPGRADE"},
112     {ActionType::Uninstall,  "PKGMGR_MDPARSER_PLUGIN_UNINSTALL"},
113     {ActionType::RecoverInstall,  "PKGMGR_MDPARSER_PLUGIN_RECOVERINSTALL"},
114     {ActionType::RecoverUpgrade,  "PKGMGR_MDPARSER_PLUGIN_RECOVERUPGRADE"},
115     {ActionType::RecoverUninstall,  "PKGMGR_MDPARSER_PLUGIN_RECOVERUNINSTALL"},
116     {ActionType::Clean, "PKGMGR_MDPARSER_PLUGIN_CLEAN"},
117     {ActionType::Undo, "PKGMGR_MDPARSER_PLUGIN_UNDO"},
118     {ActionType::Removed, "PKGMGR_MDPARSER_PLUGIN_REMOVED"},
119   };
120
121   auto pos = names.find(action);
122   if (pos == names.end()) {
123     LOG(INFO) << "Function name not defined";
124     return "";
125   }
126   return pos->second;
127 }
128
129 bool MetadataPlugin::LoadPluginInfo(manifest_x* manifest) {
130   if (pkgmgrinfo_plugininfo_foreach_plugininfo(manifest->package,
131       MetadataPlugin::kType,
132       plugin_info_.name().c_str(),
133       [](const char* pkgid, const char* appid, const char*,
134           const char*, void* user_data) -> int {
135         auto* set = static_cast<std::set<PkgAppPair>*>(user_data);
136         set->emplace(std::string(pkgid), std::string(appid));
137         return PMINFO_R_OK;
138       },
139       &pkg_app_set_) != PMINFO_R_OK) {
140     LOG(ERROR) << "Failed to get previous execution info";
141     return false;
142   }
143   return true;
144 }
145
146 bool MetadataPlugin::ExecutePlugin(const std::string& name,
147     const char* pkgid, const char* appid, GList* md_list) {
148   int result = 0;
149   Exec(name, &result, pkgid, appid, md_list);
150   if (result) {
151     LOG(ERROR) << "Function: " << name << " of plugin "
152         << plugin_info_.path() << " failed";
153     return false;
154   }
155   return true;
156 }
157
158 bool MetadataPlugin::Run(xmlDocPtr /*doc_ptr*/, manifest_x* manifest,
159                          ActionType action_type) {
160   std::string name;
161   std::string tag = GetMetadataTag(plugin_info_.name());
162   if (tag.empty())
163     return false;
164
165   if (action_type == ActionType::Upgrade) {
166     // Load previous execution info. If some element left in pkg_app_set_
167     // at the end of this method, it means that the plugin was not executed for
168     // this pkg or app. (need to invoke PKGMGR_MDPARSER_PLUGIN_REMOVED)
169     if (!LoadPluginInfo(manifest))
170       return false;
171   }
172
173   bool result;
174   // pack all metadata starting with key to list that will
175   // be sent to the plugin.
176   // e.g. all http://developer.samsung.com/tizen/metadata/profile/*
177   //   will be packed for http://developer.samsung.com/tizen/metadata/profile
178   GList* md_list = GetMetadataListForKey(manifest->metadata,
179       plugin_info_.name());
180   if (md_list) {
181     name = GetFunctionName(action_type);
182     if (!AddPluginInfo(manifest, nullptr)) {
183       g_list_free_full(md_list, &ClearMetadataDetail);
184       return false;
185     }
186     pkg_app_set_.erase(
187         std::make_pair<std::string, std::string>(manifest->package, ""));
188     result = ExecutePlugin(name, manifest->package, nullptr, md_list);
189     g_list_free_full(md_list, &ClearMetadataDetail);
190     if (!result)
191       return false;
192   }
193
194   for (application_x* app : GListRange<application_x*>(manifest->application)) {
195     md_list = GetMetadataListForKey(app->metadata, plugin_info_.name());
196     // skip application if it has no given metadata
197     if (!md_list) {
198       continue;
199     } else {
200       name = GetFunctionName(action_type);
201       if (!AddPluginInfo(manifest, app->appid)) {
202         g_list_free_full(md_list, &ClearMetadataDetail);
203         return false;
204       }
205       pkg_app_set_.erase(
206           std::make_pair<std::string, std::string>(
207               manifest->package, app->appid));
208     }
209     result = ExecutePlugin(name, manifest->package, app->appid, md_list);
210     g_list_free_full(md_list, &ClearMetadataDetail);
211     if (!result)
212       return false;
213   }
214
215   if (action_type == ActionType::Clean || action_type == ActionType::Undo)
216     name = GetFunctionName(action_type);
217   else
218     name = GetFunctionName(ActionType::Removed);
219
220   for (const auto& pkg_app : pkg_app_set_) {
221     result = ExecutePlugin(name, pkg_app.first.c_str(),
222         pkg_app.second.empty() ? nullptr : pkg_app.second.c_str(), nullptr);
223     if (!result)
224       return false;
225   }
226   return true;
227 }
228
229 }  // namespace common_installer