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