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