Fix memleaks in new category code
[gpgol.git] / src / categorymanager.cpp
1 /* @file categorymanager.cpp
2  * @brief Handles category management
3  *
4  * Copyright (C) 2018 Intevation GmbH
5  *
6  * This file is part of GpgOL.
7  *
8  * GpgOL is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * GpgOL is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this program; if not, see <http://www.gnu.org/licenses/>.
20  */
21
22 #include "categorymanager.h"
23 #include "common.h"
24 #include "mail.h"
25 #include "gpgoladdin.h"
26 #include "oomhelp.h"
27
28 #include <unordered_map>
29
30 class CategoryManager::Private
31 {
32 public:
33   Private()
34   {
35   }
36
37   void createCategory (shared_disp_t store,
38                        const std::string &category, int color)
39     {
40       TSTART;
41       LPDISPATCH categories = get_oom_object (store.get(), "Categories");
42       if (!categories)
43         {
44           STRANGEPOINT;
45           TRETURN;
46         }
47       if (create_category (categories, category.c_str (), color))
48         {
49           log_debug ("%s:%s: Failed to create category %s",
50                      SRCNAME, __func__, anonstr (category.c_str()));
51           gpgol_release (categories);
52           TRETURN;
53         }
54       gpgol_release (categories);
55       TRETURN;
56     }
57
58   void registerCategory (const std::string &storeID,
59                          const std::string &category)
60     {
61       TSTART;
62       auto storeIt = mCategoryStoreMap.find (storeID);
63       if (storeIt == mCategoryStoreMap.end())
64         {
65           /* First category for this store. Create a new
66              category ref map. */
67           std::unordered_map <std::string, int> categoryMap;
68           categoryMap.insert (std::make_pair (category, 1));
69           mCategoryStoreMap.insert (std::make_pair (storeID, categoryMap));
70           log_debug ("%s:%s: Register category %s in new store %s ref now 1",
71                      SRCNAME, __func__, anonstr (category.c_str()),
72                      anonstr (storeID.c_str()));
73           TRETURN;
74         }
75       auto categoryIt = storeIt->second.find (category);
76       if (categoryIt == storeIt->second.end ())
77         {
78           storeIt->second.insert (std::make_pair (category, 1));
79           log_debug ("%s:%s: Register category %s in store %s ref now 1",
80                      SRCNAME, __func__, anonstr (category.c_str()),
81                      anonstr (storeID.c_str()));
82         }
83       else
84         {
85           categoryIt->second++;
86           log_debug ("%s:%s: Register category %s in store %s ref now %i",
87                      SRCNAME, __func__, anonstr (category.c_str()),
88                      anonstr (storeID.c_str()), categoryIt->second);
89         }
90       TRETURN;
91     }
92
93   void unregisterCategory (const std::string &storeID,
94                            const std::string &category)
95     {
96       TSTART;
97       auto storeIt = mCategoryStoreMap.find (storeID);
98       if (storeIt == mCategoryStoreMap.end ())
99         {
100           log_error ("%s:%s: Unregister called for unregistered store %s",
101                      SRCNAME, __func__, anonstr (storeID.c_str()));
102           TRETURN;
103         }
104       auto categoryIt = storeIt->second.find (category);
105       if (categoryIt == storeIt->second.end ())
106         {
107           log_debug ("%s:%s: Unregister %s not found for store %s",
108                      SRCNAME, __func__, anonstr (category.c_str()),
109                      anonstr (storeID.c_str()));
110           TRETURN;
111         }
112       categoryIt->second--;
113       log_debug ("%s:%s: Unregister category %s in store %s ref now %i",
114                  SRCNAME, __func__, anonstr (category.c_str()),
115                  anonstr (storeID.c_str()), categoryIt->second);
116       if (categoryIt->second < 0)
117         {
118           log_debug ("%s:%s: Unregister %s negative for store %s",
119                      SRCNAME, __func__, anonstr (category.c_str()),
120                      anonstr (storeID.c_str()));
121           TRETURN;
122         }
123       if (categoryIt->second == 0)
124         {
125           log_debug ("%s:%s: Deleting %s for store %s",
126                      SRCNAME, __func__, anonstr (category.c_str()),
127                      anonstr (storeID.c_str()));
128
129           LPDISPATCH store = get_store_for_id (storeID.c_str());
130           if (!store)
131             {
132               STRANGEPOINT;
133               TRETURN;
134             }
135           delete_category (store, category.c_str ());
136           gpgol_release (store);
137           storeIt->second.erase (categoryIt);
138         }
139       TRETURN;
140     }
141
142   bool categoryExistsInMap (const std::string &storeID,
143                             const std::string &category)
144     {
145       const auto it = mCategoryStoreMap.find (storeID);
146       if (it == mCategoryStoreMap.end ())
147         {
148           return false;
149         }
150       return it->second.find (category) != it->second.end();
151     }
152
153 private:
154   /* Map from: store to map of category -> refs. */
155   std::unordered_map<std::string,
156     std::unordered_map<std::string, int> > mCategoryStoreMap;
157 };
158
159 /* static */
160 std::shared_ptr <CategoryManager>
161 CategoryManager::instance ()
162 {
163   return GpgolAddin::get_instance ()->get_category_mngr ();
164 }
165
166 CategoryManager::CategoryManager():
167   d(new Private)
168 {
169 }
170
171 std::string
172 CategoryManager::addCategoryToMail (Mail *mail, const std::string &category, int color)
173 {
174   TSTART;
175   std::string ret;
176   if (!mail || category.empty())
177     {
178       TRETURN ret;
179     }
180
181   auto store = MAKE_SHARED (get_oom_object (mail->item (), "Parent.Store"));
182   if (!store)
183     {
184       log_error ("%s:%s Failed to obtain store",
185                  SRCNAME, __func__);
186       TRETURN std::string ();
187     }
188   char *storeID = get_oom_string (store.get (), "StoreID");
189   if (!storeID)
190     {
191       log_error ("%s:%s Failed to obtain storeID",
192                  SRCNAME, __func__);
193       TRETURN std::string ();
194     }
195   ret = storeID;
196   xfree (storeID);
197
198   if (!d->categoryExistsInMap (ret, category))
199     {
200       d->createCategory (store, category, color);
201     }
202   d->registerCategory (ret, category);
203
204   if (add_category (mail->item (), category.c_str()))
205     {
206       /* Probably the category already existed there
207          so it is not super worrysome. */
208       log_debug ("%s:%s Failed to add category.",
209                  SRCNAME, __func__);
210     }
211   return ret;
212 }
213
214 void
215 CategoryManager::removeCategory (Mail *mail, const std::string &category)
216 {
217   TSTART;
218   if (!mail || category.empty())
219     {
220       STRANGEPOINT;
221       TRETURN;
222     }
223   if (remove_category (mail->item (), category.c_str (), true))
224     {
225       log_debug ("%s:%s Failed to remvoe category.",
226                  SRCNAME, __func__);
227     }
228   d->unregisterCategory (mail->storeID (), category.c_str ());
229
230   TRETURN;
231 }
232
233 /* static */
234 void
235 CategoryManager::removeAllGpgOLCategories ()
236 {
237   TSTART;
238   delete_all_categories_starting_with ("GpgOL: ");
239   TRETURN;
240 }
241
242 /* static */
243 const std::string &
244 CategoryManager::getEncMailCategory ()
245 {
246   static std::string decStr;
247   if (decStr.empty())
248     {
249       decStr = std::string ("GpgOL: ") +
250                             std::string (_("Encrypted Message"));
251     }
252   return decStr;
253 }