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