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