Add minimalistic protected-headers support
[gpgol.git] / src / debug.cpp
1 /* debug.cpp - Debugging / Log helpers for GpgOL
2  * Copyright (C) 2018 by by Intevation GmbH
3  *
4  * This file is part of GpgOL.
5  *
6  * GpgOL is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public License
8  * as published by the Free Software Foundation; either version 2.1
9  * of the License, or (at your option) any later version.
10  *
11  * GpgOL is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include "common_indep.h"
21
22 #include <gpg-error.h>
23
24 #include <string>
25 #include <unordered_map>
26
27 /* The malloced name of the logfile and the logging stream.  If
28    LOGFILE is NULL, no logging is done. */
29 static char *logfile;
30 static FILE *logfp;
31
32 GPGRT_LOCK_DEFINE (log_lock);
33
34 /* Acquire the mutex for logging.  Returns 0 on success. */
35 static int
36 lock_log (void)
37 {
38   gpgrt_lock_lock (&log_lock);
39   return 0;
40 }
41
42 /* Release the mutex for logging. No error return is done because this
43    is a fatal error anyway and we have no means for proper
44    notification. */
45 static void
46 unlock_log (void)
47 {
48   gpgrt_lock_unlock (&log_lock);
49 }
50
51 const char *
52 get_log_file (void)
53 {
54   return logfile? logfile : "";
55 }
56
57 void
58 set_log_file (const char *name)
59 {
60   if (!lock_log ())
61     {
62       if (logfp)
63         {
64           fclose (logfp);
65           logfp = NULL;
66         }
67       free (logfile);
68       if (!name || *name == '\"' || !*name)
69         logfile = NULL;
70       else
71         logfile = strdup (name);
72
73       unlock_log ();
74     }
75 }
76
77 static void
78 do_log (const char *fmt, va_list a, int w32err, int err,
79         const void *buf, size_t buflen)
80 {
81   if (!logfile)
82     return;
83
84 #ifdef HAVE_W32_SYSTEM
85   if (!opt.enable_debug)
86     return;
87
88   if (lock_log ())
89     {
90       OutputDebugStringA ("GpgOL: Failed to log.");
91       return;
92     }
93 #endif
94
95   if (!strcmp (logfile, "stdout"))
96     {
97       logfp = stdout;
98     }
99   else if (!strcmp (logfile, "stderr"))
100     {
101       logfp = stderr;
102     }
103   if (!logfp)
104     logfp = fopen (logfile, "a+");
105 #ifdef HAVE_W32_SYSTEM
106   if (!logfp)
107     {
108       unlock_log ();
109       return;
110     }
111
112   char time_str[9];
113   SYSTEMTIME utc_time;
114   GetSystemTime (&utc_time);
115   if (GetTimeFormatA (LOCALE_INVARIANT,
116                       TIME_FORCE24HOURFORMAT | LOCALE_USE_CP_ACP,
117                       &utc_time,
118                       "HH:mm:ss",
119                       time_str,
120                       9))
121     {
122       fprintf (logfp, "%s/%lu/",
123                time_str,
124                (unsigned long)GetCurrentThreadId ());
125     }
126   else
127     {
128       fprintf (logfp, "unknown/%lu/",
129                (unsigned long)GetCurrentThreadId ());
130     }
131 #endif
132
133   if (err == 1)
134     fputs ("ERROR/", logfp);
135   vfprintf (logfp, fmt, a);
136 #ifdef HAVE_W32_SYSTEM
137   if (w32err)
138     {
139       char tmpbuf[256];
140
141       FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, w32err,
142                      MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
143                      tmpbuf, sizeof (tmpbuf)-1, NULL);
144       fputs (": ", logfp);
145       if (*tmpbuf && tmpbuf[strlen (tmpbuf)-1] == '\n')
146         tmpbuf[strlen (tmpbuf)-1] = 0;
147       if (*tmpbuf && tmpbuf[strlen (tmpbuf)-1] == '\r')
148         tmpbuf[strlen (tmpbuf)-1] = 0;
149       fprintf (logfp, "%s (%d)", tmpbuf, w32err);
150     }
151 #else
152   (void) w32err;
153 #endif
154   if (buf)
155     {
156       const unsigned char *p = (const unsigned char*)buf;
157
158       for ( ; buflen; buflen--, p++)
159         fprintf (logfp, "%02X", *p);
160       putc ('\n', logfp);
161     }
162   else if ( *fmt && fmt[strlen (fmt) - 1] != '\n')
163     putc ('\n', logfp);
164
165   fflush (logfp);
166 #ifdef HAVE_W32_SYSTEM
167   unlock_log ();
168 #endif
169 }
170
171 const char *
172 log_srcname (const char *file)
173 {
174   const char *s = strrchr (file, '/');
175   return s? s+1:file;
176 }
177
178 void
179 log_debug (const char *fmt, ...)
180 {
181   va_list a;
182
183   va_start (a, fmt);
184   do_log (fmt, a, 0, 0, NULL, 0);
185   va_end (a);
186 }
187
188 void
189 log_error (const char *fmt, ...)
190 {
191   va_list a;
192
193   va_start (a, fmt);
194   do_log (fmt, a, 0, 1, NULL, 0);
195   va_end (a);
196 }
197
198 void
199 log_vdebug (const char *fmt, va_list a)
200 {
201   do_log (fmt, a, 0, 0, NULL, 0);
202 }
203
204 void
205 log_hexdump (const void *buf, size_t buflen, const char *fmt, ...)
206 {
207   va_list a;
208
209   va_start (a, fmt);
210   do_log (fmt, a, 0, 2, buf, buflen);
211   va_end (a);
212 }
213
214 #ifdef HAVE_W32_SYSTEM
215 void
216 log_debug_w32 (int w32err, const char *fmt, ...)
217 {
218   va_list a;
219
220   if (w32err == -1)
221     w32err = GetLastError ();
222
223   va_start (a, fmt);
224   do_log (fmt, a, w32err, 0, NULL, 0);
225   va_end (a);
226 }
227
228 void
229 log_error_w32 (int w32err, const char *fmt, ...)
230 {
231   va_list a;
232
233   if (w32err == -1)
234     w32err = GetLastError ();
235
236   va_start (a, fmt);
237   do_log (fmt, a, w32err, 1, NULL, 0);
238   va_end (a);
239 }
240
241 static void
242 do_log_window_info (HWND window, int level)
243 {
244   char buf[1024+1];
245   char name[200];
246   int nname;
247   char *pname;
248   DWORD pid;
249
250   if (!window)
251     return;
252
253   GetWindowThreadProcessId (window, &pid);
254   if (pid != GetCurrentProcessId ())
255     return;
256
257   memset (buf, 0, sizeof (buf));
258   GetWindowText (window, buf, sizeof (buf)-1);
259   nname = GetClassName (window, name, sizeof (name)-1);
260   if (nname)
261     pname = name;
262   else
263     pname = NULL;
264
265   if (level == -1)
266     log_debug ("  parent=%p/%lu (%s) `%s'", window, (unsigned long)pid,
267                pname? pname:"", buf);
268   else
269     log_debug ("    %*shwnd=%p/%lu (%s) `%s'", level*2, "", window,
270                (unsigned long)pid, pname? pname:"", buf);
271 }
272
273
274 /* Helper to log_window_hierarchy.  */
275 static HWND
276 do_log_window_hierarchy (HWND parent, int level)
277 {
278   HWND child;
279
280   child = GetWindow (parent, GW_CHILD);
281   while (child)
282     {
283       do_log_window_info (child, level);
284       do_log_window_hierarchy (child, level+1);
285       child = GetNextWindow (child, GW_HWNDNEXT);
286     }
287
288   return NULL;
289 }
290
291
292 /* Print a debug message using the format string FMT followed by the
293    window hierarchy of WINDOW.  */
294 void
295 log_window_hierarchy (HWND window, const char *fmt, ...)
296 {
297   va_list a;
298
299   va_start (a, fmt);
300   do_log (fmt, a, 0, 0, NULL, 0);
301   va_end (a);
302   if (window)
303     {
304       do_log_window_info (window, -1);
305       do_log_window_hierarchy (window, 0);
306     }
307 }
308 #endif
309
310 GPGRT_LOCK_DEFINE (anon_str_lock);
311
312 /* Weel ok this survives unload but we don't want races
313    and it makes a bit of sense to keep the strings constant. */
314 static std::unordered_map<std::string, std::string> str_map;
315
316 const char *anonstr (const char *data)
317 {
318   static int64_t cnt;
319   if (opt.enable_debug & DBG_DATA)
320     {
321       return data;
322     }
323   if (!data)
324     {
325       return "gpgol_str_null";
326     }
327   if (!strlen (data))
328     {
329       return "gpgol_str_empty";
330     }
331
332   gpgol_lock (&anon_str_lock);
333   const std::string strData (data);
334   auto it = str_map.find (strData);
335
336   if (it == str_map.end ())
337     {
338       const auto anon = std::string ("gpgol_string_") + std::to_string (++cnt);
339       str_map.insert (std::make_pair (strData, anon));
340       it = str_map.find (strData);
341     }
342
343   // As the data is saved in our map we can return
344   // the c_str as it won't be touched as const.
345
346   gpgol_unlock (&anon_str_lock);
347
348   return it->second.c_str();
349 }