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