Changed the demoronizer filter description.
[privoxy.git] / errlog.c
1 const char errlog_rcs[] = "$Id: errlog.c,v 1.40.2.2 2002/09/28 00:30:57 david__schmidt Exp $";
2 /*********************************************************************
3  *
4  * File        :  $Source: /cvsroot/ijbswa/current/Attic/errlog.c,v $
5  *
6  * Purpose     :  Log errors to a designated destination in an elegant,
7  *                printf-like fashion.
8  *
9  * Copyright   :  Written by and Copyright (C) 2001 the SourceForge
10  *                Privoxy team. http://www.privoxy.org/
11  *
12  *                Based on the Internet Junkbuster originally written
13  *                by and Copyright (C) 1997 Anonymous Coders and 
14  *                Junkbusters Corporation.  http://www.junkbusters.com
15  *
16  *                This program is free software; you can redistribute it 
17  *                and/or modify it under the terms of the GNU General
18  *                Public License as published by the Free Software
19  *                Foundation; either version 2 of the License, or (at
20  *                your option) any later version.
21  *
22  *                This program is distributed in the hope that it will
23  *                be useful, but WITHOUT ANY WARRANTY; without even the
24  *                implied warranty of MERCHANTABILITY or FITNESS FOR A
25  *                PARTICULAR PURPOSE.  See the GNU General Public
26  *                License for more details.
27  *
28  *                The GNU General Public License should be included with
29  *                this file.  If not, you can view it at
30  *                http://www.gnu.org/copyleft/gpl.html
31  *                or write to the Free Software Foundation, Inc., 59
32  *                Temple Place - Suite 330, Boston, MA  02111-1307, USA.
33  *
34  * Revisions   :
35  *    $Log: errlog.c,v $
36  *    Revision 1.40.2.2  2002/09/28 00:30:57  david__schmidt
37  *    Update error logging to give sane values for thread IDs on Mach kernels.
38  *    It's still a hack, but at least it looks farily normal.  We print the
39  *    absolute value of the first 4 bytes of the pthread_t modded with 1000.
40  *
41  *    Revision 1.40.2.1  2002/09/25 12:47:42  oes
42  *    Make log_error safe against NULL string arguments
43  *
44  *    Revision 1.40  2002/05/22 01:27:27  david__schmidt
45  *
46  *    Add os2_socket_strerr mirroring w32_socket_strerr.
47  *
48  *    Revision 1.39  2002/04/03 17:15:27  gliptak
49  *    zero padding thread ids in log
50  *
51  *    Revision 1.38  2002/03/31 17:18:59  jongfoster
52  *    Win32 only: Enabling STRICT to fix a VC++ compile warning.
53  *
54  *    Revision 1.37  2002/03/27 14:32:43  david__schmidt
55  *    More compiler warning message maintenance
56  *
57  *    Revision 1.36  2002/03/26 22:29:54  swa
58  *    we have a new homepage!
59  *
60  *    Revision 1.35  2002/03/24 15:23:33  jongfoster
61  *    Name changes
62  *
63  *    Revision 1.34  2002/03/24 13:25:43  swa
64  *    name change related issues
65  *
66  *    Revision 1.33  2002/03/13 00:27:04  jongfoster
67  *    Killing warnings
68  *
69  *    Revision 1.32  2002/03/07 03:46:17  oes
70  *    Fixed compiler warnings
71  *
72  *    Revision 1.31  2002/03/06 23:02:57  jongfoster
73  *    Removing tabs
74  *
75  *    Revision 1.30  2002/03/05 22:43:45  david__schmidt
76  *    - Better error reporting on OS/2
77  *    - Fix double-slash comment (oops)
78  *
79  *    Revision 1.29  2002/03/04 23:45:13  jongfoster
80  *    Printing thread ID if using Win32 native threads
81  *
82  *    Revision 1.28  2002/03/04 17:59:59  oes
83  *    Deleted deletePidFile(), cosmetics
84  *
85  *    Revision 1.27  2002/03/04 02:08:01  david__schmidt
86  *    Enable web editing of actions file on OS/2 (it had been broken all this time!)
87  *
88  *    Revision 1.26  2002/01/09 19:05:45  steudten
89  *    Fix big memory leak.
90  *
91  *    Revision 1.25  2002/01/09 14:32:08  oes
92  *    Added support for gmtime_r and localtime_r.
93  *
94  *    Revision 1.24  2001/12/30 14:07:32  steudten
95  *    - Add signal handling (unix)
96  *    - Add SIGHUP handler (unix)
97  *    - Add creation of pidfile (unix)
98  *    - Add action 'top' in rc file (RH)
99  *    - Add entry 'SIGNALS' to manpage
100  *    - Add exit message to logfile (unix)
101  *
102  *    Revision 1.23  2001/11/07 00:02:13  steudten
103  *    Add line number in error output for lineparsing for
104  *    actionsfile and configfile.
105  *    Special handling for CLF added.
106  *
107  *    Revision 1.22  2001/11/05 23:43:05  steudten
108  *    Add time+date to log files.
109  *
110  *    Revision 1.21  2001/10/25 03:40:47  david__schmidt
111  *    Change in porting tactics: OS/2's EMX porting layer doesn't allow multiple
112  *    threads to call select() simultaneously.  So, it's time to do a real, live,
113  *    native OS/2 port.  See defines for __EMX__ (the porting layer) vs. __OS2__
114  *    (native). Both versions will work, but using __OS2__ offers multi-threading.
115  *
116  *    Revision 1.20  2001/09/16 23:04:34  jongfoster
117  *    Fixing a warning
118  *
119  *    Revision 1.19  2001/09/13 20:08:06  jongfoster
120  *    Adding support for LOG_LEVEL_CGI
121  *
122  *    Revision 1.18  2001/09/10 11:27:24  oes
123  *    Declaration of w32_socket_strerr now conditional
124  *
125  *    Revision 1.17  2001/09/10 10:17:13  oes
126  *    Removed unused variable; Fixed sprintf format
127  *
128  *    Revision 1.16  2001/07/30 22:08:36  jongfoster
129  *    Tidying up #defines:
130  *    - All feature #defines are now of the form FEATURE_xxx
131  *    - Permanently turned off WIN_GUI_EDIT
132  *    - Permanently turned on WEBDAV and SPLIT_PROXY_ARGS
133  *
134  *    Revision 1.15  2001/07/29 17:41:10  jongfoster
135  *    Now prints thread ID for each message (pthreads only)
136  *
137  *    Revision 1.14  2001/07/19 19:03:48  haroon
138  *    - Added case for LOG_LEVEL_POPUPS
139  *
140  *    Revision 1.13  2001/07/13 13:58:58  oes
141  *     - Added case for LOG_LEVEL_DEANIMATE
142  *     - Removed all #ifdef PCRS
143  *
144  *    Revision 1.12  2001/06/09 10:55:28  jongfoster
145  *    Changing BUFSIZ ==> BUFFER_SIZE
146  *
147  *    Revision 1.11  2001/06/01 18:14:49  jongfoster
148  *    Changing the calls to strerr() to check HAVE_STRERR (which is defined
149  *    in config.h if appropriate) rather than the NO_STRERR macro.
150  *
151  *    Revision 1.10  2001/05/29 11:52:21  oes
152  *    Conditional compilation of w32_socket_error
153  *
154  *    Revision 1.9  2001/05/28 16:15:17  jongfoster
155  *    Improved reporting of errors under Win32.
156  *
157  *    Revision 1.8  2001/05/26 17:25:14  jongfoster
158  *    Added support for CLF (Common Log Format) and fixed LOG_LEVEL_LOG
159  *
160  *    Revision 1.7  2001/05/26 15:21:28  jongfoster
161  *    Activity animation in Win32 GUI now works even if debug==0
162  *
163  *    Revision 1.6  2001/05/25 21:55:08  jongfoster
164  *    Now cleans up properly on FATAL (removes taskbar icon etc)
165  *
166  *    Revision 1.5  2001/05/22 18:46:04  oes
167  *
168  *    - Enabled filtering banners by size rather than URL
169  *      by adding patterns that replace all standard banner
170  *      sizes with the "Junkbuster" gif to the re_filterfile
171  *
172  *    - Enabled filtering WebBugs by providing a pattern
173  *      which kills all 1x1 images
174  *
175  *    - Added support for PCRE_UNGREEDY behaviour to pcrs,
176  *      which is selected by the (nonstandard and therefore
177  *      capital) letter 'U' in the option string.
178  *      It causes the quantifiers to be ungreedy by default.
179  *      Appending a ? turns back to greedy (!).
180  *
181  *    - Added a new interceptor ijb-send-banner, which
182  *      sends back the "Junkbuster" gif. Without imagelist or
183  *      MSIE detection support, or if tinygif = 1, or the
184  *      URL isn't recognized as an imageurl, a lame HTML
185  *      explanation is sent instead.
186  *
187  *    - Added new feature, which permits blocking remote
188  *      script redirects and firing back a local redirect
189  *      to the browser.
190  *      The feature is conditionally compiled, i.e. it
191  *      can be disabled with --disable-fast-redirects,
192  *      plus it must be activated by a "fast-redirects"
193  *      line in the config file, has its own log level
194  *      and of course wants to be displayed by show-proxy-args
195  *      Note: Boy, all the #ifdefs in 1001 locations and
196  *      all the fumbling with configure.in and acconfig.h
197  *      were *way* more work than the feature itself :-(
198  *
199  *    - Because a generic redirect template was needed for
200  *      this, tinygif = 3 now uses the same.
201  *
202  *    - Moved GIFs, and other static HTTP response templates
203  *      to project.h
204  *
205  *    - Some minor fixes
206  *
207  *    - Removed some >400 CRs again (Jon, you really worked
208  *      a lot! ;-)
209  *
210  *    Revision 1.4  2001/05/21 19:32:54  jongfoster
211  *    Added another #ifdef _WIN_CONSOLE
212  *
213  *    Revision 1.3  2001/05/20 01:11:40  jongfoster
214  *    Added support for LOG_LEVEL_FATAL
215  *    Renamed LOG_LEVEL_FRC to LOG_LEVEL_FORCE,
216  *    and LOG_LEVEL_REF to LOG_LEVEL_RE_FILTER
217  *
218  *    Revision 1.2  2001/05/17 22:42:01  oes
219  *     - Cleaned CRLF's from the sources and related files
220  *     - Repaired logging for REF and FRC
221  *
222  *    Revision 1.1.1.1  2001/05/15 13:58:51  oes
223  *    Initial import of version 2.9.3 source tree
224  *
225  *
226  *********************************************************************/
227 \f
228
229 #include "config.h"
230 #include "miscutil.h"
231
232 #include <stdlib.h>
233 #include <stdio.h>
234 #include <stdarg.h>
235 #include <string.h>
236
237 #if !defined(_WIN32) && !defined(__OS2__)
238 #include <unistd.h>
239 #endif /* !defined(_WIN32) && !defined(__OS2__) */
240
241 #include <errno.h>
242 #include <assert.h>
243 #ifdef FEATURE_PTHREAD
244 #include <pthread.h>
245 #endif /* def FEATURE_PTHREAD */
246
247 #ifdef _WIN32
248 #ifndef STRICT
249 #define STRICT
250 #endif
251 #include <windows.h>
252 #ifndef _WIN_CONSOLE
253 #include "w32log.h"
254 #endif /* ndef _WIN_CONSOLE */
255 #endif /* def _WIN32 */
256
257 #ifdef __OS2__
258 #include <sys/socket.h> /* For sock_errno */
259 #define INCL_DOS
260 #include <os2.h>
261 #endif
262
263 #include "errlog.h"
264 #include "project.h"
265 #include "jcc.h"
266
267 const char errlog_h_rcs[] = ERRLOG_H_VERSION;
268
269
270 /*
271  * LOG_LEVEL_FATAL cannot be turned off.  (There are
272  * some exceptional situations where we need to get a
273  * message to the user).
274  */
275 #define LOG_LEVEL_MINIMUM  LOG_LEVEL_FATAL
276
277 /* where to log (default: stderr) */
278 static FILE *logfp = NULL;
279
280 /* logging detail level.  */
281 static int debug = (LOG_LEVEL_FATAL | LOG_LEVEL_ERROR | LOG_LEVEL_INFO);  
282
283 /* static functions */
284 static void fatal_error(const char * error_message);
285 #ifdef _WIN32
286 static char *w32_socket_strerr(int errcode, char *tmp_buf);
287 #endif
288 #ifdef __OS2__
289 static char *os2_socket_strerr(int errcode, char *tmp_buf);
290 #endif
291
292 /*********************************************************************
293  *
294  * Function    :  fatal_error
295  *
296  * Description :  Displays a fatal error to standard error (or, on 
297  *                a WIN32 GUI, to a dialog box), and exits
298  *                JunkBuster with status code 1.
299  *
300  * Parameters  :
301  *          1  :  error_message = The error message to display.
302  *
303  * Returns     :  Does not return.
304  *
305  *********************************************************************/
306 static void fatal_error(const char * error_message)
307 {
308 #if defined(_WIN32) && !defined(_WIN_CONSOLE)
309    MessageBox(g_hwndLogFrame, error_message, "Privoxy Error", 
310       MB_OK | MB_ICONERROR | MB_TASKMODAL | MB_SETFOREGROUND | MB_TOPMOST);  
311
312    /* Cleanup - remove taskbar icon etc. */
313    TermLogWindow();
314
315 #else /* if !defined(_WIN32) || defined(_WIN_CONSOLE) */
316    fputs(error_message, stderr);
317 #endif /* defined(_WIN32) && !defined(_WIN_CONSOLE) */
318
319 #if defined(unix)
320    unlink(pidfile);
321 #endif /* unix */
322
323    exit(1);
324 }
325
326
327 /*********************************************************************
328  *
329  * Function    :  init_error_log
330  *
331  * Description :  Initializes the logging module.  Must call before
332  *                calling log_error.
333  *
334  * Parameters  :
335  *          1  :  prog_name  = The program name.
336  *          2  :  logfname   = The logfile name, or NULL for stderr.
337  *          3  :  debuglevel = The debugging level.
338  *
339  * Returns     :  N/A
340  *
341  *********************************************************************/
342 void init_error_log(const char *prog_name, const char *logfname, int debuglevel)
343 {
344    FILE *fp;
345
346    /* FIXME RACE HAZARD: should start critical section error_log_use here */
347
348    /* set the logging detail level */
349    debug = debuglevel | LOG_LEVEL_MINIMUM;
350
351    if ((logfp != NULL) && (logfp != stderr))
352    {
353       log_error(LOG_LEVEL_INFO, "(Re-)Open logfile %s", logfname ? logfname : "none");
354       fclose(logfp);
355    }
356    logfp = stderr;
357
358    /* set the designated log file */
359    if( logfname )
360    {
361       if( NULL == (fp = fopen(logfname, "a")) )
362       {
363          log_error(LOG_LEVEL_FATAL, "init_error_log(): can't open logfile: %s", logfname);
364       }
365
366       /* set logging to be completely unbuffered */
367       setbuf(fp, NULL);
368
369       logfp = fp;
370    }
371
372    log_error(LOG_LEVEL_INFO, "Privoxy version " VERSION);
373    if (prog_name != NULL)
374    {
375       log_error(LOG_LEVEL_INFO, "Program name: %s", prog_name);
376    }
377
378    /* FIXME RACE HAZARD: should end critical section error_log_use here */
379
380 } /* init_error_log */
381
382
383 /*********************************************************************
384  *
385  * Function    :  log_error
386  *
387  * Description :  This is the error-reporting and logging function.
388  *
389  * Parameters  :
390  *          1  :  loglevel  = the type of message to be logged
391  *          2  :  fmt       = the main string we want logged, printf-like
392  *          3  :  ...       = arguments to be inserted in fmt (printf-like).
393  *
394  * Returns     :  N/A
395  *
396  *********************************************************************/
397 void log_error(int loglevel, char *fmt, ...)
398 {
399    va_list ap;
400    char *outbuf= NULL;
401    static char *outbuf_save = NULL;
402    char * src = fmt;
403    int outc = 0;
404    long this_thread = 1;  /* was: pthread_t this_thread;*/
405 #ifdef __OS2__
406    PTIB     ptib;
407    APIRET   ulrc;
408 #endif /* __OS2__ */
409
410 #if defined(_WIN32) && !defined(_WIN_CONSOLE)
411    /*
412     * Irrespective of debug setting, a GET/POST/CONNECT makes
413     * the taskbar icon animate.  (There is an option to disable
414     * this but checking that is handled inside LogShowActivity()).
415     */
416    if (loglevel == LOG_LEVEL_GPC)
417    {
418       LogShowActivity();
419    }
420 #endif /* defined(_WIN32) && !defined(_WIN_CONSOLE) */
421
422    /* verify if loglevel applies to current settings and bail out if negative */
423    if ((loglevel & debug) == 0)
424    {
425       return;
426    }
427
428    /* FIXME get current thread id */
429 #ifdef FEATURE_PTHREAD
430    this_thread = (long)pthread_self();
431 #ifdef __MACH__
432    /*
433     * Mac OSX (and perhaps other Mach instances) doesn't have a debuggable
434     * value at the first 4 bytes of pthread_self()'s return value, a pthread_t.
435     * pthread_t is supposed to be opaque... but it's fairly random, though, so
436     * we make it mostly presentable.
437     */
438    this_thread = abs(this_thread % 1000);
439 #endif /* def __MACH__ */
440 #elif defined(_WIN32)
441    this_thread = GetCurrentThreadId();
442 #elif defined(__OS2__)
443    ulrc = DosGetInfoBlocks(&ptib, NULL);
444    if (ulrc == 0)
445      this_thread = ptib -> tib_ptib2 -> tib2_ultid;
446 #endif /* def FEATURE_PTHREAD */
447
448    if ( !outbuf_save ) 
449    {
450       outbuf_save = outbuf = (char*)malloc(BUFFER_SIZE);
451       assert(outbuf);
452    }
453    outbuf = outbuf_save;
454
455     {
456        /*
457         * Write timestamp into tempbuf.
458         *
459         * Complex because not all OSs have tm_gmtoff or
460         * the %z field in strftime()
461         */
462        time_t now; 
463        struct tm tm_now; 
464        time (&now);
465 #ifdef HAVE_LOCALTIME_R
466        tm_now = *localtime_r(&now, &tm_now);
467 #elif OSX_DARWIN
468        pthread_mutex_lock(&localtime_mutex);
469        tm_now = *localtime (&now); 
470        pthread_mutex_unlock(&localtime_mutex);
471 #else
472        tm_now = *localtime (&now); 
473 #endif
474        strftime(outbuf, BUFFER_SIZE-6, "%b %d %H:%M:%S ", &tm_now); 
475        outbuf += strlen( outbuf );
476     }
477    switch (loglevel)
478    {
479       case LOG_LEVEL_ERROR:
480          outc = sprintf(outbuf, "Privoxy(%05ld) Error: ", this_thread);
481          break;
482       case LOG_LEVEL_FATAL:
483          outc = sprintf(outbuf, "Privoxy(%05ld) Fatal error: ", this_thread);
484          break;
485       case LOG_LEVEL_GPC:
486          outc = sprintf(outbuf, "Privoxy(%05ld) Request: ", this_thread);
487          break;
488       case LOG_LEVEL_CONNECT:
489          outc = sprintf(outbuf, "Privoxy(%05ld) Connect: ", this_thread);
490          break;
491       case LOG_LEVEL_LOG:
492          outc = sprintf(outbuf, "Privoxy(%05ld) Writing: ", this_thread);
493          break;
494       case LOG_LEVEL_HEADER:
495          outc = sprintf(outbuf, "Privoxy(%05ld) Header: ", this_thread);
496          break;
497       case LOG_LEVEL_INFO:
498          outc = sprintf(outbuf, "Privoxy(%05ld) Info: ", this_thread);
499          break;
500       case LOG_LEVEL_RE_FILTER:
501          outc = sprintf(outbuf, "Privoxy(%05ld) Re-Filter: ", this_thread);
502          break;
503 #ifdef FEATURE_FORCE_LOAD
504       case LOG_LEVEL_FORCE:
505          outc = sprintf(outbuf, "Privoxy(%05ld) Force: ", this_thread);
506          break;
507 #endif /* def FEATURE_FORCE_LOAD */
508 #ifdef FEATURE_FAST_REDIRECTS
509       case LOG_LEVEL_REDIRECTS:
510          outc = sprintf(outbuf, "Privoxy(%05ld) Redirect: ", this_thread);
511          break;
512 #endif /* def FEATURE_FAST_REDIRECTS */
513       case LOG_LEVEL_DEANIMATE:
514          outc = sprintf(outbuf, "Privoxy(%05ld) Gif-Deanimate: ", this_thread);
515          break;
516       case LOG_LEVEL_CLF:
517          outbuf = outbuf_save;
518          outc = 0;
519          outbuf[0] = '\0';
520          break;
521 #ifdef FEATURE_KILL_POPUPS
522       case LOG_LEVEL_POPUPS:
523          outc = sprintf(outbuf, "Privoxy(%05ld) Kill-Popups: ", this_thread);
524          break;
525 #endif /* def FEATURE_KILL_POPUPS */
526       case LOG_LEVEL_CGI:
527          outc = sprintf(outbuf, "Privoxy(%05ld) CGI: ", this_thread);
528          break;
529       default:
530          outc = sprintf(outbuf, "Privoxy(%05ld) UNKNOWN LOG TYPE(%d): ", this_thread, loglevel);
531          break;
532    }
533    
534    /* get ready to scan var. args. */
535    va_start( ap, fmt );
536
537    /* build formatted message from fmt and var-args */
538    while ((*src) && (outc < BUFFER_SIZE-2))
539    {
540       char tempbuf[BUFFER_SIZE];
541       char *sval = NULL;
542       int ival;
543       unsigned uval;
544       long lval;
545       unsigned long ulval;
546       int oldoutc;
547       char ch;
548       
549       ch = *src++;
550       if( ch != '%' )
551       {
552          outbuf[outc++] = ch;
553          continue;
554       }
555
556       ch = *src++;
557       switch (ch) {
558          case '%':
559             outbuf[outc++] = '%';
560             break;
561          case 'd':
562             ival = va_arg( ap, int );
563             oldoutc = outc;
564             outc += sprintf(tempbuf, "%d", ival);
565             if (outc < BUFFER_SIZE-1) 
566             {
567                strcpy(outbuf + oldoutc, tempbuf);
568             }
569             else
570             {
571                outbuf[oldoutc] = '\0';
572             }
573             break;
574          case 'u':
575             uval = va_arg( ap, unsigned );
576             oldoutc = outc;
577             outc += sprintf(tempbuf, "%u", uval);
578             if (outc < BUFFER_SIZE-1) 
579             {
580                strcpy(outbuf + oldoutc, tempbuf);
581             }
582             else
583             {
584                outbuf[oldoutc] = '\0';
585             }
586             break;
587          case 'l':
588             /* this is a modifier that must be followed by u or d */
589             ch = *src++;
590             if (ch == 'd')
591             {
592                lval = va_arg( ap, long );
593                oldoutc = outc;
594                outc += sprintf(tempbuf, "%ld", lval);
595             }
596             else if (ch == 'u')
597             {
598                ulval = va_arg( ap, unsigned long );
599                oldoutc = outc;
600                outc += sprintf(tempbuf, "%lu", ulval);
601             }
602             else
603             {
604                /* Error */
605                sprintf(outbuf, "Privoxy(%ld) Error: log_error(): Bad format string:\n"
606                                "Format = \"%s\"\n"
607                                "Exiting.", this_thread, fmt);
608                /* FIXME RACE HAZARD: should start critical section error_log_use here */
609                if( !logfp )
610                {
611                   logfp = stderr;
612                }
613                fputs(outbuf, logfp);
614                /* FIXME RACE HAZARD: should end critical section error_log_use here */
615                fatal_error(outbuf);
616                /* Never get here */
617                break;
618             }
619             if (outc < BUFFER_SIZE-1) 
620             {
621                strcpy(outbuf + oldoutc, tempbuf);
622             }
623             else
624             {
625                outbuf[oldoutc] = '\0';
626             }
627             break;
628          case 'c':
629             /*
630              * Note that char paramaters are converted to int, so we need to
631              * pass "int" to va_arg.  (See K&R, 2nd ed, section A7.3.2, page 202)
632              */
633             outbuf[outc++] = (char) va_arg( ap, int );
634             break;
635          case 's':
636             sval = va_arg( ap, char * );
637             if (sval == NULL)
638             {
639                sval = "[null]";
640             }
641             oldoutc = outc;
642             outc += strlen(sval);
643             if (outc < BUFFER_SIZE-1) 
644             {
645                strcpy(outbuf + oldoutc, sval);
646             }
647             else
648             {
649                outbuf[oldoutc] = '\0';
650             }
651             break;
652          case 'N':
653             /* Non-standard: Print a counted string.  Takes 2 parameters:
654              * int length, const char * string
655              */
656             ival = va_arg( ap, int );
657             sval = va_arg( ap, char * );
658             if (sval == NULL)
659             {
660                sval = "[null]";
661             }
662             if (ival < 0)
663             {
664                ival = 0;
665             }
666             oldoutc = outc;
667             outc += ival;
668             if (outc < BUFFER_SIZE-1)
669             {
670                memcpy(outbuf + oldoutc, sval, (size_t) ival);
671             }
672             else
673             {
674                outbuf[oldoutc] = '\0';
675             }
676             break;
677          case 'E':
678             /* Non-standard: Print error code from errno */
679 #ifdef _WIN32
680             ival = WSAGetLastError();
681             sval = w32_socket_strerr(ival, tempbuf);
682 #elif __OS2__
683             ival = sock_errno();
684             if (ival != 0)
685               sval = os2_socket_strerr(ival, tempbuf);
686             else
687             {
688               ival = errno;
689               sval = strerror(ival);
690             }
691 #else /* ifndef _WIN32 */
692             ival = errno; 
693 #ifdef HAVE_STRERROR
694             sval = strerror(ival);
695 #else /* ifndef HAVE_STRERROR */
696             sval = NULL;
697 #endif /* ndef HAVE_STRERROR */
698             if (sval == NULL)
699             {
700                sprintf(tempbuf, "(errno = %d)", ival);
701                sval = tempbuf;
702             }
703 #endif /* ndef _WIN32 */
704             oldoutc = outc;
705             outc += strlen(sval);
706             if (outc < BUFFER_SIZE-1) 
707             {
708                strcpy(outbuf + oldoutc, sval);
709             }
710             else
711             {
712                outbuf[oldoutc] = '\0';
713             }
714             break;
715          case 'T':
716             /* Non-standard: Print a Common Log File timestamp */
717             {
718                /*
719                 * Write timestamp into tempbuf.
720                 *
721                 * Complex because not all OSs have tm_gmtoff or
722                 * the %z field in strftime()
723                 */
724                time_t now; 
725                struct tm *tm_now; 
726                struct tm gmt;
727 #ifdef HAVE_LOCALTIME_R
728                struct tm dummy;
729 #endif
730                int days, hrs, mins; 
731                time (&now); 
732 #ifdef HAVE_GMTIME_R
733                gmt = *gmtime_r(&now, &gmt);
734 #elif OSX_DARWIN
735                pthread_mutex_lock(&gmtime_mutex);
736                gmt = *gmtime(&now);
737                pthread_mutex_unlock(&gmtime_mutex);
738 #else
739                gmt = *gmtime(&now);
740 #endif
741 #ifdef HAVE_LOCALTIME_R
742                tm_now = localtime_r(&now, &dummy);
743 #elif OSX_DARWIN
744                pthread_mutex_lock(&localtime_mutex);
745                tm_now = localtime (&now); 
746                pthread_mutex_unlock(&localtime_mutex);
747 #else
748                tm_now = localtime (&now); 
749 #endif
750                days = tm_now->tm_yday - gmt.tm_yday; 
751                hrs = ((days < -1 ? 24 : 1 < days ? -24 : days * 24) + tm_now->tm_hour - gmt.tm_hour); 
752                mins = hrs * 60 + tm_now->tm_min - gmt.tm_min; 
753                strftime (tempbuf, BUFFER_SIZE-6, "%d/%b/%Y:%H:%M:%S ", tm_now); 
754                sprintf (tempbuf + strlen(tempbuf), "%+03d%02d", mins / 60, abs(mins) % 60); 
755             }
756             oldoutc = outc;
757             outc += strlen(tempbuf);
758             if (outc < BUFFER_SIZE-1) 
759             {
760                strcpy(outbuf + oldoutc, tempbuf);
761             }
762             else
763             {
764                outbuf[oldoutc] = '\0';
765             }
766             break;
767          default:
768             sprintf(outbuf, "Privoxy(%ld) Error: log_error(): Bad format string:\n"
769                             "Format = \"%s\"\n"
770                             "Exiting.", this_thread, fmt);
771             /* FIXME RACE HAZARD: should start critical section error_log_use here */
772             if( !logfp )
773             {
774                logfp = stderr;
775             }
776             fputs(outbuf_save, logfp);
777             /* FIXME RACE HAZARD: should end critical section error_log_use here */
778             fatal_error(outbuf_save);
779             /* Never get here */
780             break;
781
782       } /* switch( p ) */
783
784    } /* for( p ... ) */
785    
786    /* done with var. args */
787    va_end( ap );
788    
789    if (outc >= BUFFER_SIZE-2)
790    {
791       /* insufficient room for newline and trailing null. */
792
793       static const char warning[] = "... [too long, truncated]\n";
794
795       if (outc < BUFFER_SIZE)
796       {
797          /* Need to add terminating null in this case. */
798          outbuf[outc] = '\0';
799       }
800
801       /* Truncate output */
802       outbuf[BUFFER_SIZE - sizeof(warning)] = '\0';
803
804       /* Append warning */
805       strcat(outbuf, warning);
806    }
807    else
808    {
809       /* Add terminating newline and null */
810       outbuf[outc++] = '\n';
811       outbuf[outc] = '\0';
812    }
813
814    /* FIXME RACE HAZARD: should start critical section error_log_use here */
815
816    /* deal with glibc stupidity - it won't let you initialize logfp */
817    if( !logfp )
818    {
819       logfp = stderr;
820    }
821
822    fputs(outbuf_save, logfp);
823
824    if (loglevel == LOG_LEVEL_FATAL)
825    {
826       fatal_error(outbuf_save);
827       /* Never get here */
828    }
829
830    /* FIXME RACE HAZARD: should end critical section error_log_use here */
831
832 #if defined(_WIN32) && !defined(_WIN_CONSOLE)
833    /* Write to display */
834    LogPutString(outbuf_save);
835 #endif /* defined(_WIN32) && !defined(_WIN_CONSOLE) */
836
837 }
838
839
840 #ifdef _WIN32
841 /*********************************************************************
842  *
843  * Function    :  w32_socket_strerr
844  *
845  * Description :  Translate the return value from WSAGetLastError()
846  *                into a string.
847  *
848  * Parameters  :
849  *          1  :  errcode = The return value from WSAGetLastError().
850  *          2  :  tmp_buf = A temporary buffer that might be used to
851  *                          store the string.
852  *
853  * Returns     :  String representing the error code.  This may be
854  *                a global string constant or a string stored in
855  *                tmp_buf.
856  *
857  *********************************************************************/
858 static char *w32_socket_strerr(int errcode, char *tmp_buf)
859 {
860 #define TEXT_FOR_ERROR(code,text) \
861    if (errcode == code)           \
862    {                              \
863       return #code " - " text;    \
864    }
865
866    TEXT_FOR_ERROR(WSAEACCES, "Permission denied")
867    TEXT_FOR_ERROR(WSAEADDRINUSE, "Address already in use.")
868    TEXT_FOR_ERROR(WSAEADDRNOTAVAIL, "Cannot assign requested address.");
869    TEXT_FOR_ERROR(WSAEAFNOSUPPORT, "Address family not supported by protocol family.");
870    TEXT_FOR_ERROR(WSAEALREADY, "Operation already in progress.");
871    TEXT_FOR_ERROR(WSAECONNABORTED, "Software caused connection abort.");
872    TEXT_FOR_ERROR(WSAECONNREFUSED, "Connection refused.");
873    TEXT_FOR_ERROR(WSAECONNRESET, "Connection reset by peer.");
874    TEXT_FOR_ERROR(WSAEDESTADDRREQ, "Destination address required.");
875    TEXT_FOR_ERROR(WSAEFAULT, "Bad address.");
876    TEXT_FOR_ERROR(WSAEHOSTDOWN, "Host is down.");
877    TEXT_FOR_ERROR(WSAEHOSTUNREACH, "No route to host.");
878    TEXT_FOR_ERROR(WSAEINPROGRESS, "Operation now in progress.");
879    TEXT_FOR_ERROR(WSAEINTR, "Interrupted function call.");
880    TEXT_FOR_ERROR(WSAEINVAL, "Invalid argument.");
881    TEXT_FOR_ERROR(WSAEISCONN, "Socket is already connected.");
882    TEXT_FOR_ERROR(WSAEMFILE, "Too many open sockets.");
883    TEXT_FOR_ERROR(WSAEMSGSIZE, "Message too long.");
884    TEXT_FOR_ERROR(WSAENETDOWN, "Network is down.");
885    TEXT_FOR_ERROR(WSAENETRESET, "Network dropped connection on reset.");
886    TEXT_FOR_ERROR(WSAENETUNREACH, "Network is unreachable.");
887    TEXT_FOR_ERROR(WSAENOBUFS, "No buffer space available.");
888    TEXT_FOR_ERROR(WSAENOPROTOOPT, "Bad protocol option.");
889    TEXT_FOR_ERROR(WSAENOTCONN, "Socket is not connected.");
890    TEXT_FOR_ERROR(WSAENOTSOCK, "Socket operation on non-socket.");
891    TEXT_FOR_ERROR(WSAEOPNOTSUPP, "Operation not supported.");
892    TEXT_FOR_ERROR(WSAEPFNOSUPPORT, "Protocol family not supported.");
893    TEXT_FOR_ERROR(WSAEPROCLIM, "Too many processes.");
894    TEXT_FOR_ERROR(WSAEPROTONOSUPPORT, "Protocol not supported.");
895    TEXT_FOR_ERROR(WSAEPROTOTYPE, "Protocol wrong type for socket.");
896    TEXT_FOR_ERROR(WSAESHUTDOWN, "Cannot send after socket shutdown.");
897    TEXT_FOR_ERROR(WSAESOCKTNOSUPPORT, "Socket type not supported.");
898    TEXT_FOR_ERROR(WSAETIMEDOUT, "Connection timed out.");
899    TEXT_FOR_ERROR(WSAEWOULDBLOCK, "Resource temporarily unavailable.");
900    TEXT_FOR_ERROR(WSAHOST_NOT_FOUND, "Host not found.");
901    TEXT_FOR_ERROR(WSANOTINITIALISED, "Successful WSAStartup not yet performed.");
902    TEXT_FOR_ERROR(WSANO_DATA, "Valid name, no data record of requested type.");
903    TEXT_FOR_ERROR(WSANO_RECOVERY, "This is a non-recoverable error.");
904    TEXT_FOR_ERROR(WSASYSNOTREADY, "Network subsystem is unavailable.");
905    TEXT_FOR_ERROR(WSATRY_AGAIN, "Non-authoritative host not found.");
906    TEXT_FOR_ERROR(WSAVERNOTSUPPORTED, "WINSOCK.DLL version out of range.");
907    TEXT_FOR_ERROR(WSAEDISCON, "Graceful shutdown in progress.");
908    /*
909     * The following error codes are documented in the Microsoft WinSock
910     * reference guide, but don't actually exist.
911     *
912     * TEXT_FOR_ERROR(WSA_INVALID_HANDLE, "Specified event object handle is invalid.");
913     * TEXT_FOR_ERROR(WSA_INVALID_PARAMETER, "One or more parameters are invalid.");
914     * TEXT_FOR_ERROR(WSAINVALIDPROCTABLE, "Invalid procedure table from service provider.");
915     * TEXT_FOR_ERROR(WSAINVALIDPROVIDER, "Invalid service provider version number.");
916     * TEXT_FOR_ERROR(WSA_IO_PENDING, "Overlapped operations will complete later.");
917     * TEXT_FOR_ERROR(WSA_IO_INCOMPLETE, "Overlapped I/O event object not in signaled state.");
918     * TEXT_FOR_ERROR(WSA_NOT_ENOUGH_MEMORY, "Insufficient memory available.");
919     * TEXT_FOR_ERROR(WSAPROVIDERFAILEDINIT, "Unable to initialize a service provider.");
920     * TEXT_FOR_ERROR(WSASYSCALLFAILURE, "System call failure.");
921     * TEXT_FOR_ERROR(WSA_OPERATION_ABORTED, "Overlapped operation aborted.");
922     */
923
924    sprintf(tmp_buf, "(error number %d)", errcode);
925    return tmp_buf;
926 }
927 #endif /* def _WIN32 */
928
929
930 #ifdef __OS2__
931 /*********************************************************************
932  *
933  * Function    :  os2_socket_strerr
934  *
935  * Description :  Translate the return value from sock_errno()
936  *                into a string.
937  *
938  * Parameters  :
939  *          1  :  errcode = The return value from sock_errno().
940  *          2  :  tmp_buf = A temporary buffer that might be used to
941  *                          store the string.
942  *
943  * Returns     :  String representing the error code.  This may be
944  *                a global string constant or a string stored in
945  *                tmp_buf.
946  *
947  *********************************************************************/
948 static char *os2_socket_strerr(int errcode, char *tmp_buf)
949 {
950 #define TEXT_FOR_ERROR(code,text) \
951    if (errcode == code)           \
952    {                              \
953       return #code " - " text;    \
954    }
955
956    TEXT_FOR_ERROR(SOCEPERM          , "Not owner.")
957    TEXT_FOR_ERROR(SOCESRCH          , "No such process.")
958    TEXT_FOR_ERROR(SOCEINTR          , "Interrupted system call.")
959    TEXT_FOR_ERROR(SOCENXIO          , "No such device or address.")
960    TEXT_FOR_ERROR(SOCEBADF          , "Bad file number.")
961    TEXT_FOR_ERROR(SOCEACCES         , "Permission denied.")
962    TEXT_FOR_ERROR(SOCEFAULT         , "Bad address.")
963    TEXT_FOR_ERROR(SOCEINVAL         , "Invalid argument.")
964    TEXT_FOR_ERROR(SOCEMFILE         , "Too many open files.")
965    TEXT_FOR_ERROR(SOCEPIPE          , "Broken pipe.")
966    TEXT_FOR_ERROR(SOCEWOULDBLOCK    , "Operation would block.")
967    TEXT_FOR_ERROR(SOCEINPROGRESS    , "Operation now in progress.")
968    TEXT_FOR_ERROR(SOCEALREADY       , "Operation already in progress.")
969    TEXT_FOR_ERROR(SOCENOTSOCK       , "Socket operation on non-socket.")
970    TEXT_FOR_ERROR(SOCEDESTADDRREQ   , "Destination address required.")
971    TEXT_FOR_ERROR(SOCEMSGSIZE       , "Message too long.")
972    TEXT_FOR_ERROR(SOCEPROTOTYPE     , "Protocol wrong type for socket.")
973    TEXT_FOR_ERROR(SOCENOPROTOOPT    , "Protocol not available.")
974    TEXT_FOR_ERROR(SOCEPROTONOSUPPORT, "Protocol not supported.")
975    TEXT_FOR_ERROR(SOCESOCKTNOSUPPORT, "Socket type not supported.")
976    TEXT_FOR_ERROR(SOCEOPNOTSUPP     , "Operation not supported.")
977    TEXT_FOR_ERROR(SOCEPFNOSUPPORT   , "Protocol family not supported.")
978    TEXT_FOR_ERROR(SOCEAFNOSUPPORT   , "Address family not supported by protocol family.")
979    TEXT_FOR_ERROR(SOCEADDRINUSE     , "Address already in use.")
980    TEXT_FOR_ERROR(SOCEADDRNOTAVAIL  , "Can't assign requested address.")
981    TEXT_FOR_ERROR(SOCENETDOWN       , "Network is down.")
982    TEXT_FOR_ERROR(SOCENETUNREACH    , "Network is unreachable.")
983    TEXT_FOR_ERROR(SOCENETRESET      , "Network dropped connection on reset.")
984    TEXT_FOR_ERROR(SOCECONNABORTED   , "Software caused connection abort.")
985    TEXT_FOR_ERROR(SOCECONNRESET     , "Connection reset by peer.")
986    TEXT_FOR_ERROR(SOCENOBUFS        , "No buffer space available.")
987    TEXT_FOR_ERROR(SOCEISCONN        , "Socket is already connected.")
988    TEXT_FOR_ERROR(SOCENOTCONN       , "Socket is not connected.")
989    TEXT_FOR_ERROR(SOCESHUTDOWN      , "Can't send after socket shutdown.")
990    TEXT_FOR_ERROR(SOCETOOMANYREFS   , "Too many references: can't splice.")
991    TEXT_FOR_ERROR(SOCETIMEDOUT      , "Operation timed out.")
992    TEXT_FOR_ERROR(SOCECONNREFUSED   , "Connection refused.")
993    TEXT_FOR_ERROR(SOCELOOP          , "Too many levels of symbolic links.")
994    TEXT_FOR_ERROR(SOCENAMETOOLONG   , "File name too long.")
995    TEXT_FOR_ERROR(SOCEHOSTDOWN      , "Host is down.")
996    TEXT_FOR_ERROR(SOCEHOSTUNREACH   , "No route to host.")
997    TEXT_FOR_ERROR(SOCENOTEMPTY      , "Directory not empty.")
998    TEXT_FOR_ERROR(SOCEOS2ERR        , "OS/2 Error.")
999
1000    sprintf(tmp_buf, "(error number %d)", errcode);
1001    return tmp_buf;
1002 }
1003 #endif /* def __OS2__ */
1004
1005
1006 /*
1007   Local Variables:
1008   tab-width: 3
1009   end:
1010 */