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