Add minimalistic protected-headers support
[gpgol.git] / src / cpphelp.cpp
1 /* @file cpphelp.h
2  * @brief Common cpp helper stuff
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 #ifdef HAVE_CONFIG_H
23 # include "config.h"
24 #endif
25
26 #include "cpphelp.h"
27
28 #include <algorithm>
29 #include <sstream>
30 #include <vector>
31 #include <iterator>
32
33 #include "common_indep.h"
34
35 #include <gpgme++/context.h>
36 #include <gpgme++/error.h>
37 #include <gpgme++/configuration.h>
38
39 #ifdef HAVE_W32_SYSTEM
40 # include "common.h"
41 # include <windows.h>
42 #else
43 #include "common_indep.h"
44 #endif
45
46 void
47 release_cArray (char **carray)
48 {
49   if (carray)
50     {
51       for (int idx = 0; carray[idx]; idx++)
52         {
53           xfree (carray[idx]);
54         }
55       xfree (carray);
56     }
57 }
58
59 void
60 rtrim(std::string &s)
61 {
62   s.erase (std::find_if (s.rbegin(), s.rend(), [] (int ch) {
63       return !std::isspace(ch);
64   }).base(), s.end());
65 }
66
67 void
68 ltrim(std::string &s)
69 {
70   s.erase (s.begin(), std::find_if (s.begin(), s.end(), [] (int ch) {
71       return !std::isspace(ch);
72   }));
73 }
74
75 void
76 trim(std::string &s)
77 {
78   ltrim (s);
79   rtrim (s);
80 }
81
82 void
83 remove_whitespace (std::string &s)
84 {
85   s.erase(remove_if(s.begin(), s.end(), isspace), s.end());
86 }
87
88 void
89 join(const std::vector<std::string>& v, const char *c, std::string& s)
90 {
91   s.clear();
92   for (auto p = v.begin(); p != v.end(); ++p)
93     {
94       s += *p;
95       if (p != v.end() - 1)
96         {
97           s += c;
98         }
99     }
100 }
101
102 char **
103 vector_to_cArray(const std::vector<std::string> &vec)
104 {
105   char ** ret = (char**) xmalloc (sizeof (char*) * (vec.size() + 1));
106   for (size_t i = 0; i < vec.size(); i++)
107     {
108       ret[i] = xstrdup (vec[i].c_str());
109     }
110   ret[vec.size()] = NULL;
111   return ret;
112 }
113
114 std::vector <std::string>
115 cArray_to_vector(const char **cArray)
116 {
117   std::vector<std::string> ret;
118
119   if (!cArray)
120     {
121       return ret;
122     }
123
124   for (int i = 0; cArray[i]; i++)
125     {
126       ret.push_back (std::string (cArray[i]));
127     }
128   return ret;
129 }
130
131 bool
132 in_de_vs_mode()
133 {
134 /* We cache the values only once. A change requires restart.
135      This is because checking this is very expensive as gpgconf
136      spawns each process to query the settings. */
137   static bool checked;
138   static bool vs_mode;
139
140   if (checked)
141     {
142       return vs_mode;
143     }
144   checked = true;
145   GpgME::Error err;
146   const auto components = GpgME::Configuration::Component::load (err);
147   log_debug ("%s:%s: Checking for de-vs mode.",
148              SRCNAME, __func__);
149   if (err)
150     {
151       log_error ("%s:%s: Failed to get gpgconf components: %s",
152                  SRCNAME, __func__, err.asString ());
153       vs_mode = false;
154       return vs_mode;
155     }
156   for (const auto &component: components)
157     {
158       if (component.name () && !strcmp (component.name (), "gpg"))
159         {
160           for (const auto &option: component.options ())
161             {
162               if (option.name () && !strcmp (option.name (), "compliance") &&
163                   option.currentValue ().stringValue () &&
164 #ifdef HAVE_W32_SYSTEM
165                   !stricmp (option.currentValue ().stringValue (), "de-vs"))
166 #else
167                   !strcasecmp (option.currentValue ().stringValue (), "de-vs"))
168 #endif
169                 {
170                   log_debug ("%s:%s: Detected de-vs mode",
171                              SRCNAME, __func__);
172                   vs_mode = true;
173                   return vs_mode;
174                 }
175             }
176           vs_mode = false;
177           return vs_mode;
178         }
179     }
180   vs_mode = false;
181   return false;
182 }
183
184 #ifdef HAVE_W32_SYSTEM
185 std::map<std::string, std::string>
186 get_registry_subkeys (const char *path)
187 {
188   HKEY theKey;
189   std::map<std::string, std::string> ret;
190
191   std::string regPath = GPGOL_REGPATH;
192   regPath += "\\";
193   regPath += path;
194
195   if (RegOpenKeyEx (HKEY_CURRENT_USER,
196                     regPath.c_str (),
197                     0, KEY_ENUMERATE_SUB_KEYS | KEY_READ,
198                     &theKey) != ERROR_SUCCESS)
199     {
200       TRACEPOINT;
201       return ret;
202     }
203
204   DWORD values = 0,
205         maxValueName = 0,
206         maxValueLen = 0;
207
208   DWORD err = RegQueryInfoKey (theKey,
209                                nullptr,
210                                nullptr,
211                                nullptr,
212                                nullptr,
213                                nullptr,
214                                nullptr,
215                                &values,
216                                &maxValueName,
217                                &maxValueLen,
218                                nullptr,
219                                nullptr);
220
221   if (err != ERROR_SUCCESS)
222     {
223       TRACEPOINT;
224       RegCloseKey (theKey);
225       return ret;
226     }
227
228   /* Add space for NULL */
229   maxValueName++;
230   maxValueLen++;
231
232   char name[maxValueName + 1];
233   char value[maxValueLen + 1];
234   for (int i = 0; i < values; i++)
235     {
236       DWORD nameLen = maxValueName;
237       err = RegEnumValue (theKey, i,
238                           name,
239                           &nameLen,
240                           nullptr,
241                           nullptr,
242                           nullptr,
243                           nullptr);
244
245       if (err != ERROR_SUCCESS)
246         {
247           TRACEPOINT;
248           continue;
249         }
250
251       DWORD type;
252       DWORD valueLen = maxValueLen;
253       err = RegQueryValueEx (theKey, name,
254                              NULL, &type,
255                              (BYTE*)value, &valueLen);
256
257       if (err != ERROR_SUCCESS)
258         {
259           TRACEPOINT;
260           continue;
261         }
262       if (type != REG_SZ)
263         {
264           TRACEPOINT;
265           continue;
266         }
267       ret.insert (std::make_pair (std::string (name, nameLen),
268                                   std::string (value, valueLen)));
269     }
270   RegCloseKey (theKey);
271   return ret;
272 }
273 #endif
274
275 template<typename Out> void
276 internal_split (const std::string &s, char delim, Out result) {
277   std::stringstream ss(s);
278   std::string item;
279   while (std::getline (ss, item, delim))
280     {
281       *(result++) = item;
282     }
283 }
284
285 std::vector<std::string>
286 gpgol_split (const std::string &s, char delim)
287 {
288   std::vector<std::string> elems;
289   internal_split (s, delim, std::back_inserter (elems));
290   return elems;
291 }
292
293 std::string
294 string_to_hex(const std::string& input)
295 {
296     static const char* const lut = "0123456789ABCDEF";
297     size_t len = input.length();
298
299     std::string output;
300     output.reserve (3 * len + (len * 3 / 26));
301     for (size_t i = 0; i < len; ++i)
302     {
303         const unsigned char c = input[i];
304         output.push_back (lut[c >> 4]);
305         output.push_back (lut[c & 15]);
306         output.push_back (' ');
307         if (i % 26 == 0)
308           {
309             output.push_back ('\n');
310           }
311     }
312     return output;
313 }
314
315 bool
316 is_binary (const std::string &input)
317 {
318   for (int i = 0; i < input.size() - 1; ++i)
319     {
320       const unsigned char c = input[i];
321       if (c < 32 && c != 0x0d && c != 0x0a)
322         {
323           return true;
324         }
325     }
326   return false;
327 }
328
329 const char *
330 to_cstr (const GpgME::Protocol &prot)
331 {
332   return prot == GpgME::CMS ? "S/MIME" :
333          prot == GpgME::OpenPGP ? "OpenPGP" :
334          "Unknown Protocol";
335 }