41ada9153967cc5705d404677234dad6f6a1c0ec
[gpgme.git] / assuan / assuan-logging.c
1 /* assuan-logging.c - Default logging function.
2  * Copyright (C) 2002, 2003, 2004, 2007 Free Software Foundation, Inc.
3  *
4  * This file is part of Assuan.
5  *
6  * Assuan is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU Lesser General Public License as
8  * published by the Free Software Foundation; either version 2.1 of
9  * the License, or (at your option) any later version.
10  *
11  * Assuan is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
19  * USA. 
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <stdarg.h>
29 #ifdef HAVE_W32_SYSTEM
30 #include <windows.h>
31 #endif /*HAVE_W32_SYSTEM*/
32 #include <errno.h>
33 #include <ctype.h>
34
35 #include "assuan-defs.h"
36
37 static char prefix_buffer[80];
38 static FILE *_assuan_log;
39 static int full_logging;
40 static int log_level = 1;  /* Defaults to logging enabled.  */
41
42
43 /* Set the log level for general assuan commands.  0 is no logging at
44    all, 1 is the standard logging and the default. Higher leveles may
45    be defined in the future.  Passing a level of -1 will not change
46    the current log level.  Returns previosu log level.  */
47 int
48 assuan_set_assuan_log_level (int level)
49 {
50   int old = log_level;
51
52   if (level != -1)
53     log_level = level;
54   return old;
55 }
56
57
58 void
59 _assuan_set_default_log_stream (FILE *fp)
60 {
61   if (!_assuan_log)
62     {
63       _assuan_log = fp;
64       full_logging = !!getenv ("ASSUAN_FULL_LOGGING");
65     }
66 }
67
68 void
69 assuan_set_assuan_log_stream (FILE *fp)
70 {
71   _assuan_log = fp;
72 }
73
74
75 /* Set the per context log stream.  Also enable the default log stream
76    if it has not been set.  */
77 void
78 assuan_set_log_stream (assuan_context_t ctx, FILE *fp)
79 {
80   if (ctx)
81     {
82       if (ctx->log_fp)
83         fflush (ctx->log_fp);
84       ctx->log_fp = fp;
85       _assuan_set_default_log_stream (fp);
86     }
87 }
88
89
90 FILE *
91 assuan_get_assuan_log_stream (void)
92 {
93   return _assuan_log ? _assuan_log : stderr;
94 }
95
96
97 /* Set the prefix to be used for logging to TEXT or
98    resets it to the default if TEXT is NULL. */
99 void
100 assuan_set_assuan_log_prefix (const char *text)
101 {
102   if (text)
103     {
104       strncpy (prefix_buffer, text, sizeof (prefix_buffer)-1);
105       prefix_buffer[sizeof (prefix_buffer)-1] = 0;
106     }
107   else
108     *prefix_buffer = 0;
109 }
110
111 const char *
112 assuan_get_assuan_log_prefix (void)
113 {
114   return prefix_buffer;
115 }
116
117
118 void
119 _assuan_log_printf (const char *format, ...)
120 {
121   va_list arg_ptr;
122   FILE *fp;
123   const char *prf;
124   int save_errno = errno;
125
126   if (!log_level)
127     return;
128   
129   fp = assuan_get_assuan_log_stream ();
130   prf = assuan_get_assuan_log_prefix ();
131   if (*prf)
132     fprintf (fp, "%s[%u]: ", prf, (unsigned int)getpid ());
133
134   va_start (arg_ptr, format);
135   vfprintf (fp, format, arg_ptr );
136   va_end (arg_ptr);
137   errno = save_errno;
138 }
139
140
141 /* Dump a possibly binary string (used for debugging).  Distinguish
142    ascii text from binary and print it accordingly.  This function
143    takes FILE pointer arg becuase logging may be enabled on a per
144    context basis. */
145 void
146 _assuan_log_print_buffer (FILE *fp, const void *buffer, size_t length)
147 {
148   const unsigned char *s;
149   int n;
150
151   if (!log_level)
152     return;
153
154   for (n=length,s=buffer; n; n--, s++)
155     if  ((!isascii (*s) || iscntrl (*s) || !isprint (*s)) && !(*s >= 0x80))
156       break;
157
158   s = buffer;
159   if (!n && *s != '[')
160     fwrite (buffer, length, 1, fp);
161   else
162     {
163 #ifdef HAVE_FLOCKFILE
164       flockfile (fp);
165 #endif
166       putc_unlocked ('[', fp);
167       if ( length > 16 && !full_logging)
168         {
169           for (n=0; n < 12; n++, s++)
170             fprintf (fp, " %02x", *s);
171           fprintf (fp, " ...(%d bytes skipped)", (int)length - 12);
172         }
173       else
174         {
175           for (n=0; n < length; n++, s++)
176             fprintf (fp, " %02x", *s);
177         }
178       putc_unlocked (' ', fp);
179       putc_unlocked (']', fp);
180 #ifdef HAVE_FUNLOCKFILE
181       funlockfile (fp);
182 #endif
183     }
184 }
185
186 /* Log a user supplied string.  Escapes non-printable before
187    printing.  */
188 void
189 _assuan_log_sanitized_string (const char *string)
190 {
191   const unsigned char *s = (const unsigned char *) string;
192   FILE *fp;
193
194   if (!log_level)
195     return;
196
197   if (!*s)
198     return;
199
200   fp = assuan_get_assuan_log_stream ();
201
202 #ifdef HAVE_FLOCKFILE
203   flockfile (fp);
204 #endif
205
206   for (; *s; s++)
207     {
208       int c = 0;
209
210       switch (*s)
211         {
212         case '\r':
213           c = 'r';
214           break;
215
216         case '\n':
217           c = 'n';
218           break;
219
220         case '\f':
221           c = 'f';
222           break;
223
224         case '\v':
225           c = 'v';
226           break;
227
228         case '\b':
229           c = 'b';
230           break;
231
232         default:
233           if ((isascii (*s) && isprint (*s)) || (*s >= 0x80))
234             putc_unlocked (*s, fp);
235           else
236             {
237               putc_unlocked ('\\', fp);
238               fprintf (fp, "x%02x", *s);
239             }
240         }
241
242       if (c)
243         {
244           putc_unlocked ('\\', fp);
245           putc_unlocked (c, fp);
246         }
247     }
248
249 #ifdef HAVE_FUNLOCKFILE
250   funlockfile (fp);
251 #endif
252 }
253
254
255
256 #ifdef HAVE_W32_SYSTEM
257 const char *
258 _assuan_w32_strerror (int ec)
259 {
260   static char strerr[256];
261   
262   if (ec == -1)
263     ec = (int)GetLastError ();
264   FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, ec,
265                  MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
266                  strerr, sizeof (strerr)-1, NULL);
267   return strerr;    
268 }
269
270 static int (*my_strerror_r) (unsigned int err, char *buf, size_t buflen);
271 static const char * (*my_strsource) (unsigned int err);
272
273 static int
274 load_libgpg_error (void)
275 {
276   /* This code is not race free but suitable for our purpose.  */
277   static volatile int initialized;
278   void *handle;
279
280   if (initialized)
281     return (my_strerror_r && my_strsource)? 0:-1;
282   handle = LoadLibrary ("libgpg-error-0.dll");
283   if (handle)
284     {
285       void *foo, *bar;
286       foo = GetProcAddress (handle, "gpg_strerror_r");
287       bar = GetProcAddress (handle, "gpg_strsource");
288       if (foo && bar)
289         {
290           my_strerror_r = foo;
291           my_strsource = bar;
292         }
293       else
294         CloseHandle (handle);
295     }
296   initialized = 1;
297   return 0;
298 }
299
300 int
301 _assuan_gpg_strerror_r (unsigned int err, char *buf, size_t buflen)
302 {
303   if (load_libgpg_error ())
304     return -1;
305   return my_strerror_r (err, buf, buflen);
306 }
307
308
309 const char *
310 _assuan_gpg_strsource (unsigned int err)
311 {
312   if (load_libgpg_error ())
313     return NULL;
314   return my_strsource (err);
315 }
316 #endif /*HAVE_W32_SYSTEM*/