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