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