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