9f855a71a2fd5cda20d34f758fc171e54983a3e9
[privoxy.git] / cgisimple.c
1 const char cgisimple_rcs[] = "$Id: cgisimple.c,v 1.31 2002/04/26 12:54:36 oes Exp $";
2 /*********************************************************************
3  *
4  * File        :  $Source: /cvsroot/ijbswa/current/cgisimple.c,v $
5  *
6  * Purpose     :  Simple CGIs to get information about Privoxy's
7  *                status.
8  *                
9  *                Functions declared include:
10  * 
11  *
12  * Copyright   :  Written by and Copyright (C) 2001 the SourceForge
13  *                Privoxy team. http://www.privoxy.org/
14  *
15  *                Based on the Internet Junkbuster originally written
16  *                by and Copyright (C) 1997 Anonymous Coders and 
17  *                Junkbusters Corporation.  http://www.junkbusters.com
18  *
19  *                This program is free software; you can redistribute it 
20  *                and/or modify it under the terms of the GNU General
21  *                Public License as published by the Free Software
22  *                Foundation; either version 2 of the License, or (at
23  *                your option) any later version.
24  *
25  *                This program is distributed in the hope that it will
26  *                be useful, but WITHOUT ANY WARRANTY; without even the
27  *                implied warranty of MERCHANTABILITY or FITNESS FOR A
28  *                PARTICULAR PURPOSE.  See the GNU General Public
29  *                License for more details.
30  *
31  *                The GNU General Public License should be included with
32  *                this file.  If not, you can view it at
33  *                http://www.gnu.org/copyleft/gpl.html
34  *                or write to the Free Software Foundation, Inc., 59
35  *                Temple Place - Suite 330, Boston, MA  02111-1307, USA.
36  *
37  * Revisions   :
38  *    $Log: cgisimple.c,v $
39  *    Revision 1.31  2002/04/26 12:54:36  oes
40  *     - Kill obsolete REDIRECT_URL code
41  *     - Error handling fixes
42  *     - Style sheet related HTML snipplet changes
43  *     - cgi_show_url_info:
44  *       - Matches now in table, actions on single lines,
45  *         linked to help
46  *       - standard.action suppressed
47  *       - Buttons to View and Edit AFs
48  *
49  *    Revision 1.30  2002/04/24 02:18:08  oes
50  *     - show-status is now the starting point for editing
51  *       the actions files, generate list of all AFs with buttons
52  *       for viewing and editing, new look for file list (Jon:
53  *       buttons now aligned ;-P ), view mode now supports multiple
54  *       AFs, name changes, no view links for unspecified files,
55  *       no edit link for standard.action.
56  *
57  *     - Jon's multiple AF patch: cgi_show_url_info now uses all
58  *       AFs and marks the output accordingly
59  *
60  *    Revision 1.29  2002/04/10 13:38:35  oes
61  *    load_template signature changed
62  *
63  *    Revision 1.28  2002/04/07 15:42:12  jongfoster
64  *    Fixing send-banner?type=auto when the image-blocker is
65  *    a redirect to send-banner
66  *
67  *    Revision 1.27  2002/04/05 15:50:48  oes
68  *    added send-stylesheet CGI
69  *
70  *    Revision 1.26  2002/04/04 00:36:36  gliptak
71  *    always use pcre for matching
72  *
73  *    Revision 1.25  2002/04/03 22:28:03  gliptak
74  *    Removed references to gnu_regex
75  *
76  *    Revision 1.24  2002/04/02 16:12:47  oes
77  *    Fix: moving misplaced lines into #ifdef FEATURE_FORCE
78  *
79  *    Revision 1.23  2002/03/26 22:29:54  swa
80  *    we have a new homepage!
81  *
82  *    Revision 1.22  2002/03/24 16:18:15  jongfoster
83  *    Removing old logo
84  *
85  *    Revision 1.21  2002/03/24 15:23:33  jongfoster
86  *    Name changes
87  *
88  *    Revision 1.20  2002/03/24 13:25:43  swa
89  *    name change related issues
90  *
91  *    Revision 1.19  2002/03/16 23:54:06  jongfoster
92  *    Adding graceful termination feature, to help look for memory leaks.
93  *    If you enable this (which, by design, has to be done by hand
94  *    editing config.h) and then go to http://i.j.b/die, then the program
95  *    will exit cleanly after the *next* request.  It should free all the
96  *    memory that was used.
97  *
98  *    Revision 1.18  2002/03/12 01:44:49  oes
99  *    Changed default for "blocked" image from jb logo to checkboard pattern
100  *
101  *    Revision 1.17  2002/03/08 16:43:18  oes
102  *    Added choice beween GIF and PNG built-in images
103  *
104  *    Revision 1.16  2002/03/07 03:48:38  oes
105  *     - Changed built-in images from GIF to PNG
106  *       (with regard to Unisys patent issue)
107  *     - Added a 4x4 pattern PNG which is less intrusive
108  *       than the logo but also clearly marks the deleted banners
109  *
110  *    Revision 1.15  2002/03/06 22:54:35  jongfoster
111  *    Automated function-comment nitpicking.
112  *
113  *    Revision 1.14  2002/03/02 04:14:50  david__schmidt
114  *    Clean up a little CRLF unpleasantness that suddenly appeared
115  *
116  *    Revision 1.13  2002/02/21 00:10:37  jongfoster
117  *    Adding send-banner?type=auto option
118  *
119  *    Revision 1.12  2002/01/23 01:03:32  jongfoster
120  *    Fixing gcc [CygWin] compiler warnings
121  *
122  *    Revision 1.11  2002/01/23 00:01:04  jongfoster
123  *    Adding cgi_transparent_gif() for http://i.j.b/t
124  *    Adding missing html_encode() to many CGI functions.
125  *    Adding urlmatch.[ch] to http://i.j.b/show-version
126  *
127  *    Revision 1.10  2002/01/17 21:10:37  jongfoster
128  *    Changes to cgi_show_url_info to use new matching code from urlmatch.c.
129  *    Also fixing a problem in the same function with improperly quoted URLs
130  *    in output HTML, and adding code to handle https:// URLs correctly.
131  *
132  *    Revision 1.9  2001/11/30 23:09:15  jongfoster
133  *    Now reports on FEATURE_CGI_EDIT_ACTIONS
134  *    Removing FEATURE_DENY_GZIP from template
135  *
136  *    Revision 1.8  2001/11/13 00:14:07  jongfoster
137  *    Fixing stupid bug now I've figured out what || means.
138  *    (It always returns 0 or 1, not one of it's paramaters.)
139  *
140  *    Revision 1.7  2001/10/23 21:48:19  jongfoster
141  *    Cleaning up error handling in CGI functions - they now send back
142  *    a HTML error page and should never cause a FATAL error.  (Fixes one
143  *    potential source of "denial of service" attacks).
144  *
145  *    CGI actions file editor that works and is actually useful.
146  *
147  *    Ability to toggle JunkBuster remotely using a CGI call.
148  *
149  *    You can turn off both the above features in the main configuration
150  *    file, e.g. if you are running a multi-user proxy.
151  *
152  *    Revision 1.6  2001/10/14 22:00:32  jongfoster
153  *    Adding support for a 404 error when an invalid CGI page is requested.
154  *
155  *    Revision 1.5  2001/10/07 15:30:41  oes
156  *    Removed FEATURE_DENY_GZIP
157  *
158  *    Revision 1.4  2001/10/02 15:31:12  oes
159  *    Introduced show-request cgi
160  *
161  *    Revision 1.3  2001/09/22 16:34:44  jongfoster
162  *    Removing unneeded #includes
163  *
164  *    Revision 1.2  2001/09/19 18:01:11  oes
165  *    Fixed comments; cosmetics
166  *
167  *    Revision 1.1  2001/09/16 17:08:54  jongfoster
168  *    Moving simple CGI functions from cgi.c to new file cgisimple.c
169  *
170  *
171  **********************************************************************/
172 \f
173
174 #include "config.h"
175
176 #include <stdio.h>
177 #include <sys/types.h>
178 #include <stdlib.h>
179 #include <ctype.h>
180 #include <string.h>
181 #include <assert.h>
182
183 #ifdef _WIN32
184 #define snprintf _snprintf
185 #endif /* def _WIN32 */
186
187 #include "project.h"
188 #include "cgi.h"
189 #include "cgisimple.h"
190 #include "list.h"
191 #include "encode.h"
192 #include "jcc.h"
193 #include "filters.h"
194 #include "actions.h"
195 #include "miscutil.h"
196 #include "loadcfg.h"
197 #include "parsers.h"
198 #include "urlmatch.h"
199 #include "errlog.h"
200
201 const char cgisimple_h_rcs[] = CGISIMPLE_H_VERSION;
202
203
204 static char *show_rcs(void);
205 static jb_err show_defines(struct map *exports);
206
207
208 /*********************************************************************
209  *
210  * Function    :  cgi_default
211  *
212  * Description :  CGI function that is called if no action was given.
213  *                Lists menu of available unhidden CGIs.
214  *               
215  * Parameters  :
216  *          1  :  csp = Current client state (buffers, headers, etc...)
217  *          2  :  rsp = http_response data structure for output
218  *          3  :  parameters = map of cgi parameters
219  *
220  * CGI Parameters : none
221  *
222  * Returns     :  JB_ERR_OK on success
223  *                JB_ERR_MEMORY on out-of-memory
224  *                (Problems other than out-of-memory should be
225  *                handled by this routine - it should set the
226  *                rsp appropriately and return "success")
227  *
228  *********************************************************************/
229 jb_err cgi_default(struct client_state *csp,
230                    struct http_response *rsp,
231                    const struct map *parameters)
232 {
233    char *tmp;
234    struct map *exports;
235
236    assert(csp);
237    assert(rsp);
238    assert(parameters);
239
240    if (NULL == (exports = default_exports(csp, "")))
241    {
242       return JB_ERR_MEMORY;
243    }
244
245    /* If there were other parameters, export a dump as "cgi-parameters" */
246    if (parameters->first)
247    {
248       tmp = strdup("<p>What made you think this cgi takes parameters?\n"
249                    "Anyway, here they are, in case you're interested:</p>\n");
250       string_join(&tmp, dump_map(parameters));
251       if (tmp == NULL)
252       {
253          free_map(exports);
254          return JB_ERR_MEMORY;
255       }
256       if (map(exports, "cgi-parameters", 1, tmp, 0))
257       {
258          return JB_ERR_MEMORY;
259       }
260    }
261    else
262    {
263       if (map(exports, "cgi-parameters", 1, "", 1))
264       {
265          return JB_ERR_MEMORY;
266       }
267    }
268
269    return template_fill_for_cgi(csp, "default", exports, rsp);
270 }
271
272
273
274
275 /*********************************************************************
276  *
277  * Function    :  cgi_error_404
278  *
279  * Description :  CGI function that is called if an unknown action was
280  *                given.
281  *               
282  * Parameters  :
283  *          1  :  csp = Current client state (buffers, headers, etc...)
284  *          2  :  rsp = http_response data structure for output
285  *          3  :  parameters = map of cgi parameters
286  *
287  * CGI Parameters : none
288  *
289  * Returns     :  JB_ERR_OK on success
290  *                JB_ERR_MEMORY on out-of-memory error.  
291  *
292  *********************************************************************/
293 jb_err cgi_error_404(struct client_state *csp,
294                      struct http_response *rsp,
295                      const struct map *parameters)
296 {
297    struct map *exports;
298
299    assert(csp);
300    assert(rsp);
301    assert(parameters);
302
303    if (NULL == (exports = default_exports(csp, NULL)))
304    {
305       return JB_ERR_MEMORY;
306    }
307
308    rsp->status = strdup("404 Privoxy configuration page not found");
309    if (rsp->status == NULL)
310    {
311       free_map(exports);
312       return JB_ERR_MEMORY;
313    }
314
315    return template_fill_for_cgi(csp, "cgi-error-404", exports, rsp);
316 }
317
318
319 #ifdef FEATURE_GRACEFUL_TERMINATION
320 /*********************************************************************
321  *
322  * Function    :  cgi_die
323  *
324  * Description :  CGI function to shut down Privoxy.
325  *                NOTE: Turning this on in a production build
326  *                would be a BAD idea.  An EXTREMELY BAD idea.
327  *                In short, don't do it.
328  *               
329  * Parameters  :
330  *          1  :  csp = Current client state (buffers, headers, etc...)
331  *          2  :  rsp = http_response data structure for output
332  *          3  :  parameters = map of cgi parameters
333  *
334  * CGI Parameters : none
335  *
336  * Returns     :  JB_ERR_OK on success
337  *                JB_ERR_MEMORY on out-of-memory error.  
338  *
339  *********************************************************************/
340 jb_err cgi_die (struct client_state *csp,
341                 struct http_response *rsp,
342                 const struct map *parameters)
343 {
344    assert(csp);
345    assert(rsp);
346    assert(parameters);
347
348    /* quit */
349    g_terminate = 1;
350
351    /*
352     * I don't really care what gets sent back to the browser.
353     * Take the easy option - "out of memory" page.
354     */
355
356    return JB_ERR_MEMORY;
357 }
358 #endif /* def FEATURE_GRACEFUL_TERMINATION */
359
360
361 /*********************************************************************
362  *
363  * Function    :  cgi_show_request
364  *
365  * Description :  Show the client's request and what sed() would have
366  *                made of it.
367  *               
368  * Parameters  :
369  *          1  :  csp = Current client state (buffers, headers, etc...)
370  *          2  :  rsp = http_response data structure for output
371  *          3  :  parameters = map of cgi parameters
372  *
373  * CGI Parameters : none
374  *
375  * Returns     :  JB_ERR_OK on success
376  *                JB_ERR_MEMORY on out-of-memory error.  
377  *
378  *********************************************************************/
379 jb_err cgi_show_request(struct client_state *csp,
380                         struct http_response *rsp,
381                         const struct map *parameters)
382 {
383    char *p;
384    struct map *exports;
385
386    assert(csp);
387    assert(rsp);
388    assert(parameters);
389
390    if (NULL == (exports = default_exports(csp, "show-request")))
391    {
392       return JB_ERR_MEMORY;
393    }
394    
395    /*
396     * Repair the damage done to the IOB by get_header()
397     */
398    for (p = csp->iob->buf; p < csp->iob->eod; p++)
399    {
400       if (*p == '\0') *p = '\n';
401    }
402
403    /*
404     * Export the original client's request and the one we would
405     * be sending to the server if this wasn't a CGI call
406     */
407
408    if (map(exports, "client-request", 1, html_encode(csp->iob->buf), 0))
409    {
410       free_map(exports);
411       return JB_ERR_MEMORY;
412    }
413
414    if (map(exports, "processed-request", 1, html_encode_and_free_original(
415       sed(client_patterns, add_client_headers, csp)), 0))
416    {
417       free_map(exports);
418       return JB_ERR_MEMORY;
419    }
420
421    return template_fill_for_cgi(csp, "show-request", exports, rsp);
422 }
423
424
425 /*********************************************************************
426  *
427  * Function    :  cgi_send_banner
428  *
429  * Description :  CGI function that returns a banner. 
430  *
431  * Parameters  :
432  *          1  :  csp = Current client state (buffers, headers, etc...)
433  *          2  :  rsp = http_response data structure for output
434  *          3  :  parameters = map of cgi parameters
435  *
436  * CGI Parameters :
437  *           type : Selects the type of banner between "trans", "logo",
438  *                  and "auto". Defaults to "logo" if absent or invalid.
439  *                  "auto" means to select as if we were image-blocking.
440  *                  (Only the first character really counts).
441  *
442  * Returns     :  JB_ERR_OK on success
443  *                JB_ERR_MEMORY on out-of-memory error.  
444  *
445  *********************************************************************/
446 jb_err cgi_send_banner(struct client_state *csp,
447                        struct http_response *rsp,
448                        const struct map *parameters)
449 {
450    char imagetype = lookup(parameters, "type")[0];
451
452    if (imagetype == 'a') /* auto */
453    {
454       /* Default to pattern */
455       imagetype = 'p';
456 #ifdef FEATURE_IMAGE_BLOCKING
457       if ((csp->action->flags & ACTION_IMAGE_BLOCKER) != 0)
458       {
459          static const char prefix1[] = CGI_PREFIX "send-banner?type=";
460          static const char prefix2[] = "http://" CGI_SITE_1_HOST "/send-banner?type=";
461
462          /* determine HOW images should be blocked */
463          const char * p = csp->action->string[ACTION_STRING_IMAGE_BLOCKER];
464
465          /* and handle accordingly: */
466          if (p == NULL)
467          {
468             /* Use default - nothing to do here. */
469          }
470          else if (0 == strcmpic(p, "blank"))
471          {
472             imagetype = 'b';
473          }
474          else if (0 == strcmpic(p, "pattern"))
475          {
476             imagetype = 'p';
477          }
478          else if (0 == strncmpic(p, prefix1, sizeof(prefix1) - 1))
479          {
480             imagetype = p[sizeof(prefix1) - 1];
481          }
482          else if (0 == strncmpic(p, prefix2, sizeof(prefix2) - 1))
483          {
484             imagetype = p[sizeof(prefix2) - 1];
485          }
486       }
487 #endif /* def FEATURE_IMAGE_BLOCKING */
488    }
489       
490    if ((imagetype == 'b') || (imagetype == 't')) /* blank / transparent */
491    {
492       rsp->body = bindup(image_blank_data, image_blank_length);
493       rsp->content_length = image_blank_length;
494
495    }
496    else /* pattern */
497    {
498       rsp->body = bindup(image_pattern_data, image_pattern_length);
499       rsp->content_length = image_pattern_length;
500    }   
501
502    if (rsp->body == NULL)
503    {
504       return JB_ERR_MEMORY;
505    }
506
507    if (enlist(rsp->headers, "Content-Type: " BUILTIN_IMAGE_MIMETYPE))
508    {
509       return JB_ERR_MEMORY;
510    }
511
512    rsp->is_static = 1;
513
514    return JB_ERR_OK;
515
516 }
517
518
519 /*********************************************************************
520  *
521  * Function    :  cgi_transparent_image
522  *
523  * Description :  CGI function that sends a 1x1 transparent image.
524  *
525  * Parameters  :
526  *          1  :  csp = Current client state (buffers, headers, etc...)
527  *          2  :  rsp = http_response data structure for output
528  *          3  :  parameters = map of cgi parameters
529  *
530  * CGI Parameters : None
531  *
532  * Returns     :  JB_ERR_OK on success
533  *                JB_ERR_MEMORY on out-of-memory error.  
534  *
535  *********************************************************************/
536 jb_err cgi_transparent_image(struct client_state *csp,
537                              struct http_response *rsp,
538                              const struct map *parameters)
539 {
540    rsp->body = bindup(image_blank_data, image_blank_length);
541    rsp->content_length = image_blank_length;
542
543    if (rsp->body == NULL)
544    {
545       return JB_ERR_MEMORY;
546    }
547
548    if (enlist(rsp->headers, "Content-Type: " BUILTIN_IMAGE_MIMETYPE))
549    {
550       return JB_ERR_MEMORY;
551    }
552
553    rsp->is_static = 1;
554
555    return JB_ERR_OK;
556
557 }
558
559
560 /*********************************************************************
561  *
562  * Function    :  cgi_send_stylesheet
563  *
564  * Description :  CGI function that sends a css stylesheet found
565  *                in the cgi-style.css template
566  *
567  * Parameters  :
568  *          1  :  csp = Current client state (buffers, headers, etc...)
569  *          2  :  rsp = http_response data structure for output
570  *          3  :  parameters = map of cgi parameters
571  *
572  * CGI Parameters : None
573  *
574  * Returns     :  JB_ERR_OK on success
575  *                JB_ERR_MEMORY on out-of-memory error.  
576  *
577  *********************************************************************/
578 jb_err cgi_send_stylesheet(struct client_state *csp,
579                            struct http_response *rsp,
580                            const struct map *parameters)
581 {
582    jb_err err;
583    
584    assert(csp);
585    assert(rsp);
586
587    err = template_load(csp, &rsp->body, "cgi-style.css", 0);
588
589    if (err == JB_ERR_FILE)
590    {
591       /*
592        * No way to tell user; send empty stylesheet
593        */
594       log_error(LOG_LEVEL_ERROR, "Could not find cgi-style.css template");
595    }
596    else if (err)
597    {
598       return err; /* JB_ERR_MEMORY */
599    }
600
601    if (enlist(rsp->headers, "Content-Type: text/css"))
602    {
603       return JB_ERR_MEMORY;
604    }
605
606    return JB_ERR_OK;
607
608 }
609
610
611 /*********************************************************************
612  *
613  * Function    :  cgi_show_version
614  *
615  * Description :  CGI function that returns a a web page describing the
616  *                file versions of Privoxy.
617  *
618  * Parameters  :
619  *          1  :  csp = Current client state (buffers, headers, etc...)
620  *          2  :  rsp = http_response data structure for output
621  *          3  :  parameters = map of cgi parameters
622  *
623  * CGI Parameters : none
624  *
625  * Returns     :  JB_ERR_OK on success
626  *                JB_ERR_MEMORY on out-of-memory error.  
627  *
628  *********************************************************************/
629 jb_err cgi_show_version(struct client_state *csp,
630                         struct http_response *rsp,
631                         const struct map *parameters)
632 {
633    struct map *exports;
634
635    assert(csp);
636    assert(rsp);
637    assert(parameters);
638
639    if (NULL == (exports = default_exports(csp, "show-version")))
640    {
641       return JB_ERR_MEMORY;
642    }
643
644    if (map(exports, "sourceversions", 1, show_rcs(), 0))
645    {
646       free_map(exports);
647       return JB_ERR_MEMORY;
648    }
649
650    return template_fill_for_cgi(csp, "show-version", exports, rsp);
651 }
652
653  
654 /*********************************************************************
655  *
656  * Function    :  cgi_show_status
657  *
658  * Description :  CGI function that returns a a web page describing the
659  *                current status of Privoxy.
660  *
661  * Parameters  :
662  *          1  :  csp = Current client state (buffers, headers, etc...)
663  *          2  :  rsp = http_response data structure for output
664  *          3  :  parameters = map of cgi parameters
665  *
666  * CGI Parameters :
667  *        file :  Which file to show.  Only first letter is checked,
668  *                valid values are:
669  *                - "p"ermissions (actions) file
670  *                - "r"egex
671  *                - "t"rust
672  *                Default is to show menu and other information.
673  *
674  * Returns     :  JB_ERR_OK on success
675  *                JB_ERR_MEMORY on out-of-memory error.  
676  *
677  *********************************************************************/
678 jb_err cgi_show_status(struct client_state *csp,
679                        struct http_response *rsp,
680                        const struct map *parameters)
681 {
682    char *s = NULL;
683    unsigned i;
684    int j;
685
686    FILE * fp;
687    char buf[BUFFER_SIZE];
688    const char * filename = NULL;
689    char * file_description = NULL;
690 #ifdef FEATURE_STATISTICS
691    float perc_rej;   /* Percentage of http requests rejected */
692    int local_urls_read;
693    int local_urls_rejected;
694 #endif /* ndef FEATURE_STATISTICS */
695    struct file_list * fl;
696    struct url_actions * b;
697    jb_err err = JB_ERR_OK;
698
699    struct map *exports;
700
701    assert(csp);
702    assert(rsp);
703    assert(parameters);
704
705    if (NULL == (exports = default_exports(csp, "show-status")))
706    {
707       return JB_ERR_MEMORY;
708    }
709
710    switch (*(lookup(parameters, "file")))
711    {
712    case 'a':
713       if (!get_number_param(csp, parameters, "index", &i) && i < MAX_ACTION_FILES && csp->actions_list[i])
714       {
715          filename = csp->actions_list[i]->filename;
716          file_description = "Actions File";
717       }
718       break;
719
720    case 'f':
721       if (csp->rlist)
722       {
723          filename = csp->rlist->filename;
724          file_description = "Filter File";
725       }
726       break;
727
728 #ifdef FEATURE_TRUST
729    case 't':
730       if (csp->tlist)
731       {
732          filename = csp->tlist->filename;
733          file_description = "Trust File";
734       }
735       break;
736 #endif /* def FEATURE_TRUST */
737    }
738
739    if (NULL != filename)
740    {
741       if ( map(exports, "file-description", 1, file_description, 1)
742         || map(exports, "filepath", 1, html_encode(filename), 0) )
743       {
744          free_map(exports);
745          return JB_ERR_MEMORY;
746       }
747
748       if ((fp = fopen(filename, "r")) == NULL)
749       {
750          if (map(exports, "content", 1, "<h1>ERROR OPENING FILE!</h1>", 1))
751          {
752             free_map(exports);
753             return JB_ERR_MEMORY;
754          }
755       }
756       else
757       {
758          s = strdup("");
759          while ((s != NULL) && fgets(buf, sizeof(buf), fp))
760          {
761             string_join  (&s, html_encode(buf));
762          }
763          fclose(fp);
764
765          if (map(exports, "contents", 1, s, 0))
766          {
767             free_map(exports);
768             return JB_ERR_MEMORY;
769          }
770       }
771
772       return template_fill_for_cgi(csp, "show-status-file", exports, rsp);
773    }
774
775    s = strdup("");
776    for (j = 0; (s != NULL) && (j < Argc); j++)
777    {
778       if (!err) err = string_join  (&s, html_encode(Argv[j]));
779       if (!err) err = string_append(&s, " ");
780    }
781    if (!err) err = map(exports, "invocation", 1, s, 0);
782
783    if (!err) err = map(exports, "options", 1, csp->config->proxy_args, 1);
784    if (!err) err = show_defines(exports);
785
786    if (err) 
787    {
788       free_map(exports);
789       return JB_ERR_MEMORY;
790    }
791
792 #ifdef FEATURE_STATISTICS
793    local_urls_read     = urls_read;
794    local_urls_rejected = urls_rejected;
795
796    /*
797     * Need to alter the stats not to include the fetch of this
798     * page.
799     *
800     * Can't do following thread safely! doh!
801     *
802     * urls_read--;
803     * urls_rejected--; * This will be incremented subsequently *
804     */
805
806    if (local_urls_read == 0)
807    {
808       if (!err) err = map_block_killer(exports, "have-stats");
809    }
810    else
811    {
812       if (!err) err = map_block_killer(exports, "have-no-stats");
813
814       perc_rej = (float)local_urls_rejected * 100.0F /
815             (float)local_urls_read;
816
817       sprintf(buf, "%d", local_urls_read);
818       if (!err) err = map(exports, "requests-received", 1, buf, 1);
819
820       sprintf(buf, "%d", local_urls_rejected);
821       if (!err) err = map(exports, "requests-blocked", 1, buf, 1);
822
823       sprintf(buf, "%6.2f", perc_rej);
824       if (!err) err = map(exports, "percent-blocked", 1, buf, 1);
825    }
826
827 #else /* ndef FEATURE_STATISTICS */
828    err = err || map_block_killer(exports, "statistics");
829 #endif /* ndef FEATURE_STATISTICS */
830    
831    /* 
832     * List all action files in use, together with view and edit links,
833     * except for standard.action, which should only be viewable. (Not
834     * enforced in the editor itself)
835     * FIXME: Shouldn't include hardwired HTML here, use line template instead!
836     */
837    s = strdup("");
838    for (i = 0; i < MAX_ACTION_FILES; i++)
839    {
840       if (((fl = csp->actions_list[i]) != NULL) && ((b = fl->f) != NULL))
841       {
842          if (!err) err = string_append(&s, "<tr><td>");
843          if (!err) err = string_join(&s, html_encode(csp->actions_list[i]->filename));
844          snprintf(buf, 100, "</td><td class=\"buttons\"><a href=\"/show-status?file=actions&index=%d\">View</a> ", i);
845          if (!err) err = string_append(&s, buf);
846
847          if (NULL == strstr(csp->actions_list[i]->filename, "standard.action") && NULL != csp->config->actions_file_short[i])
848          {
849             snprintf(buf, 100, "<a href=\"/edit-actions-list?f=%s\">Edit</a>", csp->config->actions_file_short[i]);
850             if (!err) err = string_append(&s, buf);
851          }
852
853          if (!err) err = string_append(&s, "</td></tr>\n");
854       }
855    }
856    if (*s != '\0')   
857    {
858       if (!err) err = map(exports, "actions-filenames", 1, s, 0);
859    }
860    else
861    {
862       if (!err) err = map(exports, "actions-filenames", 1, "<tr><td>None specified</td></tr>", 1);
863    }
864
865    if (csp->rlist)
866    {
867       if (!err) err = map(exports, "re-filter-filename", 1, html_encode(csp->rlist->filename), 0);
868    }
869    else
870    {
871       if (!err) err = map(exports, "re-filter-filename", 1, "None specified", 1);
872       if (!err) err = map_block_killer(exports, "have-filterfile");
873    }
874
875 #ifdef FEATURE_TRUST
876    if (csp->tlist)
877    {
878       if (!err) err = map(exports, "trust-filename", 1, html_encode(csp->tlist->filename), 0);
879    }
880    else
881    {
882       if (!err) err = map(exports, "trust-filename", 1, "None specified", 1);
883       if (!err) err = map_block_killer(exports, "have-trustfile");
884    }
885 #else
886    if (!err) err = map_block_killer(exports, "trust-support");
887 #endif /* ndef FEATURE_TRUST */
888
889    if (err)
890    {
891       free_map(exports);
892       return JB_ERR_MEMORY;
893    }
894
895    return template_fill_for_cgi(csp, "show-status", exports, rsp);
896 }
897
898  
899 /*********************************************************************
900  *
901  * Function    :  cgi_show_url_info
902  *
903  * Description :  CGI function that determines and shows which actions
904  *                Privoxy will perform for a given url, and which
905  *                matches starting from the defaults have lead to that.
906  *
907  * Parameters  :
908  *          1  :  csp = Current client state (buffers, headers, etc...)
909  *          2  :  rsp = http_response data structure for output
910  *          3  :  parameters = map of cgi parameters
911  *
912  * CGI Parameters :
913  *            url : The url whose actions are to be determined.
914  *                  If url is unset, the url-given conditional will be
915  *                  set, so that all but the form can be suppressed in
916  *                  the template.
917  *
918  * Returns     :  JB_ERR_OK on success
919  *                JB_ERR_MEMORY on out-of-memory error.  
920  *
921  *********************************************************************/
922 jb_err cgi_show_url_info(struct client_state *csp,
923                          struct http_response *rsp,
924                          const struct map *parameters)
925 {
926    char *url_param;
927    struct map *exports;
928    char buf[150];
929
930    assert(csp);
931    assert(rsp);
932    assert(parameters);
933
934    if (NULL == (exports = default_exports(csp, "show-url-info")))
935    {
936       return JB_ERR_MEMORY;
937    }
938
939    /*
940     * Get the url= parameter (if present) and remove any leading/trailing spaces.
941     */
942    url_param = strdup(lookup(parameters, "url"));
943    if (url_param == NULL)
944    {
945       free_map(exports);
946       return JB_ERR_MEMORY;
947    }
948    chomp(url_param);
949
950    /*
951     * Handle prefixes.  4 possibilities:
952     * 1) "http://" or "https://" prefix present and followed by URL - OK
953     * 2) Only the "http://" or "https://" part is present, no URL - change
954     *    to empty string so it will be detected later as "no URL".
955     * 3) Parameter specified but doesn't contain "http(s?)://" - add a
956     *    "http://" prefix.
957     * 4) Parameter not specified or is empty string - let this fall through
958     *    for now, next block of code will handle it.
959     */
960    if (0 == strncmp(url_param, "http://", 7))
961    {
962       if (url_param[7] == '\0')
963       {
964          /*
965           * Empty URL (just prefix).
966           * Make it totally empty so it's caught by the next if()
967           */
968          url_param[0] = '\0';
969       }
970    }
971    else if (0 == strncmp(url_param, "https://", 8))
972    {
973       if (url_param[8] == '\0')
974       {
975          /*
976           * Empty URL (just prefix).
977           * Make it totally empty so it's caught by the next if()
978           */
979          url_param[0] = '\0';
980       }
981    }
982    else if (url_param[0] != '\0')
983    {
984       /*
985        * Unknown prefix - assume http://
986        */
987       char * url_param_prefixed = malloc(7 + 1 + strlen(url_param));
988       if (NULL == url_param_prefixed)
989       {
990          free(url_param);
991          free_map(exports);
992          return JB_ERR_MEMORY;
993       }
994       strcpy(url_param_prefixed, "http://");
995       strcpy(url_param_prefixed + 7, url_param);
996       free(url_param);
997       url_param = url_param_prefixed;
998    }
999
1000
1001    if (url_param[0] == '\0')
1002    {
1003       /* URL paramater not specified, display query form only. */
1004       free(url_param);
1005       if (map_block_killer(exports, "url-given")
1006         || map(exports, "url", 1, "", 1))
1007       {
1008          free_map(exports);
1009          return JB_ERR_MEMORY;
1010       }
1011    }
1012    else
1013    {
1014       /* Given a URL, so query it. */
1015       jb_err err;
1016       char *matches;
1017       char *s;
1018       int hits = 0;
1019       struct file_list *fl;
1020       struct url_actions *b;
1021       struct http_request url_to_query[1];
1022       struct current_action_spec action[1];
1023       int i;
1024       
1025       if (map(exports, "url", 1, html_encode(url_param), 0))
1026       {
1027          free(url_param);
1028          free_map(exports);
1029          return JB_ERR_MEMORY;
1030       }
1031
1032       init_current_action(action);
1033
1034       if (map(exports, "default", 1, current_action_to_html(action, csp), 0))
1035       {
1036          free_current_action(action);
1037          free(url_param);
1038          free_map(exports);
1039          return JB_ERR_MEMORY;
1040       }
1041
1042       err = parse_http_url(url_param, url_to_query, csp);
1043
1044       free(url_param);
1045
1046       if (err == JB_ERR_MEMORY)
1047       {
1048          free_current_action(action);
1049          free_map(exports);
1050          return JB_ERR_MEMORY;
1051       }
1052       else if (err)
1053       {
1054          /* Invalid URL */
1055
1056          err = map(exports, "matches", 1, "<b>[Invalid URL specified!]</b>" , 1);
1057          if (!err) err = map(exports, "final", 1, lookup(exports, "default"), 1);
1058
1059          free_current_action(action);
1060
1061          if (err)
1062          {
1063             free_map(exports);
1064             return JB_ERR_MEMORY;
1065          }
1066
1067          return template_fill_for_cgi(csp, "show-url-info", exports, rsp);
1068       }
1069
1070       /*
1071        * We have a warning about SSL paths.  Hide it for insecure sites.
1072        */
1073       if (!url_to_query->ssl)
1074       {
1075          if (map_block_killer(exports, "https"))
1076          {
1077             free_current_action(action);
1078             free_map(exports);
1079             return JB_ERR_MEMORY;
1080          }
1081       }
1082
1083       matches = strdup("<table class=\"transparent\">");
1084
1085       for (i = 0; i < MAX_ACTION_FILES; i++)
1086       {
1087          if (NULL == csp->config->actions_file_short[i]
1088              || !strcmp(csp->config->actions_file_short[i], "standard")) continue;
1089
1090          b = NULL;
1091          hits = 1;
1092          if ((fl = csp->actions_list[i]) != NULL)
1093          {
1094             if ((b = fl->f) != NULL)
1095             {
1096                /* FIXME: Hardcoded HTML! */
1097                string_append(&matches, "<tr><th>In file: ");
1098                string_join  (&matches, html_encode(csp->config->actions_file_short[i]));
1099                snprintf(buf, 150, ".action <a class=\"cmd\" href=\"/show-status?file=actions&index=%d\">", i);
1100                string_append(&matches, buf);
1101                string_append(&matches, "View</a> <a class=\"cmd\" href=\"/edit-actions-list?f=");
1102                string_join  (&matches, html_encode(csp->config->actions_file_short[i]));
1103                string_append(&matches, "\">Edit</a></th></tr>\n");
1104
1105                hits = 0;
1106                b = b->next;
1107             }
1108          }
1109
1110          for (; (b != NULL) && (matches != NULL); b = b->next)
1111          {
1112             if (url_match(b->url, url_to_query))
1113             {
1114                string_append(&matches, "<tr><td>{");
1115                string_join  (&matches, actions_to_html(b->action, csp));
1116                string_append(&matches, " }</b><br>\n<code>");
1117                string_join  (&matches, html_encode(b->url->spec));
1118                string_append(&matches, "</code></td></tr>\n");
1119
1120                if (merge_current_action(action, b->action))
1121                {
1122                   freez(matches);
1123                   free_http_request(url_to_query);
1124                   free_current_action(action);
1125                   free_map(exports);
1126                   return JB_ERR_MEMORY;
1127                }
1128                hits++;
1129             }
1130          }
1131
1132          if (!hits)
1133          {
1134             string_append(&matches, "<tr><td>(no matches in this file)</td></tr>\n");
1135          }
1136       }
1137       string_append(&matches, "</table>\n");
1138
1139       free_http_request(url_to_query);
1140
1141       if (matches == NULL)
1142       {
1143          free_current_action(action);
1144          free_map(exports);
1145          return JB_ERR_MEMORY;
1146       }
1147
1148       if (map(exports, "matches", 1, matches , 0))
1149       {
1150          free_current_action(action);
1151          free_map(exports);
1152          return JB_ERR_MEMORY;
1153       }
1154
1155       s = current_action_to_html(action, csp);
1156
1157       free_current_action(action);
1158
1159       if (map(exports, "final", 1, s, 0))
1160       {
1161          free_map(exports);
1162          return JB_ERR_MEMORY;
1163       }
1164    }
1165
1166    return template_fill_for_cgi(csp, "show-url-info", exports, rsp);
1167 }
1168
1169
1170 /*********************************************************************
1171  *
1172  * Function    :  cgi_robots_txt
1173  *
1174  * Description :  CGI function to return "/robots.txt".
1175  *
1176  * Parameters  :
1177  *          1  :  csp = Current client state (buffers, headers, etc...)
1178  *          2  :  rsp = http_response data structure for output
1179  *          3  :  parameters = map of cgi parameters
1180  *
1181  * CGI Parameters : None
1182  *
1183  * Returns     :  JB_ERR_OK on success
1184  *                JB_ERR_MEMORY on out-of-memory error.  
1185  *
1186  *********************************************************************/
1187 jb_err cgi_robots_txt(struct client_state *csp,
1188                       struct http_response *rsp,
1189                       const struct map *parameters)
1190 {
1191    char buf[100];
1192    jb_err err;
1193
1194    rsp->body = strdup(
1195       "# This is the Privoxy control interface.\n"
1196       "# It isn't very useful to index it, and you're likely to break stuff.\n"
1197       "# So go away!\n"
1198       "\n"
1199       "User-agent: *\n"
1200       "Disallow: /\n"
1201       "\n");
1202    if (rsp->body == NULL)
1203    {
1204       return JB_ERR_MEMORY;
1205    }
1206
1207    err = enlist_unique(rsp->headers, "Content-Type: text/plain", 13);
1208
1209    rsp->is_static = 1;
1210
1211    get_http_time(7 * 24 * 60 * 60, buf); /* 7 days into future */
1212    if (!err) err = enlist_unique_header(rsp->headers, "Expires", buf);
1213
1214    return (err ? JB_ERR_MEMORY : JB_ERR_OK);
1215 }
1216
1217
1218 /*********************************************************************
1219  *
1220  * Function    :  show_defines
1221  *
1222  * Description :  Add to a map the state od all conditional #defines
1223  *                used when building
1224  *
1225  * Parameters  :
1226  *          1  :  exports = map to extend
1227  *
1228  * Returns     :  JB_ERR_OK on success
1229  *                JB_ERR_MEMORY on out-of-memory error.  
1230  *
1231  *********************************************************************/
1232 static jb_err show_defines(struct map *exports)
1233 {
1234    jb_err err = JB_ERR_OK;
1235
1236 #ifdef FEATURE_ACL
1237    if (!err) err = map_conditional(exports, "FEATURE_ACL", 1);
1238 #else /* ifndef FEATURE_ACL */
1239    if (!err) err = map_conditional(exports, "FEATURE_ACL", 0);
1240 #endif /* ndef FEATURE_ACL */
1241
1242 #ifdef FEATURE_CGI_EDIT_ACTIONS
1243    if (!err) err = map_conditional(exports, "FEATURE_CGI_EDIT_ACTIONS", 1);
1244 #else /* ifndef FEATURE_COOKIE_JAR */
1245    if (!err) err = map_conditional(exports, "FEATURE_CGI_EDIT_ACTIONS", 0);
1246 #endif /* ndef FEATURE_COOKIE_JAR */
1247
1248 #ifdef FEATURE_COOKIE_JAR
1249    if (!err) err = map_conditional(exports, "FEATURE_COOKIE_JAR", 1);
1250 #else /* ifndef FEATURE_COOKIE_JAR */
1251    if (!err) err = map_conditional(exports, "FEATURE_COOKIE_JAR", 0);
1252 #endif /* ndef FEATURE_COOKIE_JAR */
1253
1254 #ifdef FEATURE_FAST_REDIRECTS
1255    if (!err) err = map_conditional(exports, "FEATURE_FAST_REDIRECTS", 1);
1256 #else /* ifndef FEATURE_FAST_REDIRECTS */
1257    if (!err) err = map_conditional(exports, "FEATURE_FAST_REDIRECTS", 0);
1258 #endif /* ndef FEATURE_FAST_REDIRECTS */
1259
1260 #ifdef FEATURE_FORCE_LOAD
1261    if (!err) err = map_conditional(exports, "FEATURE_FORCE_LOAD", 1);
1262    if (!err) err = map(exports, "FORCE_PREFIX", 1, FORCE_PREFIX, 1);
1263 #else /* ifndef FEATURE_FORCE_LOAD */
1264    if (!err) err = map_conditional(exports, "FEATURE_FORCE_LOAD", 0);
1265    if (!err) err = map(exports, "FORCE_PREFIX", 1, "(none - disabled)", 1);
1266 #endif /* ndef FEATURE_FORCE_LOAD */
1267
1268 #ifdef FEATURE_IMAGE_BLOCKING
1269    if (!err) err = map_conditional(exports, "FEATURE_IMAGE_BLOCKING", 1);
1270 #else /* ifndef FEATURE_IMAGE_BLOCKING */
1271    if (!err) err = map_conditional(exports, "FEATURE_IMAGE_BLOCKING", 0);
1272 #endif /* ndef FEATURE_IMAGE_BLOCKING */
1273
1274 #ifdef FEATURE_IMAGE_DETECT_MSIE
1275    if (!err) err = map_conditional(exports, "FEATURE_IMAGE_DETECT_MSIE", 1);
1276 #else /* ifndef FEATURE_IMAGE_DETECT_MSIE */
1277    if (!err) err = map_conditional(exports, "FEATURE_IMAGE_DETECT_MSIE", 0);
1278 #endif /* ndef FEATURE_IMAGE_DETECT_MSIE */
1279
1280 #ifdef FEATURE_KILL_POPUPS
1281    if (!err) err = map_conditional(exports, "FEATURE_KILL_POPUPS", 1);
1282 #else /* ifndef FEATURE_KILL_POPUPS */
1283    if (!err) err = map_conditional(exports, "FEATURE_KILL_POPUPS", 0);
1284 #endif /* ndef FEATURE_KILL_POPUPS */
1285
1286 #ifdef FEATURE_NO_GIFS
1287    if (!err) err = map_conditional(exports, "FEATURE_NO_GIFS", 1);
1288 #else /* ifndef FEATURE_NO_GIFS */
1289    if (!err) err = map_conditional(exports, "FEATURE_NO_GIFS", 0);
1290 #endif /* ndef FEATURE_NO_GIFS */
1291
1292 #ifdef FEATURE_PTHREAD
1293    if (!err) err = map_conditional(exports, "FEATURE_PTHREAD", 1);
1294 #else /* ifndef FEATURE_PTHREAD */
1295    if (!err) err = map_conditional(exports, "FEATURE_PTHREAD", 0);
1296 #endif /* ndef FEATURE_PTHREAD */
1297
1298 #ifdef FEATURE_STATISTICS
1299    if (!err) err = map_conditional(exports, "FEATURE_STATISTICS", 1);
1300 #else /* ifndef FEATURE_STATISTICS */
1301    if (!err) err = map_conditional(exports, "FEATURE_STATISTICS", 0);
1302 #endif /* ndef FEATURE_STATISTICS */
1303
1304 #ifdef FEATURE_TOGGLE
1305    if (!err) err = map_conditional(exports, "FEATURE_TOGGLE", 1);
1306 #else /* ifndef FEATURE_TOGGLE */
1307    if (!err) err = map_conditional(exports, "FEATURE_TOGGLE", 0);
1308 #endif /* ndef FEATURE_TOGGLE */
1309
1310 #ifdef FEATURE_TRUST
1311    if (!err) err = map_conditional(exports, "FEATURE_TRUST", 1);
1312 #else /* ifndef FEATURE_TRUST */
1313    if (!err) err = map_conditional(exports, "FEATURE_TRUST", 0);
1314 #endif /* ndef FEATURE_TRUST */
1315
1316 #ifdef STATIC_PCRE
1317    if (!err) err = map_conditional(exports, "STATIC_PCRE", 1);
1318 #else /* ifndef STATIC_PCRE */
1319    if (!err) err = map_conditional(exports, "STATIC_PCRE", 0);
1320 #endif /* ndef STATIC_PCRE */
1321
1322 #ifdef STATIC_PCRS
1323    if (!err) err = map_conditional(exports, "STATIC_PCRS", 1);
1324 #else /* ifndef STATIC_PCRS */
1325    if (!err) err = map_conditional(exports, "STATIC_PCRS", 0);
1326 #endif /* ndef STATIC_PCRS */
1327
1328    return err;
1329 }
1330
1331
1332 /*********************************************************************
1333  *
1334  * Function    :  show_rcs
1335  *
1336  * Description :  Create a string with the rcs info for all sourcefiles
1337  *
1338  * Parameters  :  None
1339  *
1340  * Returns     :  A string, or NULL on out-of-memory.
1341  *
1342  *********************************************************************/
1343 static char *show_rcs(void)
1344 {
1345    char *result = strdup("");
1346    char buf[BUFFER_SIZE];
1347
1348    /* Instead of including *all* dot h's in the project (thus creating a
1349     * tremendous amount of dependencies), I will concede to declaring them
1350     * as extern's.  This forces the developer to add to this list, but oh well.
1351     */
1352
1353 #define SHOW_RCS(__x)              \
1354    {                               \
1355       extern const char __x[];     \
1356       sprintf(buf, "%s\n", __x);   \
1357       string_append(&result, buf); \
1358    }
1359
1360    /* In alphabetical order */
1361    SHOW_RCS(actions_h_rcs)
1362    SHOW_RCS(actions_rcs)
1363    SHOW_RCS(cgi_h_rcs)
1364    SHOW_RCS(cgi_rcs)
1365 #ifdef FEATURE_CGI_EDIT_ACTIONS
1366    SHOW_RCS(cgiedit_h_rcs)
1367    SHOW_RCS(cgiedit_rcs)
1368 #endif /* def FEATURE_CGI_EDIT_ACTIONS */
1369    SHOW_RCS(cgisimple_h_rcs)
1370    SHOW_RCS(cgisimple_rcs)
1371 #ifdef __MINGW32__
1372    SHOW_RCS(cygwin_h_rcs)
1373 #endif
1374    SHOW_RCS(deanimate_h_rcs)
1375    SHOW_RCS(deanimate_rcs)
1376    SHOW_RCS(encode_h_rcs)
1377    SHOW_RCS(encode_rcs)
1378    SHOW_RCS(errlog_h_rcs)
1379    SHOW_RCS(errlog_rcs)
1380    SHOW_RCS(filters_h_rcs)
1381    SHOW_RCS(filters_rcs)
1382    SHOW_RCS(gateway_h_rcs)
1383    SHOW_RCS(gateway_rcs)
1384    SHOW_RCS(jbsockets_h_rcs)
1385    SHOW_RCS(jbsockets_rcs)
1386    SHOW_RCS(jcc_h_rcs)
1387    SHOW_RCS(jcc_rcs)
1388 #ifdef FEATURE_KILL_POPUPS
1389    SHOW_RCS(killpopup_h_rcs)
1390    SHOW_RCS(killpopup_rcs)
1391 #endif /* def FEATURE_KILL_POPUPS */
1392    SHOW_RCS(list_h_rcs)
1393    SHOW_RCS(list_rcs)
1394    SHOW_RCS(loadcfg_h_rcs)
1395    SHOW_RCS(loadcfg_rcs)
1396    SHOW_RCS(loaders_h_rcs)
1397    SHOW_RCS(loaders_rcs)
1398    SHOW_RCS(miscutil_h_rcs)
1399    SHOW_RCS(miscutil_rcs)
1400    SHOW_RCS(parsers_h_rcs)
1401    SHOW_RCS(parsers_rcs)
1402    SHOW_RCS(pcrs_rcs)
1403    SHOW_RCS(pcrs_h_rcs)
1404    SHOW_RCS(project_h_rcs)
1405    SHOW_RCS(ssplit_h_rcs)
1406    SHOW_RCS(ssplit_rcs)
1407    SHOW_RCS(urlmatch_h_rcs)
1408    SHOW_RCS(urlmatch_rcs)
1409 #ifdef _WIN32
1410 #ifndef _WIN_CONSOLE
1411    SHOW_RCS(w32log_h_rcs)
1412    SHOW_RCS(w32log_rcs)
1413    SHOW_RCS(w32res_h_rcs)
1414    SHOW_RCS(w32taskbar_h_rcs)
1415    SHOW_RCS(w32taskbar_rcs)
1416 #endif /* ndef _WIN_CONSOLE */
1417    SHOW_RCS(win32_h_rcs)
1418    SHOW_RCS(win32_rcs)
1419 #endif /* def _WIN32 */
1420
1421 #undef SHOW_RCS
1422
1423    return result;
1424
1425 }
1426
1427
1428 /*
1429   Local Variables:
1430   tab-width: 3
1431   end:
1432 */