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