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