Automatic reloading of config file.
[privoxy.git] / w32log.c
1 const char w32log_rcs[] = "$Id: w32log.c,v 1.4 2001/05/22 18:56:28 oes Exp $";
2 /*********************************************************************
3  *
4  * File        :  $Source: /cvsroot/ijbswa/current/w32log.c,v $
5  *
6  * Purpose     :  Functions for creating and destroying the log window,
7  *                ouputting strings, processing messages and so on.
8  *
9  * Copyright   :  Written by and Copyright (C) 2001 the SourceForge
10  *                IJBSWA team.  http://ijbswa.sourceforge.net
11  *
12  *                Written by and Copyright (C) 1999 Adam Lock
13  *                <locka@iol.ie>
14  *
15  *                This program is free software; you can redistribute it 
16  *                and/or modify it under the terms of the GNU General
17  *                Public License as published by the Free Software
18  *                Foundation; either version 2 of the License, or (at
19  *                your option) any later version.
20  *
21  *                This program is distributed in the hope that it will
22  *                be useful, but WITHOUT ANY WARRANTY; without even the
23  *                implied warranty of MERCHANTABILITY or FITNESS FOR A
24  *                PARTICULAR PURPOSE.  See the GNU General Public
25  *                License for more details.
26  *
27  *                The GNU General Public License should be included with
28  *                this file.  If not, you can view it at
29  *                http://www.gnu.org/copyleft/gpl.html
30  *                or write to the Free Software Foundation, Inc., 59
31  *                Temple Place - Suite 330, Boston, MA  02111-1307, USA.
32  *
33  * Revisions   :
34  *    $Log: w32log.c,v $
35  *    Revision 1.4  2001/05/22 18:56:28  oes
36  *    CRLF -> LF
37  *
38  *    Revision 1.3  2001/05/20 15:07:54  jongfoster
39  *    File is now ignored if _WIN_CONSOLE is defined.
40  *
41  *    Revision 1.2  2001/05/20 01:21:20  jongfoster
42  *    Version 2.9.4 checkin.
43  *    - Merged popupfile and cookiefile, and added control over PCRS
44  *      filtering, in new "permissionsfile".
45  *    - Implemented LOG_LEVEL_FATAL, so that if there is a configuration
46  *      file error you now get a message box (in the Win32 GUI) rather
47  *      than the program exiting with no explanation.
48  *    - Made killpopup use the PCRS MIME-type checking and HTTP-header
49  *      skipping.
50  *    - Removed tabs from "config"
51  *    - Moved duplicated url parsing code in "loaders.c" to a new funcition.
52  *    - Bumped up version number.
53  *
54  *    Revision 1.1.1.1  2001/05/15 13:59:07  oes
55  *    Initial import of version 2.9.3 source tree
56  *
57  *
58  *********************************************************************/
59 \f
60
61 #include "config.h"
62
63 #include <assert.h>
64 #include <stdio.h>
65
66 #include <windows.h>
67 #include <richedit.h>
68
69 #include "project.h"
70 #include "w32log.h"
71 #include "w32taskbar.h"
72 #include "w32rulesdlg.h"
73 #include "win32.h"
74 #include "w32res.h"
75 #include "jcc.h"
76 #include "miscutil.h"
77 #include "errlog.h"
78
79 const char w32res_h_rcs[] = W32RES_H_VERSION;
80
81 #ifdef __MINGW32__
82 #include "cygwin.h"
83 const char cygwin_h_rcs[] = CYGWIN_H_VERSION;
84 #endif
85
86 const char w32log_h_rcs[] = W32LOG_H_VERSION;
87
88 #ifndef _WIN_CONSOLE /* entire file */
89
90 /*
91  * Timers and the various durations
92  */
93 #define TIMER_ANIM_ID               1
94 #define TIMER_ANIM_TIME             100
95 #define TIMER_ANIMSTOP_ID           2
96 #define TIMER_ANIMSTOP_TIME         1000
97 #define TIMER_CLIPBUFFER_ID         3
98 #define TIMER_CLIPBUFFER_TIME       1000
99 #define TIMER_CLIPBUFFER_FORCE_ID   4
100 #define TIMER_CLIPBUFFER_FORCE_TIME 5000
101
102 /*
103  * Styles of text that can be output
104  */
105 #define STYLE_NONE      0
106 #define STYLE_HIGHLIGHT 1
107 #define STYLE_LINK      2
108 #define STYLE_HEADER    3
109
110 /*
111  * Number of frames of animation in tray activity sequence
112  */
113 #define ANIM_FRAMES 8
114
115 #define DEFAULT_MAX_BUFFER_LINES    200
116 #define DEFAULT_LOG_FONT_NAME       "MS Sans Serif"
117 #define DEFAULT_LOG_FONT_SIZE       8
118
119 /*
120  * These values affect the way the log window behaves, they should be read
121  * from a file but for the moment, they are hardcoded here. Some options are
122  * configurable through the UI.
123  */
124
125 /* Indicates whether task bar shows activity animation */
126 BOOL g_bShowActivityAnimation = 1;
127
128 /* Indicates if the log window appears on the task bar */
129 BOOL g_bShowOnTaskBar = 0;
130
131 /* Indicates whether closing the log window really just hides it */
132 BOOL g_bCloseHidesWindow = 1;
133
134 /* Indicates if messages are logged at all */
135 BOOL g_bLogMessages = 1;
136
137 /* Indicates whether log messages are highlighted */
138 BOOL g_bHighlightMessages = 1;
139
140 /* Indicates if buffer is limited in size */
141 BOOL g_bLimitBufferSize = 1;
142
143 /* Maximum number of lines allowed in buffer when limited */
144 int g_nMaxBufferLines = DEFAULT_MAX_BUFFER_LINES;
145
146 /* Font to use */
147 char g_szFontFaceName[255] = DEFAULT_LOG_FONT_NAME;
148
149 /* Size of font to use */
150 int g_nFontSize = DEFAULT_LOG_FONT_SIZE;
151
152
153 /* FIXME: this is a kludge */
154
155 const char * g_blockfile = NULL;
156 const char * g_permissions_file = NULL;
157 const char * g_forwardfile = NULL;
158 #ifdef ACL_FILES
159 const char * g_aclfile = NULL;
160 #endif /* def ACL_FILES */
161 #ifdef USE_IMAGE_LIST
162 const char * g_imagefile = NULL;
163 #endif /* def USE_IMAGE_LIST */
164 #ifdef PCRS
165 const char * g_re_filterfile = NULL;
166 #endif
167 #ifdef TRUST_FILES
168 const char * g_trustfile = NULL;
169 #endif /* def TRUST_FILES */
170
171 /* FIXME: end kludge */
172
173
174 #ifdef REGEX
175 /* Regular expression for detected URLs */
176 #define RE_URL "http:[^ \n\r]*"
177
178 /*
179  * Regular expressions that are used to perform highlight in the log window
180  */
181 static struct _Pattern
182 {
183    const char *str;
184    int style;
185    regex_t buffer;
186 } patterns_to_highlight[] =
187 {
188    /* url headers */
189    { RE_URL,                STYLE_LINK },
190 /* { "[a-zA-Z0-9]+\\.[a-zA-Z0-9]+\\.[a-zA-Z0-9]+\\.[^ \n\r]*", STYLE_LINK }, */
191    /* interesting text to highlight */
192    { "crunch!",           STYLE_HIGHLIGHT },
193    /* http headers */
194    { "referer:",            STYLE_HEADER },
195    { "proxy-connection:",   STYLE_HEADER },
196    { "proxy-agent:",        STYLE_HEADER },
197    { "user-agent:",         STYLE_HEADER },
198    { "host:",               STYLE_HEADER },
199    { "accept:",             STYLE_HEADER },
200    { "accept-encoding:",    STYLE_HEADER },
201    { "accept-language:",    STYLE_HEADER },
202    { "accept-charset:",     STYLE_HEADER },
203    { "accept-ranges:",      STYLE_HEADER },
204    { "date:",               STYLE_HEADER },
205    { "cache-control:",      STYLE_HEADER },
206    { "cache-last-checked:", STYLE_HEADER },
207    { "connection:",         STYLE_HEADER },
208    { "content-type",        STYLE_HEADER },
209    { "content-length",      STYLE_HEADER },
210    { "cookie",              STYLE_HEADER },
211    { "last-modified:",      STYLE_HEADER },
212    { "pragma:",             STYLE_HEADER },
213    { "server:",             STYLE_HEADER },
214    { "etag:",               STYLE_HEADER },
215    { "expires:",            STYLE_HEADER },
216    { "warning:",            STYLE_HEADER },
217    /* this is the terminator statement - do not delete! */
218    { NULL,                  STYLE_NONE }
219 };
220 #endif /* def REGEX */
221
222
223 /*
224  * Public variables
225  */
226 HWND g_hwndLogFrame;
227
228 /*
229  * Private variables
230  */
231 static CRITICAL_SECTION g_criticalsection;
232 static HWND g_hwndTray;
233 static HWND g_hwndLogBox;
234 static WNDPROC g_fnLogBox;
235 static HICON g_hiconAnim[ANIM_FRAMES];
236 static HICON g_hiconIdle;
237 static HICON g_hiconApp;
238 static int g_nAnimFrame;
239 static BOOL g_bClipPending = FALSE;
240 static int g_nRichEditVersion = 0;
241
242 /*
243  * Private functions
244  */
245 static HWND CreateLogWindow(HINSTANCE hInstance, int nCmdShow);
246 static HWND CreateHiddenLogOwnerWindow(HINSTANCE hInstance);
247 static LRESULT CALLBACK LogWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
248 static LRESULT CALLBACK LogOwnerWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
249 static LRESULT CALLBACK LogRichEditProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
250 static BOOL InitRichEdit(void);
251 static void LogClipBuffer(void);
252 static void LogCreatePatternMatchingBuffers(void);
253 static void LogDestroyPatternMatchingBuffers(void);
254 static int LogPutStringNoMatch(const char *pszText, int style);
255
256
257 /*********************************************************************
258  *
259  * Function    :  InitLogWindow
260  *
261  * Description :  Initialise the log window.
262  *
263  * Parameters  :  None
264  *
265  * Returns     :  Always TRUE (there should be error checking on the resources).
266  *
267  *********************************************************************/
268 BOOL InitLogWindow(void)
269 {
270    int i;
271
272    /* Load the icons */
273    g_hiconIdle = LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_IDLE));
274    for (i = 0; i < ANIM_FRAMES; i++)
275    {
276       g_hiconAnim[i] = LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_JUNKBUSTER1 + i));
277    }
278    g_hiconApp = LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_JUNKBUSTER));
279
280    /* Create the user interface */
281    g_hwndLogFrame = CreateLogWindow(g_hInstance, g_nCmdShow);
282    g_hwndTray = CreateTrayWindow(g_hInstance);
283    TrayAddIcon(g_hwndTray, 1, g_hiconApp, "Junkbuster");
284
285    /* Create pattern matching buffers (for highlighting */
286    LogCreatePatternMatchingBuffers();
287
288    /* Create a critical section to protect multi-threaded access to certain things */
289    InitializeCriticalSection(&g_criticalsection);
290
291    return TRUE;
292
293 }
294
295
296 /*********************************************************************
297  *
298  * Function    :  TermLogWindow
299  *
300  * Description :  Cleanup the logwindow.
301  *
302  * Parameters  :  None
303  *
304  * Returns     :  N/A
305  *
306  *********************************************************************/
307 void TermLogWindow(void)
308 {
309    int i;
310
311    LogDestroyPatternMatchingBuffers();
312
313    TrayDeleteIcon(g_hwndTray, 1);
314    DeleteObject(g_hiconApp);
315    DeleteObject(g_hiconIdle);
316    for (i = 0; i < ANIM_FRAMES; i++)
317    {
318       DeleteObject(g_hiconAnim[i]);
319    }
320
321 }
322
323
324 /*********************************************************************
325  *
326  * Function    :  LogCreatePatternMatchingBuffers
327  *
328  * Description :  Compile the pattern matching buffers.
329  *
330  * Parameters  :  None
331  *
332  * Returns     :  N/A
333  *
334  *********************************************************************/
335 void LogCreatePatternMatchingBuffers(void)
336 {
337 #ifdef REGEX
338    int i;
339    for (i = 0; patterns_to_highlight[i].str != NULL; i++)
340    {
341       regcomp(&patterns_to_highlight[i].buffer, patterns_to_highlight[i].str, REG_ICASE);
342    }
343 #endif
344
345 }
346
347
348 /*********************************************************************
349  *
350  * Function    :  LogDestroyPatternMatchingBuffers
351  *
352  * Description :  Free up the pattern matching buffers.
353  *
354  * Parameters  :  None
355  *
356  * Returns     :  N/A
357  *
358  *********************************************************************/
359 void LogDestroyPatternMatchingBuffers(void)
360 {
361 #ifdef REGEX
362    int i;
363    for (i = 0; patterns_to_highlight[i].str != NULL; i++)
364    {
365       regfree(&patterns_to_highlight[i].buffer);
366    }
367 #endif
368
369 }
370
371
372 /*********************************************************************
373  *
374  * Function    :  LogGetURLUnderCursor
375  *
376  * Description :  Returns the URL from under the cursor (remember to free it!).
377  *
378  * Parameters  :  None
379  *
380  * Returns     :  NULL or a pointer to an URL string.
381  *
382  *********************************************************************/
383 char *LogGetURLUnderCursor(void)
384 {
385    char *szResult = NULL;
386 #ifdef REGEX
387    regex_t re;
388    POINT ptCursor;
389    POINTL ptl;
390    DWORD nPos;
391    DWORD nWordStart = 0;
392    DWORD nWordEnd = 0;
393
394    regcomp(&re, RE_URL, REG_ICASE);
395
396    /* Get the position of the cursor over the text window */
397    GetCursorPos(&ptCursor);
398    ScreenToClient(g_hwndLogBox, &ptCursor);
399    ptl.x = ptCursor.x;
400    ptl.y = ptCursor.y;
401
402    /* Search backwards and fowards to obtain the word that is highlighted */
403    nPos = LOWORD(SendMessage(g_hwndLogBox, EM_CHARFROMPOS, 0, (LPARAM) &ptl));
404    nWordStart = SendMessage(g_hwndLogBox, EM_FINDWORDBREAK, WB_LEFT, nPos);
405    nWordEnd = SendMessage(g_hwndLogBox, EM_FINDWORDBREAK, WB_RIGHTBREAK, nPos);
406
407    /* Compare the string to the pattern */
408    if (nWordEnd > nWordStart)
409    {
410       TEXTRANGE range;
411       regmatch_t match;
412
413       range.chrg.cpMin = nWordStart;
414       range.chrg.cpMax = nWordEnd;
415       range.lpstrText = (LPSTR)zalloc(nWordEnd - nWordStart + 1);
416       SendMessage(g_hwndLogBox, EM_GETTEXTRANGE, 0, (LPARAM) &range);
417
418       if (regexec(&re, range.lpstrText, 1, &match, 0) == 0)
419       {
420          szResult = range.lpstrText;
421       }
422       else
423       {
424          free(range.lpstrText);
425       }
426
427       regfree(&re);
428    }
429 #endif
430    return szResult;
431
432 }
433
434
435 /*********************************************************************
436  *
437  * Function    :  LogPutString
438  *
439  * Description :  Inserts text into the logging window.  This is really
440  *                a REGEXP aware wrapper function to `LogPutStringNoMatch'.
441  *
442  * Parameters  :
443  *          1  :  pszText = pointer to string going to the log window
444  *
445  * Returns     :  1 => success, else the return code from `LogPutStringNoMatch'.
446  *                FIXME: this is backwards to the rest of IJB and to common
447  *                programming practice.  Please use 0 => success instead.
448  *
449  *********************************************************************/
450 int LogPutString(const char *pszText)
451 {
452 #ifdef REGEX
453    int i;
454 #endif
455    int result = 0;
456
457    if (pszText == NULL || strlen(pszText) == 0)
458    {
459       return 1;
460    }
461
462    if (!g_bLogMessages)
463    {
464       return 1;
465    }
466
467    /* Critical section stops multiple threads doing nasty interactions that
468     * foul up the highlighting and output.
469     */
470    EnterCriticalSection(&g_criticalsection);
471
472 #ifdef REGEX
473    if (g_bHighlightMessages)
474    {
475       regmatch_t match;
476
477       /* First things first, regexp scan for various things that we would like highlighted */
478       for (i = 0; patterns_to_highlight[i].str != NULL; i++)
479       {
480          if (regexec(&patterns_to_highlight[i].buffer, pszText, 1, &match, 0) == 0)
481          {
482             char *pszBefore = NULL;
483             char *pszMatch = NULL;
484             char *pszAfter = NULL;
485             int nMatchSize;
486
487             /* Split the string up into pieces representing the strings, before
488                at and after the matching pattern
489              */
490             if (match.rm_so > 0)
491             {
492                pszBefore = (char *)malloc((match.rm_so + 1) * sizeof(char));
493                memset(pszBefore, 0, (match.rm_so + 1) * sizeof(char));
494                strncpy(pszBefore, pszText, match.rm_so);
495             }
496             if (match.rm_eo < strlen(pszText))
497             {
498                pszAfter = strdup(&pszText[match.rm_eo]);
499             }
500             nMatchSize = match.rm_eo - match.rm_so;
501             pszMatch = (char *)malloc(nMatchSize + 1);
502             strncpy(pszMatch, &pszText[match.rm_so], nMatchSize);
503             pszMatch[nMatchSize] = '\0';
504
505             /* Recursively call LogPutString */
506             if (pszBefore)
507             {
508                LogPutString(pszBefore);
509                free(pszBefore);
510             }
511             if (pszMatch)
512             {
513                LogPutStringNoMatch(pszMatch, patterns_to_highlight[i].style);
514                free(pszMatch);
515             }
516             if (pszAfter)
517             {
518                LogPutString(pszAfter);
519                free(pszAfter);
520             }
521
522             result = 1;
523             goto end;
524          }
525       }
526    }
527 #endif
528
529    result = LogPutStringNoMatch(pszText, STYLE_NONE);
530
531 #ifdef REGEX
532 end:
533 #endif
534    LeaveCriticalSection(&g_criticalsection);
535
536    return result;
537
538 }
539
540
541 /*********************************************************************
542  *
543  * Function    :  LogPutStringNoMatch
544  *
545  * Description :  Puts a string into the logging window.
546  *
547  * Parameters  :
548  *          1  :  pszText = pointer to string going to the log window
549  *          2  :  style = STYLE_NONE, STYLE_HEADER, STYLE_HIGHLIGHT, or STYLE_LINK
550  *
551  * Returns     :  Always 1 => success.
552  *                FIXME: this is backwards to the rest of IJB and to common
553  *                programming practice.  Please use 0 => success instead.
554  *
555  *********************************************************************/
556 int LogPutStringNoMatch(const char *pszText, int style)
557 {
558    CHARRANGE range;
559    CHARFORMAT format;
560    int nTextLength;
561
562    assert(g_hwndLogBox);
563    if (g_hwndLogBox == NULL)
564    {
565       return 1;
566    }
567
568    /* TODO preserve existing selection */
569
570    /* Go to the end of the text */
571    nTextLength = GetWindowTextLength(g_hwndLogBox);
572    range.cpMin = nTextLength;
573    range.cpMax = nTextLength;
574    SendMessage(g_hwndLogBox, EM_EXSETSEL, 0, (LPARAM) &range);
575
576    /* Apply a formatting style */
577    memset(&format, 0, sizeof(format));
578    format.cbSize = sizeof(format);
579    format.dwMask = CFM_BOLD | CFM_UNDERLINE | CFM_STRIKEOUT | CFM_ITALIC | CFM_COLOR | CFM_FACE | CFM_SIZE;
580    format.yHeight = (g_nFontSize * 1440) / 72;
581    strcpy(format.szFaceName, g_szFontFaceName);
582    if (style == STYLE_NONE)
583    {
584       /* DO NOTHING */
585       format.dwEffects |= CFE_AUTOCOLOR;
586    }
587    else if (style == STYLE_HEADER)
588    {
589       format.dwEffects |= CFE_AUTOCOLOR | CFE_ITALIC;
590    }
591    else if (style == STYLE_HIGHLIGHT)
592    {
593       format.dwEffects |= CFE_AUTOCOLOR | CFE_BOLD;
594    }
595    else if (style == STYLE_LINK)
596    {
597       format.dwEffects |= CFE_UNDERLINE;
598       format.crTextColor = RGB(0, 0, 255);
599    }
600    SendMessage(g_hwndLogBox, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM) &format);
601
602    /* Append text to the end */
603    SendMessage(g_hwndLogBox, EM_REPLACESEL, FALSE, (LPARAM) pszText);
604
605    /* TODO Restore the old selection */
606
607    /* Purge buffer */
608    if (strchr(pszText, '\n') != NULL)
609    {
610       SetTimer(g_hwndLogFrame, TIMER_CLIPBUFFER_ID, TIMER_CLIPBUFFER_TIME, NULL);
611       if (!g_bClipPending)
612       {
613          /* Set the force clip timer going. This timer ensures clipping is done
614             intermittently even when there is a sustained burst of logging
615          */
616          SetTimer(g_hwndLogFrame, TIMER_CLIPBUFFER_FORCE_ID, TIMER_CLIPBUFFER_FORCE_TIME, NULL);
617       }
618       g_bClipPending = TRUE;
619    }
620
621    return 1;
622
623 }
624
625
626 /*********************************************************************
627  *
628  * Function    :  LogShowActivity
629  *
630  * Description :  Start the spinner.
631  *
632  * Parameters  :  None
633  *
634  * Returns     :  N/A
635  *
636  *********************************************************************/
637 void LogShowActivity(void)
638 {
639    /* Start some activity timers */
640    if (g_bShowActivityAnimation)
641    {
642       SetTimer(g_hwndLogFrame, TIMER_ANIM_ID, TIMER_ANIM_TIME, NULL);
643       SetTimer(g_hwndLogFrame, TIMER_ANIMSTOP_ID, TIMER_ANIMSTOP_TIME, NULL);
644    }
645
646 }
647
648
649 /*********************************************************************
650  *
651  * Function    :  LogClipBuffer
652  *
653  * Description :  Prunes old lines from the log.
654  *
655  * Parameters  :  None
656  *
657  * Returns     :  N/A
658  *
659  *********************************************************************/
660 void LogClipBuffer(void)
661 {
662    int nLines = SendMessage(g_hwndLogBox, EM_GETLINECOUNT, 0, 0);
663    if (g_bLimitBufferSize && nLines > g_nMaxBufferLines)
664    {
665       /* Compute the range representing the lines to be deleted */
666       LONG nLastLineToDelete = nLines - g_nMaxBufferLines;
667       LONG nLastChar = SendMessage(g_hwndLogBox, EM_LINEINDEX, nLastLineToDelete, 0);
668       CHARRANGE range;
669       range.cpMin = 0;
670       range.cpMax = nLastChar;
671
672       /* TODO get current selection */
673
674       /* TODO adjust and clip old selection against range to be deleted */
675
676       /* Select range and erase it (turning off autoscroll to prevent
677          nasty scrolling) */
678       SendMessage(g_hwndLogBox, EM_SETOPTIONS, ECOOP_XOR, ECO_AUTOVSCROLL);
679       SendMessage(g_hwndLogBox, EM_EXSETSEL, 0, (LPARAM) &range);
680       SendMessage(g_hwndLogBox, EM_REPLACESEL, FALSE, (LPARAM) "");
681       SendMessage(g_hwndLogBox, EM_SETOPTIONS, ECOOP_XOR, ECO_AUTOVSCROLL);
682
683       /* Restore old selection */
684    }
685
686 }
687
688
689 /*********************************************************************
690  *
691  * Function    :  CreateHiddenLogOwnerWindow
692  *
693  * Description :  Creates a hidden owner window that stops the log
694  *                window appearing in the task bar.
695  *
696  * Parameters  :
697  *          1  :  hInstance = application's instance handle
698  *
699  * Returns     :  Handle to newly created window.
700  *
701  *********************************************************************/
702 HWND CreateHiddenLogOwnerWindow(HINSTANCE hInstance)
703 {
704    static const char *szWndName = "JunkbusterLogLogOwner";
705    WNDCLASS wc;
706    HWND hwnd;
707
708    wc.style          = 0;
709    wc.lpfnWndProc    = LogOwnerWindowProc;
710    wc.cbClsExtra     = 0;
711    wc.cbWndExtra     = 0;
712    wc.hInstance      = hInstance;
713    wc.hIcon          = 0;
714    wc.hCursor        = 0;
715    wc.hbrBackground  = 0;
716    wc.lpszMenuName   = 0;
717    wc.lpszClassName  = szWndName;
718
719    RegisterClass(&wc);
720
721    hwnd = CreateWindow(szWndName, szWndName,
722       WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
723       CW_USEDEFAULT, NULL, NULL, hInstance, NULL );
724
725    return hwnd;
726
727 }
728
729
730 /*********************************************************************
731  *
732  * Function    :  LogOwnerWindowProc
733  *
734  * Description :  Dummy procedure that does nothing special.
735  *
736  * Parameters  :
737  *          1  :  hwnd = window handle
738  *          2  :  uMsg = message number
739  *          3  :  wParam = first param for this message
740  *          4  :  lParam = next param for this message
741  *
742  * Returns     :  Same as `DefWindowProc'.
743  *
744  *********************************************************************/
745 LRESULT CALLBACK LogOwnerWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
746 {
747    return DefWindowProc(hwnd, uMsg, wParam, lParam);
748
749 }
750
751
752 /*********************************************************************
753  *
754  * Function    :  CreateLogWindow
755  *
756  * Description :  Create the logging window.
757  *
758  * Parameters  :
759  *          1  :  hInstance = application's instance handle
760  *          2  :  nCmdShow = window show value (MIN, MAX, NORMAL, etc...)
761  *
762  * Returns     :  Handle to newly created window.
763  *
764  *********************************************************************/
765 HWND CreateLogWindow(HINSTANCE hInstance, int nCmdShow)
766 {
767    static const char *szWndName = "JunkbusterLogWindow";
768    static const char *szWndTitle = "Junkbuster";
769
770    HWND hwnd = NULL;
771    HWND hwndOwner = (g_bShowOnTaskBar) ? NULL : CreateHiddenLogOwnerWindow(hInstance);
772    HWND hwndChild = NULL;
773    RECT rcClient;
774    WNDCLASSEX wc;
775
776    memset(&wc, 0, sizeof(wc));
777    wc.cbSize         = sizeof(wc);
778    wc.style          = CS_DBLCLKS;
779    wc.lpfnWndProc    = LogWindowProc;
780    wc.cbClsExtra     = 0;
781    wc.cbWndExtra     = 0;
782    wc.hInstance      = hInstance;
783    wc.hIcon          = g_hiconApp;
784    wc.hCursor        = 0;
785    wc.hbrBackground  = 0;
786    wc.lpszMenuName   = MAKEINTRESOURCE(IDR_LOGVIEW);
787    wc.lpszClassName  = szWndName;
788    wc.hbrBackground  = GetStockObject(WHITE_BRUSH);
789    RegisterClassEx(&wc);
790
791    hwnd = CreateWindowEx(WS_EX_APPWINDOW, szWndName, szWndTitle,
792       WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
793       CW_USEDEFAULT, hwndOwner, NULL, hInstance, NULL);
794
795    /* Now create a child list box */
796    GetClientRect(hwnd, &rcClient);
797
798    /* Create a rich edit control */
799    InitRichEdit();
800    g_hwndLogBox = CreateWindowEx(0, (g_nRichEditVersion == 0x0100) ? "RichEdit" : RICHEDIT_CLASS, "",
801       ES_AUTOVSCROLL | ES_MULTILINE | ES_READONLY | ES_NOHIDESEL | WS_CHILD | WS_VSCROLL | WS_HSCROLL | WS_VISIBLE,
802       rcClient.left, rcClient.top, rcClient.right, rcClient.bottom,
803       hwnd, NULL, hInstance, NULL);
804 /* SendMessage(g_hwndLogBox, EM_SETWORDWRAPMODE, 0, 0); */
805
806    /* Subclass the control to catch certain messages */
807    g_fnLogBox = (WNDPROC) GetWindowLong(g_hwndLogBox, GWL_WNDPROC);
808    SetWindowLong(g_hwndLogBox, GWL_WNDPROC, (LONG) LogRichEditProc);
809
810    /* Minimizing looks stupid when the log window is not on the task bar, so hide instead */
811    if (!g_bShowOnTaskBar &&
812          (nCmdShow == SW_SHOWMINIMIZED ||
813           nCmdShow == SW_MINIMIZE ||
814           nCmdShow == SW_SHOWMINNOACTIVE))
815    {
816       nCmdShow = SW_HIDE;
817    }
818
819    ShowWindow(hwnd, nCmdShow);
820    UpdateWindow(hwnd);
821
822    GetClientRect(g_hwndLogFrame, &rcClient);
823    SetWindowPos(g_hwndLogBox, NULL, rcClient.left, rcClient.top, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top, SWP_NOZORDER);
824
825    return hwnd;
826
827 }
828
829
830 /*********************************************************************
831  *
832  * Function    :  InitRichEdit
833  *
834  * Description :  Initialise the rich edit control library.
835  *
836  * Parameters  :  None
837  *
838  * Returns     :  TRUE => success, FALSE => failure.
839  *                FIXME: this is backwards to the rest of IJB and to common
840  *                programming practice.  Please use 0 => success instead.
841  *
842  *********************************************************************/
843 BOOL InitRichEdit(void)
844 {
845    static HINSTANCE hInstRichEdit;
846    if (hInstRichEdit == NULL)
847    {
848       g_nRichEditVersion = 0;
849       hInstRichEdit = LoadLibraryA("RICHED20.DLL");
850       if (hInstRichEdit)
851       {
852          g_nRichEditVersion = _RICHEDIT_VER;
853       }
854       else
855       {
856          hInstRichEdit = LoadLibraryA("RICHED32.DLL");
857          if (hInstRichEdit)
858          {
859             g_nRichEditVersion = 0x0100;
860          }
861       }
862    }
863    return (hInstRichEdit != NULL) ? TRUE : FALSE;
864
865 }
866
867
868 /*********************************************************************
869  *
870  * Function    :  ShowLogWindow
871  *
872  * Description :  Shows or hides the log window.  We will also raise the
873  *                window on a show command in case it is buried.
874  *
875  * Parameters  :
876  *          1  :  bShow = TRUE to show, FALSE to mimize/hide
877  *
878  * Returns     :  N/A
879  *
880  *********************************************************************/
881 void ShowLogWindow(BOOL bShow)
882 {
883    if (bShow)
884    {
885       SetForegroundWindow(g_hwndLogFrame);
886       SetWindowPos(g_hwndLogFrame, HWND_TOP, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE);
887    }
888    else if (g_bShowOnTaskBar)
889    {
890       ShowWindow(g_hwndLogFrame, SW_MINIMIZE);
891    }
892    else
893    {
894       ShowWindow(g_hwndLogFrame, SW_HIDE);
895    }
896
897 }
898
899
900 /*********************************************************************
901  *
902  * Function    :  EditFile
903  *
904  * Description :  Opens the specified setting file for editing.
905  *
906  * Parameters  :
907  *          1  :  filename = filename from the config (aka junkbstr.txt) file.
908  *
909  * Returns     :  N/A
910  *
911  *********************************************************************/
912 void EditFile(const char *filename)
913 {
914    if (filename)
915    {
916       ShellExecute(g_hwndLogFrame, "open", filename, NULL, NULL, SW_SHOWNORMAL);
917    }
918
919 }
920
921
922 /*--------------------------------------------------------------------------*/
923 /* Windows message handlers                                                 */
924 /*--------------------------------------------------------------------------*/
925
926
927 /*********************************************************************
928  *
929  * Function    :  OnLogRButtonUp
930  *
931  * Description :  Handler for WM_RBUTTONUP messages.
932  *
933  * Parameters  :
934  *          1  :  nModifier = wParam from mouse message (unused)
935  *          2  :  x = x coordinate of the mouse event
936  *          3  :  y = y coordinate of the mouse event
937  *
938  * Returns     :  N/A
939  *
940  *********************************************************************/
941 void OnLogRButtonUp(int nModifier, int x, int y)
942 {
943    HMENU hMenu = LoadMenu(g_hInstance, MAKEINTRESOURCE(IDR_POPUP_SELECTION));
944    if (hMenu != NULL)
945    {
946       HMENU hMenuPopup = GetSubMenu(hMenu, 0);
947       char *szURL;
948
949       /* Check if there is a selection */
950       CHARRANGE range;
951       SendMessage(g_hwndLogBox, EM_EXGETSEL, 0, (LPARAM) &range);
952       if (range.cpMin == range.cpMax)
953       {
954          EnableMenuItem(hMenuPopup, ID_EDIT_COPY, MF_BYCOMMAND | MF_GRAYED);
955       }
956       else
957       {
958          EnableMenuItem(hMenuPopup, ID_EDIT_COPY, MF_BYCOMMAND | MF_ENABLED);
959       }
960
961       /* Check if cursor is over a link */
962       szURL = LogGetURLUnderCursor();
963       if (szURL)
964       {
965          MENUITEMINFO item;
966          TCHAR szMenuItemTemplate[1000];
967          char *szMenuItem;
968
969          memset(&item, 0, sizeof(item));
970          item.cbSize = sizeof(item);
971          item.fMask = MIIM_TYPE | MIIM_ID | MIIM_STATE;
972          item.fType = MFT_STRING;
973          item.fState = MFS_ENABLED;
974          item.wID = ID_NEW_BLOCKER;
975
976          /* Put the item into the menu */
977          memset(szMenuItemTemplate, 0, sizeof(szMenuItemTemplate));
978          LoadString(g_hInstance, IDS_NEW_BLOCKER, szMenuItemTemplate, sizeof(szMenuItemTemplate) / sizeof(szMenuItemTemplate[0]));
979
980          szMenuItem = (char *)malloc(strlen(szMenuItemTemplate) + strlen(szURL) + 1);
981          sprintf(szMenuItem, szMenuItemTemplate, szURL);
982
983          item.dwTypeData = szMenuItem;
984          item.cch = strlen(szMenuItem);
985
986          InsertMenuItem(hMenuPopup, 1, TRUE, &item);
987
988          SetDefaultRule(szURL);
989
990          free(szURL);
991       }
992
993       /* Display the popup */
994       TrackPopupMenu(hMenuPopup, TPM_LEFTALIGN | TPM_TOPALIGN | TPM_RIGHTBUTTON, x, y, 0, g_hwndLogFrame, NULL);
995       DestroyMenu(hMenu);
996    }
997
998 }
999
1000
1001 /*********************************************************************
1002  *
1003  * Function    :  OnLogCommand
1004  *
1005  * Description :  Handler for WM_COMMAND messages.
1006  *
1007  * Parameters  :
1008  *          1  :  nCommand = the command portion of the menu selection event
1009  *
1010  * Returns     :  N/A
1011  *
1012  *********************************************************************/
1013 void OnLogCommand(int nCommand)
1014 {
1015    switch (nCommand)
1016    {
1017       case ID_SHOWWINDOW:
1018          ShowLogWindow(TRUE);
1019          break;
1020
1021       case ID_FILE_EXIT:
1022          PostMessage(g_hwndLogFrame, WM_CLOSE, 0, 0);
1023          break;
1024
1025       case ID_EDIT_COPY:
1026          SendMessage(g_hwndLogBox, WM_COPY, 0, 0);
1027          break;
1028
1029       case ID_VIEW_CLEARLOG:
1030          SendMessage(g_hwndLogBox, WM_SETTEXT, 0, (LPARAM) "");
1031          break;
1032
1033       case ID_VIEW_LOGMESSAGES:
1034          g_bLogMessages = !g_bLogMessages;
1035          /* SaveLogSettings(); */
1036          break;
1037
1038       case ID_VIEW_MESSAGEHIGHLIGHTING:
1039          g_bHighlightMessages = !g_bHighlightMessages;
1040          /* SaveLogSettings(); */
1041          break;
1042
1043       case ID_VIEW_LIMITBUFFERSIZE:
1044          g_bLimitBufferSize = !g_bLimitBufferSize;
1045          /* SaveLogSettings(); */
1046          break;
1047
1048       case ID_VIEW_ACTIVITYANIMATION:
1049          g_bShowActivityAnimation = !g_bShowActivityAnimation;
1050          /* SaveLogSettings(); */
1051          break;
1052
1053 #ifdef TOGGLE
1054       /* by haroon - change toggle to its opposite value */
1055       case ID_TOGGLE_IJB:
1056          g_bToggleIJB = !g_bToggleIJB;
1057          if (g_bToggleIJB)
1058          {
1059             log_error(LOG_LEVEL_INFO, "Now toggled ON.");
1060          }
1061          else
1062          {
1063             log_error(LOG_LEVEL_INFO, "Now toggled OFF.");
1064          }
1065          break;
1066 #endif
1067
1068       case ID_TOOLS_EDITJUNKBUSTER:
1069          EditFile(configfile);
1070          break;
1071
1072       case ID_TOOLS_EDITBLOCKERS:
1073          EditFile(g_blockfile);
1074          break;
1075
1076       case ID_TOOLS_EDITPERMISSIONS:
1077          EditFile(g_permissions_file);
1078          break;
1079
1080       case ID_TOOLS_EDITFORWARD:
1081          EditFile(g_forwardfile);
1082          break;
1083
1084 #ifdef ACL_FILES
1085       case ID_TOOLS_EDITACLS:
1086          EditFile(g_aclfile);
1087          break;
1088 #endif /* def ACL_FILES */
1089
1090 #ifdef USE_IMAGE_LIST
1091       case ID_TOOLS_EDITIMAGE:
1092          EditFile(g_imagefile);
1093          break;
1094 #endif /* def USE_IMAGE_LIST */
1095
1096 #ifdef PCRS
1097       case ID_TOOLS_EDITPERLRE:
1098          EditFile(g_re_filterfile);
1099          break;
1100 #endif
1101
1102 #ifdef TRUST_FILES
1103       case ID_TOOLS_EDITTRUST:
1104          EditFile(g_trustfile);
1105          break;
1106 #endif /* def TRUST_FILES */
1107
1108       case ID_NEW_BLOCKER:
1109          ShowRulesDialog(g_hwndLogFrame);
1110          break;
1111
1112       case ID_HELP_GPL:
1113          ShellExecute(g_hwndLogFrame, "open", "gpl.html", NULL, NULL, SW_SHOWNORMAL);
1114          break;
1115
1116       case ID_HELP_FAQ:
1117          ShellExecute(g_hwndLogFrame, "open", "ijbfaq.html", NULL, NULL, SW_SHOWNORMAL);
1118          break;
1119
1120       case ID_HELP_MANUAL:
1121          ShellExecute(g_hwndLogFrame, "open", "ijbman.html", NULL, NULL, SW_SHOWNORMAL);
1122          break;
1123
1124       case ID_HELP_STATUS:
1125          ShellExecute(g_hwndLogFrame, "open", "Junkbuster Status.URL", NULL, NULL, SW_SHOWNORMAL);
1126          break;
1127
1128       case ID_HELP_ABOUTJUNKBUSTER:
1129          MessageBox(g_hwndLogFrame, win32_blurb, "Junkbuster Information", MB_OK);
1130          break;
1131
1132       default:
1133          /* DO NOTHING */
1134          break;
1135    }
1136
1137 }
1138
1139
1140 /*********************************************************************
1141  *
1142  * Function    :  OnLogInitMenu
1143  *
1144  * Description :  Handler for WM_INITMENU messages.  Enable, disable,
1145  *                check, and/or uncheck menu options as apropos.
1146  *
1147  * Parameters  :
1148  *          1  :  hmenu = handle to menu to "make current"
1149  *
1150  * Returns     :  N/A
1151  *
1152  *********************************************************************/
1153 void OnLogInitMenu(HMENU hmenu)
1154 {
1155    /* Only enable editors if there is a file to edit */
1156    EnableMenuItem(hmenu, ID_TOOLS_EDITPERMISSIONS, MF_BYCOMMAND | (g_permissions_file ? MF_ENABLED : MF_GRAYED));
1157    EnableMenuItem(hmenu, ID_TOOLS_EDITBLOCKERS, MF_BYCOMMAND | (g_blockfile ? MF_ENABLED : MF_GRAYED));
1158    EnableMenuItem(hmenu, ID_TOOLS_EDITFORWARD, MF_BYCOMMAND | (g_forwardfile ? MF_ENABLED : MF_GRAYED));
1159 #ifdef ACL_FILES
1160    EnableMenuItem(hmenu, ID_TOOLS_EDITACLS, MF_BYCOMMAND | (g_aclfile ? MF_ENABLED : MF_GRAYED));
1161 #endif /* def ACL_FILES */
1162 #ifdef USE_IMAGE_LIST
1163    EnableMenuItem(hmenu, ID_TOOLS_EDITIMAGE, MF_BYCOMMAND | (g_imagefile ? MF_ENABLED : MF_GRAYED));
1164 #endif /* def USE_IMAGE_LIST */
1165 #ifdef PCRS
1166    EnableMenuItem(hmenu, ID_TOOLS_EDITPERLRE, MF_BYCOMMAND | (g_re_filterfile ? MF_ENABLED : MF_GRAYED));
1167 #endif
1168 #ifdef TRUST_FILES
1169    EnableMenuItem(hmenu, ID_TOOLS_EDITTRUST, MF_BYCOMMAND | (g_trustfile ? MF_ENABLED : MF_GRAYED));
1170 #endif /* def TRUST_FILES */
1171
1172    /* Check/uncheck options */
1173    CheckMenuItem(hmenu, ID_VIEW_LOGMESSAGES, MF_BYCOMMAND | (g_bLogMessages ? MF_CHECKED : MF_UNCHECKED));
1174    CheckMenuItem(hmenu, ID_VIEW_MESSAGEHIGHLIGHTING, MF_BYCOMMAND | (g_bHighlightMessages ? MF_CHECKED : MF_UNCHECKED));
1175    CheckMenuItem(hmenu, ID_VIEW_LIMITBUFFERSIZE, MF_BYCOMMAND | (g_bLimitBufferSize ? MF_CHECKED : MF_UNCHECKED));
1176    CheckMenuItem(hmenu, ID_VIEW_ACTIVITYANIMATION, MF_BYCOMMAND | (g_bShowActivityAnimation ? MF_CHECKED : MF_UNCHECKED));
1177 #ifdef TOGGLE
1178    /* by haroon - menu item for Enable toggle on/off */
1179    CheckMenuItem(hmenu, ID_TOGGLE_IJB, MF_BYCOMMAND | (g_bToggleIJB ? MF_CHECKED : MF_UNCHECKED));
1180 #endif
1181
1182 }
1183
1184
1185 /*********************************************************************
1186  *
1187  * Function    :  OnLogTimer
1188  *
1189  * Description :  Handler for WM_TIMER messages.
1190  *
1191  * Parameters  :
1192  *          1  :  nTimer = timer id (animation start/stop or clip buffer)
1193  *
1194  * Returns     :  N/A
1195  *
1196  *********************************************************************/
1197 void OnLogTimer(int nTimer)
1198 {
1199    switch (nTimer)
1200    {
1201       case TIMER_ANIM_ID:
1202          TraySetIcon(g_hwndTray, 1, g_hiconAnim[g_nAnimFrame++ % ANIM_FRAMES]);
1203          break;
1204
1205       case TIMER_ANIMSTOP_ID:
1206          g_nAnimFrame = 0;
1207          TraySetIcon(g_hwndTray, 1, g_hiconIdle);
1208          KillTimer(g_hwndLogFrame, TIMER_ANIM_ID);
1209          KillTimer(g_hwndLogFrame, TIMER_ANIMSTOP_ID);
1210          break;
1211
1212       case TIMER_CLIPBUFFER_ID:
1213       case TIMER_CLIPBUFFER_FORCE_ID:
1214          LogClipBuffer();
1215          g_bClipPending = FALSE;
1216          KillTimer(g_hwndLogFrame, TIMER_CLIPBUFFER_ID);
1217          KillTimer(g_hwndLogFrame, TIMER_CLIPBUFFER_FORCE_ID);
1218          break;
1219
1220       default:
1221          /* DO NOTHING */
1222          break;
1223    }
1224
1225 }
1226
1227
1228 /*********************************************************************
1229  *
1230  * Function    :  LogRichEditProc
1231  *
1232  * Description :  Window subclass routine handles some events for the rich edit control.
1233  *
1234  * Parameters  :
1235  *          1  :  hwnd = window handle of the rich edit control
1236  *          2  :  uMsg = message number
1237  *          3  :  wParam = first param for this message
1238  *          4  :  lParam = next param for this message
1239  *
1240  * Returns     :  Appropriate M$ window message handler codes.
1241  *
1242  *********************************************************************/
1243 LRESULT CALLBACK LogRichEditProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1244 {
1245    switch (uMsg)
1246    {
1247       case WM_RBUTTONUP:
1248       {
1249          POINT pt;
1250          pt.x = LOWORD(lParam);
1251          pt.y = HIWORD(lParam);
1252          ClientToScreen(hwnd, &pt);
1253          OnLogRButtonUp(wParam, pt.x, pt.y);
1254       }
1255       return 0;
1256    }
1257    return CallWindowProc(g_fnLogBox, hwnd, uMsg, wParam, lParam);
1258
1259 }
1260
1261
1262 /*********************************************************************
1263  *
1264  * Function    :  LogWindowProc
1265  *
1266  * Description :  Windows call back routine handles events on the log window.
1267  *
1268  * Parameters  :
1269  *          1  :  hwnd = handle of the logging window
1270  *          2  :  uMsg = message number
1271  *          3  :  wParam = first param for this message
1272  *          4  :  lParam = next param for this message
1273  *
1274  * Returns     :  Appropriate M$ window message handler codes.
1275  *
1276  *********************************************************************/
1277 LRESULT CALLBACK LogWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1278 {
1279    switch (uMsg)
1280    {
1281       case WM_CREATE:
1282          return 0;
1283
1284       case WM_CLOSE:
1285          /* This is the end - beautiful friend - the end */
1286          DestroyWindow(g_hwndLogBox);
1287          DestroyWindow(g_hwndLogFrame);
1288          return 0;
1289
1290       case WM_DESTROY:
1291          PostQuitMessage(0);
1292          return 0;
1293
1294       case WM_SHOWWINDOW:
1295       case WM_SIZE:
1296          /* Resize the logging window to fit the new frame */
1297          if (g_hwndLogBox)
1298          {
1299             RECT rc;
1300             GetClientRect(g_hwndLogFrame, &rc);
1301             SetWindowPos(g_hwndLogBox, NULL, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOZORDER);
1302          }
1303          return 0;
1304
1305       case WM_INITMENU:
1306          OnLogInitMenu((HMENU) wParam);
1307          return 0;
1308
1309       case WM_TIMER:
1310          OnLogTimer(wParam);
1311          return 0;
1312
1313       case WM_COMMAND:
1314          OnLogCommand(LOWORD(wParam));
1315          return 0;
1316
1317       case WM_SYSCOMMAND:
1318          switch (wParam)
1319          {
1320             case SC_CLOSE:
1321                if (g_bCloseHidesWindow)
1322                {
1323                   ShowLogWindow(FALSE);
1324                   return 0;
1325                }
1326                break;
1327             case SC_MINIMIZE:
1328                ShowLogWindow(FALSE);
1329                return 0;
1330          }
1331          break;
1332    }
1333
1334    return DefWindowProc(hwnd, uMsg, wParam, lParam);
1335
1336 }
1337
1338 #endif /* ndef _WIN_CONSOLE - entire file */
1339
1340 /*
1341   Local Variables:
1342   tab-width: 3
1343   end:
1344 */