Fix CategoryPlugin, MetadataPlugin
[platform/core/appfw/app-installers.git] / src / common / plugins / category_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/category_plugin.h"
6
7 #include <pkgmgr-info.h>
8 #include <pkgmgr_parser.h>
9
10 #include <algorithm>
11 #include <map>
12 #include <set>
13
14 #include "common/utils/glist_range.h"
15
16 namespace {
17
18 std::string GetCategoryName(const std::string& url) {
19   return url.substr(url.find_last_of('/') + 1);
20 }
21
22 void ClearCategoryDetail(gpointer data) {
23   __category_t* category = reinterpret_cast<__category_t*>(data);
24   free(const_cast<char*>(category->name));
25   free(category);
26 }
27
28 GList* GetCategoryListForKey(GList* list, const std::string& key) {
29   // pack all categories starting with key to list that will
30   // be sent to the plugin.
31   // e.g. all http://tizen.org/category/antivirus/*
32   //   will be packed for http://tizen.org/category/antivirus
33   GList* category_list = nullptr;
34   for (const char* category : GListRange<char*>(list)) {
35     if (std::string(category).find(key) != 0)
36       continue;
37
38     __category_t* c = reinterpret_cast<__category_t*>(
39         calloc(1, sizeof(__category_t)));
40     if (!c) {
41       LOG(ERROR) << "Out of memory";
42       g_list_free_full(category_list, &ClearCategoryDetail);
43       return nullptr;
44     }
45     c->name = strdup(category);
46     if (!c->name) {
47       LOG(ERROR) << "Out of memory";
48       free(c);
49       g_list_free_full(category_list, &ClearCategoryDetail);
50       return nullptr;
51     }
52     category_list = g_list_append(category_list, c);
53   }
54   return category_list;
55 }
56
57 }  // namespace
58
59 namespace common_installer {
60
61 const char CategoryPlugin::kType[] = "category";
62
63 std::unique_ptr<CategoryPlugin> CategoryPlugin::Create(
64     const PluginInfo& plugin_info) {
65   std::unique_ptr<CategoryPlugin> plugin(new CategoryPlugin(plugin_info));
66   if (!plugin->Load())
67     return nullptr;
68   return plugin;
69 }
70
71 bool CategoryPlugin::AddPluginInfo(manifest_x* manifest, const char* appid) {
72   plugin_x* plugin = static_cast<plugin_x*>(calloc(1, sizeof(plugin_x)));
73   if (!plugin) {
74     LOG(ERROR) << "out of memory";
75     return false;
76   }
77   plugin->pkgid = strdup(manifest->package);
78   plugin->appid = strdup(appid);
79   plugin->plugin_type = strdup(plugin_info_.type().c_str());
80   plugin->plugin_name = strdup(plugin_info_.name().c_str());
81   manifest->plugin = g_list_append(manifest->plugin, plugin);
82   return true;
83 }
84
85 std::string CategoryPlugin::GetFunctionName(ActionType action) const {
86   static std::map<ActionType, std::string> names {
87     {ActionType::Install,  "PKGMGR_CATEGORY_PARSER_PLUGIN_INSTALL"},
88     {ActionType::Upgrade,  "PKGMGR_CATEGORY_PARSER_PLUGIN_UPGRADE"},
89     {ActionType::Uninstall,  "PKGMGR_CATEGORY_PARSER_PLUGIN_UNINSTALL"},
90     {ActionType::RecoverInstall, "PKGMGR_CATEGORY_PARSER_PLUGIN_RECOVERINSTALL"},   // NOLINT
91     {ActionType::RecoverUpgrade, "PKGMGR_CATEGORY_PARSER_PLUGIN_RECOVERUPGRADE"},   // NOLINT
92     {ActionType::RecoverUninstall, "PKGMGR_CATEGORY_PARSER_PLUGIN_RECOVERUNINSTALL"},   // NOLINT
93     {ActionType::Clean, "PKGMGR_CATEGORY_PARSER_PLUGIN_CLEAN"},
94     {ActionType::Undo, "PKGMGR_CATEGORY_PARSER_PLUGIN_UNDO"},
95     {ActionType::Removed, "PKGMGR_CATEGORY_PARSER_PLUGIN_REMOVED"},
96   };
97
98   auto pos = names.find(action);
99   if (pos == names.end()) {
100     LOG(INFO) << "Function name not defined";
101     return "";
102   }
103   return pos->second;
104 }
105
106 bool CategoryPlugin::ExecutePlugin(const std::string& name,
107     const char* pkgid, const char* appid, GList* category_list) {
108   int result = 0;
109   Exec(name, &result, pkgid, appid, category_list);
110   if (result) {
111     LOG(ERROR) << "Function: " << name << " of plugin "
112         << plugin_info_.path() << " failed";
113     return false;
114   }
115   return true;
116 }
117
118 bool CategoryPlugin::LoadPluginInfo(manifest_x* manifest) {
119   if (pkgmgrinfo_plugininfo_foreach_plugininfo(manifest->package,
120       CategoryPlugin::kType,
121       plugin_info_.name().c_str(),
122       [](const char*, const char* appid, const char*,
123           const char*, void* user_data) -> int {
124         auto* set = static_cast<std::set<std::string>*>(user_data);
125         set->emplace(std::string(appid));
126         return PMINFO_R_OK;
127       },
128       &appid_set_) != PMINFO_R_OK) {
129     LOG(ERROR) << "Failed to get previous execution info";
130     return false;
131   }
132   return true;
133 }
134
135 bool CategoryPlugin::Run(xmlDocPtr /*doc_ptr*/, manifest_x* manifest,
136          ActionType action_type) {
137   std::string name;
138   std::string tag = GetCategoryName(plugin_info_.name());
139   if (tag.empty())
140     return false;
141
142   if (action_type == ActionType::Upgrade) {
143     // Load previous execution info. If some element left in appid_set_
144     // at the end of this method, it means that the plugin was not executed for
145     // this app. (need to invoke PKGMGR_MDPARSER_PLUGIN_REMOVED)
146     if (!LoadPluginInfo(manifest))
147       return false;
148   }
149
150   bool result;
151   for (application_x* app : GListRange<application_x*>(manifest->application)) {
152     // pack all categories starting with key to list that will
153     // be sent to the plugin.
154     // e.g. all http://tizen.org/category/antivirus/*
155     //   will be packed for http://tizen.org/category/antivirus
156     GList* category_list = GetCategoryListForKey(app->category,
157         plugin_info_.name());
158     // skip application if it has no given category
159     if (!category_list) {
160       continue;
161     } else {
162       name = GetFunctionName(action_type);
163       if (!AddPluginInfo(manifest, app->appid)) {
164         g_list_free_full(category_list, &ClearCategoryDetail);
165         return false;
166       }
167       appid_set_.erase(app->appid);
168     }
169
170     result = ExecutePlugin(name, manifest->package, app->appid,
171         category_list);
172     g_list_free_full(category_list, &ClearCategoryDetail);
173     if (!result)
174       return false;
175   }
176
177   name = GetFunctionName(ActionType::Removed);
178   for (const auto& appid : appid_set_) {
179     result = ExecutePlugin(name, manifest->package, appid.c_str(), nullptr);
180     if (!result)
181       return false;
182   }
183   return true;
184 }
185
186 }  // namespace common_installer