assuan/
[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, see <http://www.gnu.org/licenses/>.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <stdarg.h>
27 #ifdef HAVE_W32_SYSTEM
28 #include <windows.h>
29 #endif /*HAVE_W32_SYSTEM*/
30 #include <errno.h>
31 #include <ctype.h>
32
33 #include "assuan-defs.h"
34
35 static char prefix_buffer[80];
36 static FILE *_assuan_log;
37 static int full_logging;
38
39 void
40 _assuan_set_default_log_stream (FILE *fp)
41 {
42   if (!_assuan_log)
43     {
44       _assuan_log = fp;
45       full_logging = !!getenv ("ASSUAN_FULL_LOGGING");
46     }
47 }
48
49 void
50 assuan_set_assuan_log_stream (FILE *fp)
51 {
52   _assuan_log = fp;
53 }
54
55
56 /* Set the per context log stream.  Also enable the default log stream
57    if it has not been set.  */
58 void
59 assuan_set_log_stream (assuan_context_t ctx, FILE *fp)
60 {
61   if (ctx)
62     {
63       if (ctx->log_fp)
64         fflush (ctx->log_fp);
65       ctx->log_fp = fp;
66       _assuan_set_default_log_stream (fp);
67     }
68 }
69
70
71 FILE *
72 assuan_get_assuan_log_stream (void)
73 {
74   return _assuan_log ? _assuan_log : stderr;
75 }
76
77
78 /* Set the prefix to be used for logging to TEXT or
79    resets it to the default if TEXT is NULL. */
80 void
81 assuan_set_assuan_log_prefix (const char *text)
82 {
83   if (text)
84     {
85       strncpy (prefix_buffer, text, sizeof (prefix_buffer)-1);
86       prefix_buffer[sizeof (prefix_buffer)-1] = 0;
87     }
88   else
89     *prefix_buffer = 0;
90 }
91
92 const char *
93 assuan_get_assuan_log_prefix (void)
94 {
95   return prefix_buffer;
96 }
97
98
99 void
100 _assuan_log_printf (const char *format, ...)
101 {
102   va_list arg_ptr;
103   FILE *fp;
104   const char *prf;
105   int save_errno = errno;
106   
107   fp = assuan_get_assuan_log_stream ();
108   prf = assuan_get_assuan_log_prefix ();
109   if (*prf)
110     fprintf (fp, "%s[%u]: ", prf, (unsigned int)getpid ());
111
112   va_start (arg_ptr, format);
113   vfprintf (fp, format, arg_ptr );
114   va_end (arg_ptr);
115   /* If the log stream is a file, the output would be buffered.  This
116      is bad for debugging, thus we flush the stream if FORMAT ends
117      with a LF.  */ 
118   if (format && *format && format[strlen(format)-1] == '\n')
119     fflush (fp);
120   errno = save_errno;
121 }
122
123
124 /* Dump a possibly binary string (used for debugging).  Distinguish
125    ascii text from binary and print it accordingly.  This function
126    takes FILE pointer arg because logging may be enabled on a per
127    context basis.  */
128 void
129 _assuan_log_print_buffer (FILE *fp, const void *buffer, size_t length)
130 {
131   const unsigned char *s;
132   unsigned int n;
133
134   for (n = length, s = buffer; n; n--, s++)
135     if  ((! isascii (*s) || iscntrl (*s) || ! isprint (*s)) && !(*s >= 0x80))
136       break;
137
138   s = buffer;
139   if (! n && *s != '[')
140     fwrite (buffer, length, 1, fp);
141   else
142     {
143 #ifdef HAVE_FLOCKFILE
144       flockfile (fp);
145 #endif
146       putc_unlocked ('[', fp);
147       if (length > 16 && ! full_logging)
148         {
149           for (n = 0; n < 12; n++, s++)
150             fprintf (fp, " %02x", *s);
151           fprintf (fp, " ...(%d bytes skipped)", (int) length - 12);
152         }
153       else
154         {
155           for (n = 0; n < length; n++, s++)
156             fprintf (fp, " %02x", *s);
157         }
158       putc_unlocked (' ', fp);
159       putc_unlocked (']', fp);
160 #ifdef HAVE_FUNLOCKFILE
161       funlockfile (fp);
162 #endif
163     }
164 }
165
166 /* Log a user supplied string.  Escapes non-printable before
167    printing.  */
168 void
169 _assuan_log_sanitized_string (const char *string)
170 {
171   const unsigned char *s = (const unsigned char *) string;
172   FILE *fp = assuan_get_assuan_log_stream ();
173
174   if (! *s)
175     return;
176
177 #ifdef HAVE_FLOCKFILE
178   flockfile (fp);
179 #endif
180
181   for (; *s; s++)
182     {
183       int c = 0;
184
185       switch (*s)
186         {
187         case '\r':
188           c = 'r';
189           break;
190
191         case '\n':
192           c = 'n';
193           break;
194
195         case '\f':
196           c = 'f';
197           break;
198
199         case '\v':
200           c = 'v';
201           break;
202
203         case '\b':
204           c = 'b';
205           break;
206
207         default:
208           if ((isascii (*s) && isprint (*s)) || (*s >= 0x80))
209             putc_unlocked (*s, fp);
210           else
211             {
212               putc_unlocked ('\\', fp);
213               fprintf (fp, "x%02x", *s);
214             }
215         }
216
217       if (c)
218         {
219           putc_unlocked ('\\', fp);
220           putc_unlocked (c, fp);
221         }
222     }
223
224 #ifdef HAVE_FUNLOCKFILE
225   funlockfile (fp);
226 #endif
227 }
228
229
230
231 #ifdef HAVE_W32_SYSTEM
232 const char *
233 _assuan_w32_strerror (int ec)
234 {
235   static char strerr[256];
236   
237   if (ec == -1)
238     ec = (int)GetLastError ();
239   FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, ec,
240                  MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
241                  strerr, sizeof (strerr)-1, NULL);
242   return strerr;    
243 }
244
245 static int (*my_strerror_r) (unsigned int err, char *buf, size_t buflen);
246 static const char * (*my_strsource) (unsigned int err);
247
248 static int
249 load_libgpg_error (void)
250 {
251   /* This code is not race free but suitable for our purpose.  */
252   static volatile int initialized;
253   void *handle;
254
255   if (initialized)
256     return (my_strerror_r && my_strsource)? 0:-1;
257   handle = LoadLibrary ("libgpg-error-0.dll");
258   if (handle)
259     {
260       void *foo, *bar;
261       foo = GetProcAddress (handle, "gpg_strerror_r");
262       bar = GetProcAddress (handle, "gpg_strsource");
263       if (foo && bar)
264         {
265           my_strerror_r = foo;
266           my_strsource = bar;
267         }
268       else
269         CloseHandle (handle);
270     }
271   initialized = 1;
272   return 0;
273 }
274
275 int
276 _assuan_gpg_strerror_r (unsigned int err, char *buf, size_t buflen)
277 {
278   if (load_libgpg_error ())
279     return -1;
280   return my_strerror_r (err, buf, buflen);
281 }
282
283
284 const char *
285 _assuan_gpg_strsource (unsigned int err)
286 {
287   if (load_libgpg_error ())
288     return NULL;
289   return my_strsource (err);
290 }
291 #endif /*HAVE_W32_SYSTEM*/