4ef0e97438fcd78725d53c3808c4eb549dc7328d
[privoxy.git] / errlog.c
1 const char errlog_rcs[] = "$Id: errlog.c,v 1.5 2001/05/22 18:46:04 oes Exp $";
2 /*********************************************************************
3  *
4  * File        :  $Source: /cvsroot/ijbswa/current/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  *                IJBSWA team.  http://ijbswa.sourceforge.net
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.5  2001/05/22 18:46:04  oes
37  *
38  *    - Enabled filtering banners by size rather than URL
39  *      by adding patterns that replace all standard banner
40  *      sizes with the "Junkbuster" gif to the re_filterfile
41  *
42  *    - Enabled filtering WebBugs by providing a pattern
43  *      which kills all 1x1 images
44  *
45  *    - Added support for PCRE_UNGREEDY behaviour to pcrs,
46  *      which is selected by the (nonstandard and therefore
47  *      capital) letter 'U' in the option string.
48  *      It causes the quantifiers to be ungreedy by default.
49  *      Appending a ? turns back to greedy (!).
50  *
51  *    - Added a new interceptor ijb-send-banner, which
52  *      sends back the "Junkbuster" gif. Without imagelist or
53  *      MSIE detection support, or if tinygif = 1, or the
54  *      URL isn't recognized as an imageurl, a lame HTML
55  *      explanation is sent instead.
56  *
57  *    - Added new feature, which permits blocking remote
58  *      script redirects and firing back a local redirect
59  *      to the browser.
60  *      The feature is conditionally compiled, i.e. it
61  *      can be disabled with --disable-fast-redirects,
62  *      plus it must be activated by a "fast-redirects"
63  *      line in the config file, has its own log level
64  *      and of course wants to be displayed by show-proxy-args
65  *      Note: Boy, all the #ifdefs in 1001 locations and
66  *      all the fumbling with configure.in and acconfig.h
67  *      were *way* more work than the feature itself :-(
68  *
69  *    - Because a generic redirect template was needed for
70  *      this, tinygif = 3 now uses the same.
71  *
72  *    - Moved GIFs, and other static HTTP response templates
73  *      to project.h
74  *
75  *    - Some minor fixes
76  *
77  *    - Removed some >400 CRs again (Jon, you really worked
78  *      a lot! ;-)
79  *
80  *    Revision 1.4  2001/05/21 19:32:54  jongfoster
81  *    Added another #ifdef _WIN_CONSOLE
82  *
83  *    Revision 1.3  2001/05/20 01:11:40  jongfoster
84  *    Added support for LOG_LEVEL_FATAL
85  *    Renamed LOG_LEVEL_FRC to LOG_LEVEL_FORCE,
86  *    and LOG_LEVEL_REF to LOG_LEVEL_RE_FILTER
87  *
88  *    Revision 1.2  2001/05/17 22:42:01  oes
89  *     - Cleaned CRLF's from the sources and related files
90  *     - Repaired logging for REF and FRC
91  *
92  *    Revision 1.1.1.1  2001/05/15 13:58:51  oes
93  *    Initial import of version 2.9.3 source tree
94  *
95  *
96  *********************************************************************/
97 \f
98
99 #include "config.h"
100
101 #include <stdlib.h>
102 #include <stdio.h>
103 #include <stdarg.h>
104 #include <string.h>
105
106 #ifndef _WIN32
107 #include <unistd.h>
108 #endif /* ndef _WIN32 */
109
110 #include <errno.h>
111 /* #include <pthread.h> */
112
113 #ifdef _WIN32
114 #include <windows.h>
115 #ifndef _WIN_CONSOLE
116 #include "w32log.h"
117 #endif /* ndef _WIN_CONSOLE */
118 #endif /* def _WIN32 */
119
120 #include "errlog.h"
121 #include "project.h"
122
123 const char errlog_h_rcs[] = ERRLOG_H_VERSION;
124
125
126 /*
127  * LOG_LEVEL_FATAL, LOG_LEVEL_ERROR and LOG_LEVEL_INFO
128  * cannot be turned off.  (There are some exceptional situations
129  * where we need to get a message to the user).
130  *
131  * FIXME: Do we need LOG_LEVEL_INFO here?
132  */
133 #define LOG_LEVEL_MINIMUM  (LOG_LEVEL_FATAL | LOG_LEVEL_ERROR | LOG_LEVEL_INFO)
134
135 /* where to log (default: stderr) */
136 static FILE *logfp = NULL;
137
138 /* where to log (NULL == stderr) */
139 static char * logfilename = NULL;
140
141 /* logging detail level.  */
142 static int debug = LOG_LEVEL_MINIMUM;  
143
144 static void fatal_error(const char * error_message);
145
146
147 /*********************************************************************
148  *
149  * Function    :  fatal_error
150  *
151  * Description :  Displays a fatal error to standard error (or, on 
152  *                a WIN32 GUI, to a dialog box), and exits
153  *                JunkBuster with status code 1.
154  *
155  * Parameters  :
156  *          1  :  error_message = The error message to display.
157  *
158  * Returns     :  Does not return.
159  *
160  *********************************************************************/
161 static void fatal_error(const char * error_message)
162 {
163 #if defined(_WIN32) && !defined(_WIN_CONSOLE)
164    MessageBox(g_hwndLogFrame, error_message, "Internet JunkBuster Error", 
165       MB_OK | MB_ICONERROR | MB_TASKMODAL | MB_SETFOREGROUND | MB_TOPMOST);  
166
167    /* Cleanup - remove taskbar icon etc. */
168    TermLogWindow();
169
170 #else /* if !defined(_WIN32) || defined(_WIN_CONSOLE) */
171    fputs(error_message, stderr);
172 #endif /* defined(_WIN32) && !defined(_WIN_CONSOLE) */
173
174    exit(1);
175 }
176
177
178 /*********************************************************************
179  *
180  * Function    :  init_errlog
181  *
182  * Description :  Initializes the logging module.  Must call before
183  *                calling log_error.
184  *
185  * Parameters  :
186  *          1  :  prog_name  = The program name.
187  *          2  :  logfname   = The logfile name, or NULL for stderr.
188  *          3  :  debuglevel = The debugging level.
189  *
190  * Returns     :  N/A
191  *
192  *********************************************************************/
193 void init_error_log(const char *prog_name, const char *logfname, int debuglevel)
194 {
195    FILE *fp;
196
197    /* FIXME RACE HAZARD: should start critical section error_log_use here */
198
199    /* set the logging detail level */
200    debug = debuglevel | LOG_LEVEL_MINIMUM;
201
202    if ((logfp != NULL) && (logfp != stderr))
203    {
204       fclose(logfp);
205    }
206    logfp = stderr;
207
208    /* set the designated log file */
209    if( logfname )
210    {
211       if( !(fp = fopen(logfname, "a")) )
212       {
213          log_error(LOG_LEVEL_FATAL, "init_errlog(): can't open logfile: %s", logfname);
214       }
215
216       /* set logging to be completely unbuffered */
217       setbuf(fp, NULL);
218
219       logfp = fp;
220    }
221
222    log_error(LOG_LEVEL_INFO, "Internet JunkBuster version " VERSION);
223    if (prog_name != NULL)
224    {
225       log_error(LOG_LEVEL_INFO, "Program name: %s", prog_name);
226    }
227
228    /* FIXME RACE HAZARD: should end critical section error_log_use here */
229
230 } /* init_error_log */
231
232
233 /*********************************************************************
234  *
235  * Function    :  log_error
236  *
237  * Description :  This is the error-reporting and logging function.
238  *
239  * Parameters  :
240  *          1  :  loglevel  = the type of message to be logged
241  *          2  :  fmt       = the main string we want logged, printf-like
242  *          3  :  ...       = arguments to be inserted in fmt (printf-like).
243  *
244  * Returns     :  N/A
245  *
246  *********************************************************************/
247 void log_error(int loglevel, char *fmt, ...)
248 {
249    va_list ap;
250    char outbuf[BUFSIZ];
251    char * src = fmt;
252    int outc = 0;
253    long this_thread = 1;  /* was: pthread_t this_thread;*/
254
255    /* verify if loglevel applies to current settings and bail out if negative */
256    if(!(loglevel & debug))
257    {
258       return;
259    }
260
261    /* FIXME get current thread id */
262    /* this_thread = (long)pthread_self(); */
263
264    switch (loglevel)
265    {
266       /* FIXME: What about LOG_LEVEL_LOG ??? */
267       case LOG_LEVEL_ERROR:
268          outc = sprintf(outbuf, "IJB(%d) Error: ", this_thread);
269          break;
270       case LOG_LEVEL_FATAL:
271          outc = sprintf(outbuf, "IJB(%d) Fatal error: ", this_thread);
272          break;
273       case LOG_LEVEL_GPC:
274          outc = sprintf(outbuf, "IJB(%d) Request: ", this_thread);
275          break;
276       case LOG_LEVEL_CONNECT:
277          outc = sprintf(outbuf, "IJB(%d) Connect: ", this_thread);
278          break;
279       case LOG_LEVEL_HEADER:
280          outc = sprintf(outbuf, "IJB(%d) Header: ", this_thread);
281          break;
282       case LOG_LEVEL_INFO:
283          outc = sprintf(outbuf, "IJB(%d) Info: ", this_thread);
284          break;
285 #ifdef PCRS
286       case LOG_LEVEL_RE_FILTER:
287          outc = sprintf(outbuf, "IJB(%d) Re-Filter: ", this_thread);
288          break;
289 #endif /* def PCRS */
290 #ifdef FORCE_LOAD
291       case LOG_LEVEL_FORCE:
292          outc = sprintf(outbuf, "IJB(%d) Force: ", this_thread);
293          break;
294 #endif /* def FORCE_LOAD */
295 #ifdef FAST_REDIRECTS
296       case LOG_LEVEL_REDIRECTS:
297          outc = sprintf(outbuf, "IJB(%d) Redirect: ", this_thread);
298          break;
299 #endif /* def FAST_REDIRECTS */
300       default:
301          outc = sprintf(outbuf, "IJB(%d) UNKNOWN LOG TYPE(%d): ", this_thread, loglevel);
302          break;
303    }
304    
305    /* get ready to scan var. args. */
306    va_start( ap, fmt );
307
308    /* build formatted message from fmt and var-args */
309    while ((*src) && (outc < BUFSIZ-2))
310    {
311       char tempbuf[BUFSIZ];
312       char *sval;
313       int ival;
314       unsigned uval;
315       long lval;
316       unsigned long ulval;
317       int oldoutc;
318       char ch;
319       
320       ch = *src++;
321       if( ch != '%' )
322       {
323          outbuf[outc++] = ch;
324          continue;
325       }
326
327       ch = *src++;
328       switch (ch) {
329          case '%':
330             outbuf[outc++] = '%';
331             break;
332          case 'd':
333             ival = va_arg( ap, int );
334             oldoutc = outc;
335             outc += sprintf(tempbuf, "%d", ival);
336             if (outc < BUFSIZ-1) 
337             {
338                strcpy(outbuf + oldoutc, tempbuf);
339             }
340             else
341             {
342                outbuf[oldoutc] = '\0';
343             }
344             break;
345          case 'u':
346             uval = va_arg( ap, unsigned );
347             oldoutc = outc;
348             outc += sprintf(tempbuf, "%u", uval);
349             if (outc < BUFSIZ-1) 
350             {
351                strcpy(outbuf + oldoutc, tempbuf);
352             }
353             else
354             {
355                outbuf[oldoutc] = '\0';
356             }
357             break;
358          case 'l':
359             /* this is a modifier that must be followed by u or d */
360             ch = *src++;
361             if (ch == 'd')
362             {
363                lval = va_arg( ap, long );
364                oldoutc = outc;
365                outc += sprintf(tempbuf, "%ld", lval);
366             }
367             else if (ch == 'u')
368             {
369                ulval = va_arg( ap, unsigned long );
370                oldoutc = outc;
371                outc += sprintf(tempbuf, "%lu", ulval);
372             }
373             else
374             {
375                /* Error */
376                sprintf(outbuf, "IJB(%d) Error: log_error(): Bad format string:\n"
377                                "Format = \"%s\"\n"
378                                "Exiting.", this_thread, fmt);
379                /* FIXME RACE HAZARD: should start critical section error_log_use here */
380                if( !logfp )
381                {
382                   logfp = stderr;
383                }
384                fputs(outbuf, logfp);
385                /* FIXME RACE HAZARD: should end critical section error_log_use here */
386                fatal_error(outbuf);
387                /* Never get here */
388                break;
389             }
390             if (outc < BUFSIZ-1) 
391             {
392                strcpy(outbuf + oldoutc, tempbuf);
393             }
394             else
395             {
396                outbuf[oldoutc] = '\0';
397             }
398             break;
399          case 'c':
400             /*
401              * Note that char paramaters are converted to int, so we need to
402              * pass "int" to va_arg.  (See K&R, 2nd ed, section A7.3.2, page 202)
403              */
404             outbuf[outc++] = (char) va_arg( ap, int );
405             break;
406          case 's':
407             sval = va_arg( ap, char * );
408             oldoutc = outc;
409             outc += strlen(sval);
410             if (outc < BUFSIZ-1) 
411             {
412                strcpy(outbuf + oldoutc, sval);
413             }
414             else
415             {
416                outbuf[oldoutc] = '\0';
417             }
418             break;
419          case 'E':
420             /* Non-standard: Print error code from errno */
421             ival = errno;
422 #ifndef NOSTRERROR
423             sval = strerror(ival);
424 #else /* def NOSTRERROR */
425             sval = NULL
426 #endif /* def NOSTRERROR */
427             if (sval == NULL)
428             {
429                sprintf(tempbuf, "(errno = %d)", ival);
430                sval = tempbuf;
431             }
432             oldoutc = outc;
433             outc += strlen(sval);
434             if (outc < BUFSIZ-1) 
435             {
436                strcpy(outbuf + oldoutc, sval);
437             }
438             else
439             {
440                outbuf[oldoutc] = '\0';
441             }
442             break;
443          default:
444             sprintf(outbuf, "IJB(%d) Error: log_error(): Bad format string:\n"
445                             "Format = \"%s\"\n"
446                             "Exiting.", this_thread, fmt);
447             /* FIXME RACE HAZARD: should start critical section error_log_use here */
448             if( !logfp )
449             {
450                logfp = stderr;
451             }
452             fputs(outbuf, logfp);
453             /* FIXME RACE HAZARD: should end critical section error_log_use here */
454             fatal_error(outbuf);
455             /* Never get here */
456             break;
457
458       } /* switch( p ) */
459
460    } /* for( p ... ) */
461    
462    /* done with var. args */
463    va_end( ap );
464    
465    if (outc >= BUFSIZ-2)
466    {
467       /* insufficient room for newline and trailing null. */
468
469       static const char warning[] = "... [too long, truncated]\n";
470
471       if (outc < BUFSIZ)
472       {
473          /* Need to add terminating null in this case. */
474          outbuf[outc] = '\0';
475       }
476
477       /* Truncate output */
478       outbuf[BUFSIZ - sizeof(warning)] = '\0';
479
480       /* Append warning */
481       strcat(outbuf, warning);
482    }
483    else
484    {
485       /* Add terminating newline and null */
486       outbuf[outc++] = '\n';
487       outbuf[outc] = '\0';
488    }
489
490    /* FIXME RACE HAZARD: should start critical section error_log_use here */
491
492    /* deal with glibc stupidity - it won't let you initialize logfp */
493    if( !logfp )
494    {
495       logfp = stderr;
496    }
497
498    fputs(outbuf, logfp);
499
500    if (loglevel == LOG_LEVEL_FATAL)
501    {
502       fatal_error(outbuf);
503       /* Never get here */
504    }
505
506    /* FIXME RACE HAZARD: should end critical section error_log_use here */
507
508 #if defined(_WIN32) && !defined(_WIN_CONSOLE)
509    /* Write to display */
510    LogPutString(outbuf);
511    LogShowActivity();
512 #endif /* defined(_WIN32) && !defined(_WIN_CONSOLE) */
513
514 }
515
516
517 /*
518   Local Variables:
519   tab-width: 3
520   end:
521 */
522