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