Update german translation
[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 #undef _
31 #define _(a) utf8_gettext (a)
32
33 class CategoryManager::Private
34 {
35 public:
36   Private()
37   {
38   }
39
40   void createCategory (shared_disp_t store,
41                        const std::string &category, int color)
42     {
43       TSTART;
44       LPDISPATCH categories = get_oom_object (store.get(), "Categories");
45       if (!categories)
46         {
47           STRANGEPOINT;
48           TRETURN;
49         }
50       if (create_category (categories, category.c_str (), color))
51         {
52           log_debug ("%s:%s: Failed to create category %s",
53                      SRCNAME, __func__, anonstr (category.c_str()));
54           gpgol_release (categories);
55           TRETURN;
56         }
57       gpgol_release (categories);
58       TRETURN;
59     }
60
61   void registerCategory (const std::string &storeID,
62                          const std::string &category)
63     {
64       TSTART;
65       auto storeIt = mCategoryStoreMap.find (storeID);
66       if (storeIt == mCategoryStoreMap.end())
67         {
68           /* First category for this store. Create a new
69              category ref map. */
70           std::unordered_map <std::string, int> categoryMap;
71           categoryMap.insert (std::make_pair (category, 1));
72           mCategoryStoreMap.insert (std::make_pair (storeID, categoryMap));
73           log_debug ("%s:%s: Register category %s in new store %s ref now 1",
74                      SRCNAME, __func__, anonstr (category.c_str()),
75                      anonstr (storeID.c_str()));
76           TRETURN;
77         }
78       auto categoryIt = storeIt->second.find (category);
79       if (categoryIt == storeIt->second.end ())
80         {
81           storeIt->second.insert (std::make_pair (category, 1));
82           log_debug ("%s:%s: Register category %s in store %s ref now 1",
83                      SRCNAME, __func__, anonstr (category.c_str()),
84                      anonstr (storeID.c_str()));
85         }
86       else
87         {
88           categoryIt->second++;
89           log_debug ("%s:%s: Register category %s in store %s ref now %i",
90                      SRCNAME, __func__, anonstr (category.c_str()),
91                      anonstr (storeID.c_str()), categoryIt->second);
92         }
93       TRETURN;
94     }
95
96   void unregisterCategory (const std::string &storeID,
97                            const std::string &category)
98     {
99       TSTART;
100       auto storeIt = mCategoryStoreMap.find (storeID);
101       if (storeIt == mCategoryStoreMap.end ())
102         {
103           log_error ("%s:%s: Unregister called for unregistered store %s",
104                      SRCNAME, __func__, anonstr (storeID.c_str()));
105           TRETURN;
106         }
107       auto categoryIt = storeIt->second.find (category);
108       if (categoryIt == storeIt->second.end ())
109         {
110           log_debug ("%s:%s: Unregister %s not found for store %s",
111                      SRCNAME, __func__, anonstr (category.c_str()),
112                      anonstr (storeID.c_str()));
113           TRETURN;
114         }
115       categoryIt->second--;
116       log_debug ("%s:%s: Unregister category %s in store %s ref now %i",
117                  SRCNAME, __func__, anonstr (category.c_str()),
118                  anonstr (storeID.c_str()), categoryIt->second);
119       if (categoryIt->second < 0)
120         {
121           log_debug ("%s:%s: Unregister %s negative for store %s",
122                      SRCNAME, __func__, anonstr (category.c_str()),
123                      anonstr (storeID.c_str()));
124           TRETURN;
125         }
126       if (categoryIt->second == 0)
127         {
128           log_debug ("%s:%s: Deleting %s for store %s",
129                      SRCNAME, __func__, anonstr (category.c_str()),
130                      anonstr (storeID.c_str()));
131
132           LPDISPATCH store = get_store_for_id (storeID.c_str());
133           if (!store)
134             {
135               STRANGEPOINT;
136               TRETURN;
137             }
138           delete_category (store, category.c_str ());
139           gpgol_release (store);
140           storeIt->second.erase (categoryIt);
141         }
142       TRETURN;
143     }
144
145   bool categoryExistsInMap (const std::string &storeID,
146                             const std::string &category)
147     {
148       const auto it = mCategoryStoreMap.find (storeID);
149       if (it == mCategoryStoreMap.end ())
150         {
151           return false;
152         }
153       return it->second.find (category) != it->second.end();
154     }
155
156 private:
157   /* Map from: store to map of category -> refs. */
158   std::unordered_map<std::string,
159     std::unordered_map<std::string, int> > mCategoryStoreMap;
160 };
161
162 /* static */
163 std::shared_ptr <CategoryManager>
164 CategoryManager::instance ()
165 {
166   return GpgolAddin::get_instance ()->get_category_mngr ();
167 }
168
169 CategoryManager::CategoryManager():
170   d(new Private)
171 {
172 }
173
174 std::string
175 CategoryManager::addCategoryToMail (Mail *mail, const std::string &category, int color)
176 {
177   TSTART;
178   std::string ret;
179   if (!mail || category.empty())
180     {
181       TRETURN ret;
182     }
183
184   auto store = MAKE_SHARED (get_oom_object (mail->item (), "Parent.Store"));
185   if (!store)
186     {
187       log_error ("%s:%s Failed to obtain store",
188                  SRCNAME, __func__);
189       TRETURN std::string ();
190     }
191   char *storeID = get_oom_string (store.get (), "StoreID");
192   if (!storeID)
193     {
194       log_error ("%s:%s Failed to obtain storeID",
195                  SRCNAME, __func__);
196       TRETURN std::string ();
197     }
198   ret = storeID;
199   xfree (storeID);
200
201   if (!d->categoryExistsInMap (ret, category))
202     {
203       d->createCategory (store, category, color);
204     }
205   d->registerCategory (ret, category);
206
207   if (add_category (mail->item (), category.c_str()))
208     {
209       /* Probably the category already existed there
210          so it is not super worrysome. */
211       log_debug ("%s:%s Failed to add category.",
212                  SRCNAME, __func__);
213     }
214   return ret;
215 }
216
217 void
218 CategoryManager::removeCategory (Mail *mail, const std::string &category)
219 {
220   TSTART;
221   if (!mail || category.empty())
222     {
223       STRANGEPOINT;
224       TRETURN;
225     }
226   if (remove_category (mail->item (), category.c_str (), true))
227     {
228       log_debug ("%s:%s Failed to remove category.",
229                  SRCNAME, __func__);
230     }
231   d->unregisterCategory (mail->storeID (), category.c_str ());
232
233   TRETURN;
234 }
235
236 /* static */
237 void
238 CategoryManager::removeAllGpgOLCategories ()
239 {
240   TSTART;
241   delete_all_categories_starting_with ("GpgOL: ");
242   TRETURN;
243 }
244
245 /* static */
246 const std::string &
247 CategoryManager::getEncMailCategory ()
248 {
249   static std::string decStr;
250   if (decStr.empty())
251     {
252       decStr = std::string ("GpgOL: ") +
253                             std::string (_("Encrypted Message"));
254     }
255   return decStr;
256 }
257
258 /* static */
259 const std::string &
260 CategoryManager::getJunkMailCategory ()
261 {
262   static std::string decStr;
263   if (decStr.empty())
264     {
265       decStr = std::string ("GpgOL: ") +
266                             std::string (_("Junk Email cannot be processed"));
267     }
268   return decStr;
269 }
270
271 /* static */
272 const std::string&
273 CategoryManager::getSeperator ()
274 {
275   /* This is fun. I have no idea what
276     all breaks if this is changed at runtime so
277     we check it only once per run.*/
278   static std::string sep = readRegStr ("HKEY_CURRENT_USER",
279                                        "Control Panel\\International",
280                                        "sList");
281   if (sep.empty ())
282     {
283       sep = std::string (",");
284     }
285   return sep;
286 }