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