Fix that plugin is executed several times
[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 }  // namespace
29
30 namespace common_installer {
31
32 const char CategoryPlugin::kType[] = "category";
33
34 std::unique_ptr<CategoryPlugin> CategoryPlugin::Create(
35     const PluginInfo& plugin_info) {
36   std::unique_ptr<CategoryPlugin> plugin(new CategoryPlugin(plugin_info));
37   if (!plugin->Load())
38     return nullptr;
39   return plugin;
40 }
41
42 bool CategoryPlugin::AddPluginInfo(manifest_x* manifest, const char* appid) {
43   plugin_x* plugin = static_cast<plugin_x*>(calloc(1, sizeof(plugin_x)));
44   if (!plugin) {
45     LOG(ERROR) << "out of memory";
46     return false;
47   }
48   plugin->pkgid = strdup(manifest->package);
49   plugin->appid = strdup(appid);
50   plugin->plugin_type = strdup(plugin_info_.type().c_str());
51   plugin->plugin_name = strdup(plugin_info_.name().c_str());
52   manifest->plugin = g_list_append(manifest->plugin, plugin);
53   return true;
54 }
55
56 std::string CategoryPlugin::GetFunctionName(ActionType action) const {
57   static std::map<ActionType, std::string> names {
58     {ActionType::Install,  "PKGMGR_CATEGORY_PARSER_PLUGIN_INSTALL"},
59     {ActionType::Upgrade,  "PKGMGR_CATEGORY_PARSER_PLUGIN_UPGRADE"},
60     {ActionType::Uninstall,  "PKGMGR_CATEGORY_PARSER_PLUGIN_UNINSTALL"},
61     {ActionType::RecoverInstall, "PKGMGR_CATEGORY_PARSER_PLUGIN_RECOVERINSTALL"},   // NOLINT
62     {ActionType::RecoverUpgrade, "PKGMGR_CATEGORY_PARSER_PLUGIN_RECOVERUPGRADE"},   // NOLINT
63     {ActionType::RecoverUninstall, "PKGMGR_CATEGORY_PARSER_PLUGIN_RECOVERUNINSTALL"},   // NOLINT
64     {ActionType::Clean, "PKGMGR_CATEGORY_PARSER_PLUGIN_CLEAN"},
65     {ActionType::Undo, "PKGMGR_CATEGORY_PARSER_PLUGIN_UNDO"},
66     {ActionType::Removed, "PKGMGR_CATEGORY_PARSER_PLUGIN_REMOVED"},
67   };
68
69   auto pos = names.find(action);
70   if (pos == names.end()) {
71     LOG(INFO) << "Function name not defined";
72     return "";
73   }
74   return pos->second;
75 }
76
77 bool CategoryPlugin::Run(xmlDocPtr /*doc_ptr*/, manifest_x* manifest,
78          ActionType action_type) {
79   std::string name;
80   std::string tag = GetCategoryName(plugin_info_.name());
81   if (tag.empty())
82     return false;
83
84   std::set<std::string> appid_list;
85   if (action_type == ActionType::Upgrade) {
86     if (pkgmgrinfo_plugininfo_foreach_plugininfo(manifest->package,
87         CategoryPlugin::kType,
88         plugin_info_.name().c_str(),
89         [](const char*, const char* appid, const char*,
90             const char*, void* user_data) -> int {
91           auto* list = static_cast<std::set<std::string>*>(user_data);
92           list->emplace(std::string(appid));
93           return PMINFO_R_OK;
94         },
95         &appid_list) != PMINFO_R_OK) {
96       LOG(ERROR) << "Failed to get previous execution info";
97       return false;
98     }
99   }
100
101   for (application_x* app : GListRange<application_x*>(manifest->application)) {
102     // pack all categories starting with key to list that will
103     // be sent to the plugin.
104     // e.g. all http://tizen.org/category/antivirus/*
105     //   will be packed for http://tizen.org/category/antivirus
106     GList* category_list = nullptr;
107     for (const char* category : GListRange<char*>(app->category)) {
108       const std::string& sub_key_prefix = plugin_info_.name();
109       if (std::string(category).find(sub_key_prefix) == 0) {
110         __category_t* c = reinterpret_cast<__category_t*>(
111             calloc(1, sizeof(__category_t)));
112         if (!c) {
113           LOG(ERROR) << "Out of memory";
114           g_list_free_full(category_list, &ClearCategoryDetail);
115           return false;
116         }
117         c->name = strdup(category);
118         if (!c->name) {
119           LOG(ERROR) << "Out of memory";
120           free(c);
121           g_list_free_full(category_list, &ClearCategoryDetail);
122           return false;
123         }
124         category_list = g_list_append(category_list, c);
125       }
126     }
127
128     // skip application if it has no given category
129     if (!category_list) {
130       if (action_type != ActionType::Upgrade)
131         continue;
132       auto iter = appid_list.find(app->appid);
133       if (iter != appid_list.end()) {
134         name = GetFunctionName(ActionType::Removed);
135         appid_list.erase(iter);
136         category_list = nullptr;
137       } else {
138         continue;
139       }
140     } else {
141       name = GetFunctionName(action_type);
142       if (!AddPluginInfo(manifest, app->appid)) {
143         g_list_free_full(category_list, &ClearCategoryDetail);
144         return false;
145       }
146       appid_list.erase(app->appid);
147     }
148     int result = 0;
149     Exec(name, &result, manifest->package, app->appid, category_list);
150     if (result) {
151       LOG(ERROR) << "Function: " << name << " of plugin "
152                  << plugin_info_.path() << " failed";
153       g_list_free_full(category_list, &ClearCategoryDetail);
154       return false;
155     }
156     g_list_free_full(category_list, &ClearCategoryDetail);
157   }
158
159   if (action_type != ActionType::Upgrade)
160     return true;
161
162   name = GetFunctionName(ActionType::Removed);
163   for (const auto& appid : appid_list) {
164     int result = 0;
165     Exec(name, &result, manifest->package, appid, nullptr);
166     if (result) {
167       LOG(ERROR) << "Function: " << name << " of plugin "
168                 << plugin_info_.path() << " failed";
169       return false;
170     }
171   }
172   return true;
173 }
174
175 }  // namespace common_installer