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