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