Bump copyright year
[privoxy.git] / w32log.c
1 const char w32log_rcs[] = "$Id: w32log.c,v 1.48 2012/05/27 15:45:05 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 <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) GetWindowLongPtr(g_hwndLogBox, GWLP_WNDPROC);
728    SetWindowLongPtr(g_hwndLogBox, GWLP_WNDPROC, (LONG_PTR) 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       case ID_TOGGLE_ENABLED:
948          global_toggle_state = !global_toggle_state;
949          log_error(LOG_LEVEL_INFO,
950             "Now toggled %s", global_toggle_state ? "ON" : "OFF");
951          /*
952           * Leverage TIMER_ANIMSTOP_ID to set the idle icon through the
953           * "application queue". According to MSDN, 10 milliseconds are
954           * the lowest value possible and seem to be close enough to
955           * "instantly".
956           */
957          SetTimer(g_hwndLogFrame, TIMER_ANIMSTOP_ID, 10, NULL);
958          break;
959 #endif /* def FEATURE_TOGGLE */
960
961       case ID_TOOLS_EDITCONFIG:
962          EditFile(configfile);
963          break;
964
965       case ID_TOOLS_EDITDEFAULTACTIONS:
966          EditFile(g_default_actions_file);
967          break;
968
969       case ID_TOOLS_EDITUSERACTIONS:
970          EditFile(g_user_actions_file);
971          break;
972
973       case ID_TOOLS_EDITDEFAULTFILTERS:
974          EditFile(g_default_filterfile);
975          break;
976
977       case ID_TOOLS_EDITUSERFILTERS:
978          EditFile(g_user_filterfile);
979          break;
980
981 #ifdef FEATURE_TRUST
982       case ID_TOOLS_EDITTRUST:
983          EditFile(g_trustfile);
984          break;
985 #endif /* def FEATURE_TRUST */
986
987       case ID_HELP_GPL:
988          ShellExecute(g_hwndLogFrame, "open", "LICENSE.txt", NULL, NULL, SW_SHOWNORMAL);
989          break;
990
991       case ID_HELP_FAQ:
992          ShellExecute(g_hwndLogFrame, "open", "doc\\faq\\index.html", NULL, NULL, SW_SHOWNORMAL);
993          break;
994
995       case ID_HELP_MANUAL:
996          ShellExecute(g_hwndLogFrame, "open", "doc\\user-manual\\index.html", NULL, NULL, SW_SHOWNORMAL);
997          break;
998
999       case ID_HELP_STATUS:
1000          ShellExecute(g_hwndLogFrame, "open", CGI_PREFIX "show-status", NULL, NULL, SW_SHOWNORMAL);
1001          break;
1002
1003       case ID_HELP_ABOUT:
1004          MessageBox(g_hwndLogFrame, win32_blurb, "About Privoxy", MB_OK);
1005          break;
1006
1007       default:
1008          /* DO NOTHING */
1009          break;
1010    }
1011
1012 }
1013
1014
1015 /*********************************************************************
1016  *
1017  * Function    :  OnLogInitMenu
1018  *
1019  * Description :  Handler for WM_INITMENU messages.  Enable, disable,
1020  *                check, and/or uncheck menu options as apropos.
1021  *
1022  * Parameters  :
1023  *          1  :  hmenu = handle to menu to "make current"
1024  *
1025  * Returns     :  N/A
1026  *
1027  *********************************************************************/
1028 void OnLogInitMenu(HMENU hmenu)
1029 {
1030    /* Only enable editors if there is a file to edit */
1031    EnableMenuItem(hmenu, ID_TOOLS_EDITDEFAULTACTIONS, MF_BYCOMMAND | (g_default_actions_file ? MF_ENABLED : MF_GRAYED));
1032    EnableMenuItem(hmenu, ID_TOOLS_EDITUSERACTIONS, MF_BYCOMMAND | (g_user_actions_file ? MF_ENABLED : MF_GRAYED));
1033    EnableMenuItem(hmenu, ID_TOOLS_EDITDEFAULTFILTERS, MF_BYCOMMAND | (g_default_filterfile ? MF_ENABLED : MF_GRAYED));
1034    EnableMenuItem(hmenu, ID_TOOLS_EDITUSERFILTERS, MF_BYCOMMAND | (g_user_filterfile ? MF_ENABLED : MF_GRAYED));
1035 #ifdef FEATURE_TRUST
1036    EnableMenuItem(hmenu, ID_TOOLS_EDITTRUST, MF_BYCOMMAND | (g_trustfile ? MF_ENABLED : MF_GRAYED));
1037 #endif /* def FEATURE_TRUST */
1038
1039    /* Check/uncheck options */
1040    CheckMenuItem(hmenu, ID_VIEW_LOGMESSAGES, MF_BYCOMMAND | (g_bLogMessages ? MF_CHECKED : MF_UNCHECKED));
1041    CheckMenuItem(hmenu, ID_VIEW_MESSAGEHIGHLIGHTING, MF_BYCOMMAND | (g_bHighlightMessages ? MF_CHECKED : MF_UNCHECKED));
1042    CheckMenuItem(hmenu, ID_VIEW_LIMITBUFFERSIZE, MF_BYCOMMAND | (g_bLimitBufferSize ? MF_CHECKED : MF_UNCHECKED));
1043    CheckMenuItem(hmenu, ID_VIEW_ACTIVITYANIMATION, MF_BYCOMMAND | (g_bShowActivityAnimation ? MF_CHECKED : MF_UNCHECKED));
1044 #ifdef FEATURE_TOGGLE
1045    /* by haroon - menu item for Enable toggle on/off */
1046    CheckMenuItem(hmenu, ID_TOGGLE_ENABLED, MF_BYCOMMAND | (global_toggle_state ? MF_CHECKED : MF_UNCHECKED));
1047 #endif /* def FEATURE_TOGGLE */
1048    CheckMenuItem(hmenu, ID_TOGGLE_SHOWWINDOW, MF_BYCOMMAND | (g_bShowLogWindow ? MF_CHECKED : MF_UNCHECKED));
1049
1050 }
1051
1052
1053 /*********************************************************************
1054  *
1055  * Function    :  OnLogTimer
1056  *
1057  * Description :  Handler for WM_TIMER messages.
1058  *
1059  * Parameters  :
1060  *          1  :  nTimer = timer id (animation start/stop or clip buffer)
1061  *
1062  * Returns     :  N/A
1063  *
1064  *********************************************************************/
1065 void OnLogTimer(int nTimer)
1066 {
1067    switch (nTimer)
1068    {
1069       case TIMER_ANIM_ID:
1070          TraySetIcon(g_hwndTray, 1, g_hiconAnim[g_nAnimFrame++ % ANIM_FRAMES]);
1071          break;
1072
1073       case TIMER_ANIMSTOP_ID:
1074          g_nAnimFrame = 0;
1075          SetIdleIcon();
1076          KillTimer(g_hwndLogFrame, TIMER_ANIM_ID);
1077          KillTimer(g_hwndLogFrame, TIMER_ANIMSTOP_ID);
1078          break;
1079
1080       case TIMER_CLIPBUFFER_ID:
1081       case TIMER_CLIPBUFFER_FORCE_ID:
1082          LogClipBuffer();
1083          g_bClipPending = FALSE;
1084          KillTimer(g_hwndLogFrame, TIMER_CLIPBUFFER_ID);
1085          KillTimer(g_hwndLogFrame, TIMER_CLIPBUFFER_FORCE_ID);
1086          break;
1087
1088       default:
1089          /* DO NOTHING */
1090          break;
1091    }
1092
1093 }
1094
1095
1096 /*********************************************************************
1097  *
1098  * Function    :  SetIdleIcon
1099  *
1100  * Description :  Sets the tray icon to either idle or off
1101  *
1102  * Parameters  :  none
1103  *
1104  * Returns     :  N/A
1105  *
1106  *********************************************************************/
1107 void SetIdleIcon()
1108 {
1109 #ifdef FEATURE_TOGGLE
1110          if (!global_toggle_state)
1111          {
1112             TraySetIcon(g_hwndTray, 1, g_hiconOff);
1113          }
1114          else
1115 #endif /* def FEATURE_TOGGLE */
1116          TraySetIcon(g_hwndTray, 1, g_hiconIdle);
1117 }
1118
1119
1120 /*********************************************************************
1121  *
1122  * Function    :  LogRichEditProc
1123  *
1124  * Description :  Window subclass routine handles some events for the rich edit control.
1125  *
1126  * Parameters  :
1127  *          1  :  hwnd = window handle of the rich edit control
1128  *          2  :  uMsg = message number
1129  *          3  :  wParam = first param for this message
1130  *          4  :  lParam = next param for this message
1131  *
1132  * Returns     :  Appropriate M$ window message handler codes.
1133  *
1134  *********************************************************************/
1135 LRESULT CALLBACK LogRichEditProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1136 {
1137    switch (uMsg)
1138    {
1139       case WM_RBUTTONUP:
1140       {
1141          POINT pt;
1142          pt.x = LOWORD(lParam);
1143          pt.y = HIWORD(lParam);
1144          ClientToScreen(hwnd, &pt);
1145          OnLogRButtonUp(wParam, pt.x, pt.y);
1146          return 0;
1147       }
1148       case WM_CHAR:
1149       {
1150          if ((GetKeyState(VK_CONTROL) != 0) && (wParam == 4)) /* ctrl+d */
1151          {
1152              OnLogCommand(ID_VIEW_CLEARLOG);
1153              return 0;
1154          }
1155       }
1156    }
1157    return CallWindowProc(g_fnLogBox, hwnd, uMsg, wParam, lParam);
1158
1159 }
1160
1161
1162 /*********************************************************************
1163  *
1164  * Function    :  LogWindowProc
1165  *
1166  * Description :  Windows call back routine handles events on the log window.
1167  *
1168  * Parameters  :
1169  *          1  :  hwnd = handle of the logging window
1170  *          2  :  uMsg = message number
1171  *          3  :  wParam = first param for this message
1172  *          4  :  lParam = next param for this message
1173  *
1174  * Returns     :  Appropriate M$ window message handler codes.
1175  *
1176  *********************************************************************/
1177 LRESULT CALLBACK LogWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1178 {
1179    switch (uMsg)
1180    {
1181       case WM_CREATE:
1182          return 0;
1183
1184       case WM_CLOSE:
1185          /* This is the end - my only friend - the end */
1186          DestroyWindow(g_hwndLogBox);
1187          DestroyWindow(g_hwndLogFrame);
1188          return 0;
1189
1190       case WM_DESTROY:
1191          PostQuitMessage(0);
1192          return 0;
1193
1194       case WM_SHOWWINDOW:
1195          g_bShowLogWindow = wParam;
1196       case WM_SIZE:
1197          /* Resize the logging window to fit the new frame */
1198          if (g_hwndLogBox)
1199          {
1200             RECT rc;
1201             GetClientRect(g_hwndLogFrame, &rc);
1202             SetWindowPos(g_hwndLogBox, NULL, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOZORDER);
1203          }
1204          return 0;
1205
1206       case WM_INITMENU:
1207          OnLogInitMenu((HMENU) wParam);
1208          return 0;
1209
1210       case WM_TIMER:
1211          OnLogTimer(wParam);
1212          return 0;
1213
1214       case WM_COMMAND:
1215          OnLogCommand(LOWORD(wParam));
1216          return 0;
1217
1218       case WM_SYSCOMMAND:
1219          switch (wParam)
1220          {
1221             case SC_CLOSE:
1222                if (g_bCloseHidesWindow)
1223                {
1224                   ShowLogWindow(FALSE);
1225                   return 0;
1226                }
1227                break;
1228             case SC_MINIMIZE:
1229                ShowLogWindow(FALSE);
1230                return 0;
1231          }
1232          break;
1233
1234       case WM_CHAR:
1235          if ((GetKeyState(VK_CONTROL) != 0) && (wParam == 4)) /* ctrl+d */
1236          {
1237              OnLogCommand(ID_VIEW_CLEARLOG);
1238              return 0;
1239          }
1240          break;
1241    }
1242
1243    return DefWindowProc(hwnd, uMsg, wParam, lParam);
1244
1245 }
1246
1247 #endif /* ndef _WIN_CONSOLE - entire file */
1248
1249 /*
1250   Local Variables:
1251   tab-width: 3
1252   end:
1253 */