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