- load_config_line() and load_permissions_file() now use chomp()
[privoxy.git] / project.h
1 #ifndef _PROJECT_H
2 #define _PROJECT_H
3 #define PROJECT_H_VERSION "$Id: project.h,v 1.7 2001/05/29 09:50:24 jongfoster Exp $"
4 /*********************************************************************
5  *
6  * File        :  $Source: /cvsroot/ijbswa/current/project.h,v $
7  *
8  * Purpose     :  Defines data structures which are widely used in the
9  *                project.  Does not define any variables or functions
10  *                (though it does declare some macros).
11  *
12  * Copyright   :  Written by and Copyright (C) 2001 the SourceForge
13  *                IJBSWA team.  http://ijbswa.sourceforge.net
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: project.h,v $
39  *    Revision 1.7  2001/05/29 09:50:24  jongfoster
40  *    Unified blocklist/imagelist/permissionslist.
41  *    File format is still under discussion, but the internal changes
42  *    are (mostly) done.
43  *
44  *    Also modified interceptor behaviour:
45  *    - We now intercept all URLs beginning with one of the following
46  *      prefixes (and *only* these prefixes):
47  *        * http://i.j.b/
48  *        * http://ijbswa.sf.net/config/
49  *        * http://ijbswa.sourceforge.net/config/
50  *    - New interceptors "home page" - go to http://i.j.b/ to see it.
51  *    - Internal changes so that intercepted and fast redirect pages
52  *      are not replaced with an image.
53  *    - Interceptors now have the option to send a binary page direct
54  *      to the client. (i.e. ijb-send-banner uses this)
55  *    - Implemented show-url-info interceptor.  (Which is why I needed
56  *      the above interceptors changes - a typical URL is
57  *      "http://i.j.b/show-url-info?url=www.somesite.com/banner.gif".
58  *      The previous mechanism would not have intercepted that, and
59  *      if it had been intercepted then it then it would have replaced
60  *      it with an image.)
61  *
62  *    Revision 1.6  2001/05/27 22:17:04  oes
63  *
64  *    - re_process_buffer no longer writes the modified buffer
65  *      to the client, which was very ugly. It now returns the
66  *      buffer, which it is then written by chat.
67  *
68  *    - content_length now adjusts the Content-Length: header
69  *      for modified documents rather than crunch()ing it.
70  *      (Length info in csp->content_length, which is 0 for
71  *      unmodified documents)
72  *
73  *    - For this to work, sed() is called twice when filtering.
74  *
75  *    Revision 1.5  2001/05/26 00:28:36  jongfoster
76  *    Automatic reloading of config file.
77  *    Removed obsolete SIGHUP support (Unix) and Reload menu option (Win32).
78  *    Most of the global variables have been moved to a new
79  *    struct configuration_spec, accessed through csp->config->globalname
80  *    Most of the globals remaining are used by the Win32 GUI.
81  *
82  *    Revision 1.4  2001/05/22 18:46:04  oes
83  *
84  *    - Enabled filtering banners by size rather than URL
85  *      by adding patterns that replace all standard banner
86  *      sizes with the "Junkbuster" gif to the re_filterfile
87  *
88  *    - Enabled filtering WebBugs by providing a pattern
89  *      which kills all 1x1 images
90  *
91  *    - Added support for PCRE_UNGREEDY behaviour to pcrs,
92  *      which is selected by the (nonstandard and therefore
93  *      capital) letter 'U' in the option string.
94  *      It causes the quantifiers to be ungreedy by default.
95  *      Appending a ? turns back to greedy (!).
96  *
97  *    - Added a new interceptor ijb-send-banner, which
98  *      sends back the "Junkbuster" gif. Without imagelist or
99  *      MSIE detection support, or if tinygif = 1, or the
100  *      URL isn't recognized as an imageurl, a lame HTML
101  *      explanation is sent instead.
102  *
103  *    - Added new feature, which permits blocking remote
104  *      script redirects and firing back a local redirect
105  *      to the browser.
106  *      The feature is conditionally compiled, i.e. it
107  *      can be disabled with --disable-fast-redirects,
108  *      plus it must be activated by a "fast-redirects"
109  *      line in the config file, has its own log level
110  *      and of course wants to be displayed by show-proxy-args
111  *      Note: Boy, all the #ifdefs in 1001 locations and
112  *      all the fumbling with configure.in and acconfig.h
113  *      were *way* more work than the feature itself :-(
114  *
115  *    - Because a generic redirect template was needed for
116  *      this, tinygif = 3 now uses the same.
117  *
118  *    - Moved GIFs, and other static HTTP response templates
119  *      to project.h
120  *
121  *    - Some minor fixes
122  *
123  *    - Removed some >400 CRs again (Jon, you really worked
124  *      a lot! ;-)
125  *
126  *    Revision 1.3  2001/05/20 01:21:20  jongfoster
127  *    Version 2.9.4 checkin.
128  *    - Merged popupfile and cookiefile, and added control over PCRS
129  *      filtering, in new "permissionsfile".
130  *    - Implemented LOG_LEVEL_FATAL, so that if there is a configuration
131  *      file error you now get a message box (in the Win32 GUI) rather
132  *      than the program exiting with no explanation.
133  *    - Made killpopup use the PCRS MIME-type checking and HTTP-header
134  *      skipping.
135  *    - Removed tabs from "config"
136  *    - Moved duplicated url parsing code in "loaders.c" to a new funcition.
137  *    - Bumped up version number.
138  *
139  *    Revision 1.2  2001/05/17 23:01:01  oes
140  *     - Cleaned CRLF's from the sources and related files
141  *
142  *    Revision 1.1.1.1  2001/05/15 13:59:03  oes
143  *    Initial import of version 2.9.3 source tree
144  *
145  *
146  *********************************************************************/
147 \f
148
149 /* Declare struct FILE for vars and funcs. */
150 #include <stdio.h>
151
152 /* Need time_t for file_list */
153 #include <time.h>
154
155 /*
156  * Include appropriate regular expression libraries.
157  *
158  * PCRS           ==> Include pcre
159  * REGEX && PCRE  ==> Include pcre and pcreposix
160  * REGEX && !PCRE ==> Include gnu_regex
161  *
162  * STATIC  ==> Use  #include "pcre.h"  (compiling at same time)
163  * !STATIC ==> Use  #include <pcre.h>  (System library)
164  *
165  */
166 #if (defined(REGEX) && defined(PCRE)) || defined(PCRS)
167 #  ifdef STATIC
168 #    include "pcre.h"
169 #  else
170 #    include <pcre.h>
171 #  endif
172 #endif /* (defined(REGEX) && defined(PCRE)) || defined(PCRS) */
173
174 #if defined(REGEX) && defined(PCRE)
175 #  ifdef STATIC
176 #    include "pcreposix.h"
177 #  else
178 #    include <pcreposix.h>
179 #  endif
180 #endif /* defined(REGEX) && defined(PCRE) */
181
182 #if defined(REGEX) && !defined(PCRE)
183 #  include "gnu_regex.h"
184 #endif
185
186 #ifdef PCRS
187 #include "pcrs.h"
188 #endif /* def PCRS */
189
190 #ifdef AMIGA 
191 #include "amiga.h" 
192 #endif /* def AMIGA */
193
194 #ifdef __cplusplus
195 extern "C" {
196 #endif
197
198 #define FOREVER 1
199
200 /* Default IP and port to listen on */
201 #define HADDR_DEFAULT   "127.0.0.1"
202 #define HADDR_PORT      8000
203
204
205 /* Need this for struct gateway */
206 struct client_state;
207
208 /* Need this for struct client_state */
209 struct configuration_spec;
210
211
212 struct http_request
213 {
214    char *cmd;
215    char *gpc;
216    char *host;
217    int   port;
218    char *path;
219    char *ver;
220    char *hostport; /* "host[:port]" */
221    int   ssl;
222 };
223
224 struct gateway
225 {
226    /* generic attributes */
227    char *name;
228    int (*conn)(const struct gateway *, struct http_request *, struct client_state *);
229    int   type;
230
231    /* domain specific attributes */
232    char *gateway_host;
233    int   gateway_port;
234
235    char *forward_host;
236    int   forward_port;
237 };
238
239
240 /* Generic linked list of strings */
241 struct list
242 {
243    char *str;
244    struct list *last;
245    struct list *next;
246 };
247
248
249 /* An I/O buffer */
250 struct iob
251 {
252    char *buf;
253    char *cur;
254    char *eod;
255 };
256
257
258 #define IOB_PEEK(CSP) ((CSP->iob->cur > CSP->iob->eod) ? (CSP->iob->eod - CSP->iob->cur) : 0)
259 #define IOB_RESET(CSP) if(CSP->iob->buf) free(CSP->iob->buf); memset(CSP->iob, '\0', sizeof(CSP->iob));
260
261
262 /* Constants defining bitmask for csp->accept_types */
263
264 #ifdef DETECT_MSIE_IMAGES
265
266 /* MSIE detected by user-agent string */
267 #define ACCEPT_TYPE_IS_MSIE     0x0001
268
269 /*
270  * *If* this is MSIE, it wants an image.  (Or this is a shift-reload, or
271  * it's got an image from this URL before...  yuck!)
272  * Only meaningful if ACCEPT_TYPE_IS_MSIE set 
273  */
274 #define ACCEPT_TYPE_MSIE_IMAGE  0x0002
275
276 /*
277  * *If* this is MSIE, it wants a HTML document.
278  * Only meaningful if ACCEPT_TYPE_IS_MSIE set
279  */
280 #define ACCEPT_TYPE_MSIE_HTML   0x0004
281
282 #endif /* def DETECT_MSIE_IMAGES */
283
284
285 struct client_state
286 {
287    /* The proxy's configuration */
288    struct configuration_spec * config;
289
290
291    /* The permissions that the current URL has */
292    int  permissions;
293
294
295    /* socket to talk to client (web browser) */
296    int  cfd;
297
298    /* socket to talk to server (web server or proxy) */
299    int  sfd;
300
301
302 #ifdef STATISTICS
303    /* 1 if this URL was rejected, 0 otherwise. Allows actual stats inc to 
304     * occur in main thread only for thread-safety. 
305     */
306    int  rejected;
307 #endif /* def STATISTICS */
308
309 #ifdef FORCE_LOAD
310    int force;
311 #endif /* def FORCE_LOAD */
312
313 #ifdef TOGGLE
314    int   toggled_on;
315 #endif /* def TOGGLE */
316
317    /*
318     * Client PC's IP address, as reported by the accept()_ function.
319     * Both as string and number
320     */
321    char *ip_addr_str;
322    long  ip_addr_long;
323
324 #ifdef TRUST_FILES
325    /* The referer in this request, if one was specified. */
326    char *referrer;
327 #endif /* def TRUST_FILES */
328
329 #if defined(DETECT_MSIE_IMAGES)
330    /* Types the client will accept.
331     * Bitmask - see ACCEPT_TYPE_XXX constants.
332     */
333    int accept_types;
334 #endif /* defined(DETECT_MSIE_IMAGES) */
335
336    /* The URL that was requested */
337    struct http_request http[1];
338
339    /* An I/O buffer used for buffering data read from the client */
340    struct iob iob[1];
341
342    /* List of all headers for this request */
343    struct list headers[1];
344
345    /* List of all cookies for this request */
346    struct list cookie_list[1];
347
348 #if defined(PCRS) || defined(KILLPOPUPS)
349    /* Nonzero if this has a text MIME type */
350    int is_text;
351 #endif /* defined(PCRS) || defined(KILLPOPUPS) */
352
353    /* The "X-Forwarded-For:" header sent by the client */
354    char   *x_forwarded;
355
356    /*
357     * Nonzero if this client is processing data.
358     * Set to zero when the thread associated with this structure dies.
359     */
360    int active;
361
362    /* files associated with this client */
363    struct file_list *flist;   /* forwardfile */
364    struct file_list *permissions_list;
365
366
367 #ifdef ACL_FILES
368    struct file_list *alist;   /* aclfile */
369 #endif /* def ACL_FILES */
370
371 #ifdef PCRS
372      struct file_list *rlist;   /* Perl re_filterfile */
373      size_t content_length;     /* Length after processing */ 
374 #endif /* def PCRS */
375
376 #ifdef TRUST_FILES
377    struct file_list *tlist;   /* trustfile */
378 #endif /* def TRUST_FILES */
379
380    struct client_state *next;
381 };
382
383
384 struct parsers
385 {
386    char *str;
387    char  len;
388    char *(*parser)(const struct parsers *, char *, struct client_state *);
389 };
390
391
392 struct interceptors
393 {
394    char *str;
395    char  len;
396    char *(*interceptor)(struct http_request *http, struct client_state *csp);
397 };
398
399
400 /* A URL pattern */
401 struct url_spec
402 {
403    char  *spec;
404    char  *domain;
405    char  *dbuf;
406    char **dvec;
407    int    dcnt;
408    int    toplevel;
409
410    char *path;
411    int   pathlen;
412    int   port;
413 #ifdef REGEX
414    regex_t *preg;
415 #endif
416 };
417
418
419 struct file_list
420 {
421    /*
422     * this is a pointer to the data structures associated with the file.
423     * Read-only once the structure has been created.
424     */
425    void *f;
426    
427    /* Normally NULL.  When we are finished with file (i.e. when we have
428     * loaded a new one), set to a pointer to an unloader function.
429     * Unloader will be called by sweep() (called from main loop) when
430     * all clients using this file are done.  This prevents threading 
431     * problems.
432     */
433    void (*unloader)(void *);
434
435    /* Used internally by sweep().  Do not access from elsewhere. */
436    int active;
437
438 #ifndef SPLIT_PROXY_ARGS
439    /* String to be displayed as part of show-proxy-args display.
440     * Read-only once the structure has been created.
441     */
442    char *proxy_args;
443 #endif /* ndef SPLIT_PROXY_ARGS */
444
445    /* Following variables allow us to check if file has been changed.
446     * Read-only once the structure has been created.
447     */
448    time_t lastmodified;
449    char * filename;
450
451    /* Pointer to next entry in the linked list of all "file_list"s.
452     * This linked list is so that sweep() can navigate it.
453     * Since sweep() can remove items from the list, we must be careful
454     * to only access this value from main thread (when we know sweep
455     * won't be running).
456     */
457    struct file_list *next;
458 };
459
460
461 #ifdef TRUST_FILES
462 struct block_spec
463 {
464    struct url_spec url[1];
465    int    reject;
466    struct block_spec *next;
467 };
468 #endif /* def TRUST_FILES */
469
470
471 #define PERMIT_COOKIE_SET      0x0001U
472 #define PERMIT_COOKIE_READ     0x0002U
473 #define PERMIT_RE_FILTER       0x0004U
474 #define PERMIT_POPUPS          0x0008U
475 #define PERMIT_REFERER         0x0010U /* sic - follow HTTP, not English */
476 #define PERMIT_FAST_REDIRECTS  0x0020U
477 #define PERMIT_BLOCK           0x0040U
478 #define PERMIT_IMAGE           0x0080U
479
480 #define PERMIT_USER_AGENT      PERMIT_COOKIE_SET /* FIXME  Alias this for now */
481
482 #define PERMIT_MASK_ALL        (~0U)
483
484 #define PERMIT_MOST_COMPATIBLE (PERMIT_COOKIE_SET | PERMIT_COOKIE_READ | \
485    PERMIT_REFERER | PERMIT_POPUPS | PERMIT_USER_AGENT)
486
487 struct permissions_spec
488 {
489    struct url_spec url[1];
490    unsigned mask;   /* a bit set to "0" = remove permission */
491    unsigned add;    /* a bit set to "1" = add permission */
492    struct permissions_spec * next;
493 };
494
495
496 struct forward_spec
497 {
498    struct url_spec url[1];
499    struct gateway gw[1];
500    struct forward_spec *next;
501 };
502
503
504 #ifdef PCRS
505 struct re_filterfile_spec
506 {
507    struct list patterns[1];
508    /* See README.re_filter */
509    pcrs_job *joblist;
510 };
511 #endif /* def PCRS */
512
513
514 #ifdef ACL_FILES
515 #define ACL_PERMIT   1  /* accept connection request */
516 #define ACL_DENY     2  /* reject connection request */
517
518 struct access_control_addr
519 {
520    unsigned long addr;
521    unsigned long mask;
522    unsigned long port;
523 };
524
525 struct access_control_list
526 {
527    struct access_control_addr src[1];
528    struct access_control_addr dst[1];
529
530    short action;
531    struct access_control_list *next;
532 };
533 #endif /* def ACL_FILES */
534
535
536 /* Maximum number of loaders (permissions, block, forward, acl...) */
537 #define NLOADERS 8
538
539 /*
540  * Data loaded from the configuration file.
541  *
542  * (Anomaly: toggle is still handled through a global, not this structure)
543  */
544 struct configuration_spec
545 {
546    int debug;
547    int multi_threaded;
548
549 #ifdef IMAGE_BLOCKING
550    int tinygif;
551    const char *tinygifurl;
552 #endif /* def IMAGE_BLOCKING */
553
554    const char *logfile;
555
556    const char *permissions_file;
557    const char *forwardfile;
558
559 #ifdef ACL_FILES
560    const char *aclfile;
561 #endif /* def ACL_FILES */
562
563 #ifdef PCRS
564    const char *re_filterfile;
565 #endif /* def PCRS */
566
567    /*
568     * Permissions to use for URLs not in the permissions list.
569     */
570    int default_permissions;
571
572 #ifdef JAR_FILES
573    const char * jarfile;
574    FILE * jar;
575 #endif /* def JAR_FILES */
576
577    const char *referrer;
578    const char *uagent;
579    const char *from;
580
581    int add_forwarded;
582
583    struct list wafer_list[1];
584    struct list xtra_list[1];
585
586    /*
587     * Port and IP to bind to.
588     * Defaults to HADDR_DEFAULT:HADDR_PORT == 127.0.0.1:8000
589     */
590    const char *haddr;
591    int         hport;
592
593 #ifndef SPLIT_PROXY_ARGS
594    const char *suppress_message;
595 #endif /* ndef SPLIT_PROXY_ARGS */
596
597 #ifndef SPLIT_PROXY_ARGS
598    /* suppress listing config files */
599    int suppress_blocklists;
600 #endif /* ndef SPLIT_PROXY_ARGS */
601
602 #ifdef TRUST_FILES
603    const char * trustfile;
604
605    struct list trust_info[1];
606    struct url_spec *trust_list[64];
607 #endif /* def TRUST_FILES */
608
609    /* Various strings for show-proxy-args */
610    char *proxy_args_header;
611    char *proxy_args_invocation;
612    char *proxy_args_gateways;
613    char *proxy_args_trailer;
614
615    /* the configuration file object. */
616    struct file_list *config_file_list;
617
618    /* List of loaders */
619    int (*loaders[NLOADERS])(struct client_state *);
620
621    /* bool, nonzero if we need to bind() to the new port */
622    int need_bind;
623 };
624
625
626 #define SZ(X)  (sizeof(X) / sizeof(*X))
627
628 #define WHITEBG   "<body bgcolor=\"#ffffff\" link=\"#000078\" alink=\"#ff0022\" vlink=\"#787878\">\n"
629 #define BODY      "<body bgcolor=\"#f8f8f0\" link=\"#000078\" alink=\"#ff0022\" vlink=\"#787878\">\n"
630 #define BANNER    "<strong>Internet J<small>UNK<i><font color=\"red\">BUSTER</font></i></small></strong>"
631
632 #ifdef FORCE_LOAD
633 #define FORCE_PREFIX "/IJB-FORCE-LOAD"
634 #endif /* def FORCE_LOAD */
635
636 #define HOME_PAGE_URL  "http://ijbswa.sourceforge.net/"
637 #define REDIRECT_URL HOME_PAGE_URL "redirect.php?v=" VERSION "&to="
638
639 static const char CFAIL[] =
640    "HTTP/1.0 503 Connect failed\n"
641    "Content-Type: text/html\n\n"
642    "<html>\n"
643    "<head>\n"
644    "<title>Internet Junkbuster: Connect failed</title>\n"
645    "</head>\n"
646    BODY
647    "<h1><center>"
648    BANNER
649    "</center></h1>"
650    "TCP connection to '%s' failed: %s.\n<br>"
651    "</body>\n"
652    "</html>\n";
653
654 static const char CNXDOM[] =
655    "HTTP/1.0 404 Non-existent domain\n"
656    "Content-Type: text/html\n\n"
657    "<html>\n"
658    "<head>\n"
659    "<title>Internet Junkbuster: Non-existent domain</title>\n"
660    "</head>\n"
661    BODY
662    "<h1><center>"
663    BANNER
664    "</center></h1>"
665    "No such domain: %s\n"
666    "</body>\n"
667    "</html>\n";
668
669 static const char CNOBANNER[] =
670    "HTTP/1.0 200 No Banner\n"
671    "Content-Type: text/html\n\n"
672    "<html>\n"
673    "<head>\n"
674    "<title>Internet Junkbuster: No Banner</title>\n"
675    "</head>\n"
676    BODY
677    "<h1><center>"
678    BANNER
679    "</h1>"
680    "You asked for a banner that this proxy can't produce because either configuration does not permit.\n<br>"
681    "or the URL didn't end with .gif\n"
682    "</center></body>\n"
683    "</html>\n";
684
685 static const char CSUCCEED[] =
686    "HTTP/1.0 200 Connection established\n"
687    "Proxy-Agent: IJ/" VERSION "\n\n";
688
689 static const char CHEADER[] =
690    "HTTP/1.0 400 Invalid header received from browser\n\n";
691
692 static const char SHEADER[] =
693    "HTTP/1.0 502 Invalid header received from server\n\n";
694
695 #ifdef IMAGE_BLOCKING
696
697 /*
698  * Hint: You can encode your own GIFs like that:
699  * perl -e 'while (read STDIN, $c, 1) { printf("\\%.3o,", unpack("C", $c)); }'
700  */
701
702 static const char BLANKGIF[] =
703    "HTTP/1.0 200 OK\r\n"
704    "Pragma: no-cache\r\n"
705    "Last-Modified: Thu Jul 31, 1997 07:42:22 pm GMT\r\n"
706    "Expires:       Thu Jul 31, 1997 07:42:22 pm GMT\r\n"
707    "Content-type: image/gif\r\n\r\n"
708    "GIF89a\001\000\001\000\200\000\000\377\377\377\000\000"
709    "\000!\371\004\001\000\000\000\000,\000\000\000\000\001"
710    "\000\001\000\000\002\002D\001\000;";
711
712 static const char JBGIF[] =
713    "HTTP/1.0 200 OK\r\n"
714    "Pragma: no-cache\r\n"
715    "Last-Modified: Thu Jul 31, 1997 07:42:22 pm GMT\r\n"
716    "Expires:       Thu Jul 31, 1997 07:42:22 pm GMT\r\n"
717    "Content-type: image/gif\r\n\r\n"
718    "GIF89aD\000\013\000\360\000\000\000\000\000\377\377\377!"
719    "\371\004\001\000\000\001\000,\000\000\000\000D\000\013\000"
720    "\000\002a\214\217\251\313\355\277\000\200G&K\025\316hC\037"
721    "\200\234\230Y\2309\235S\230\266\206\372J\253<\3131\253\271"
722    "\270\215\342\254\013\203\371\202\264\334P\207\332\020o\266"
723    "N\215I\332=\211\312\3513\266:\026AK)\364\370\365aobr\305"
724    "\372\003S\275\274k2\354\254z\347?\335\274x\306^9\374\276"
725    "\037Q\000\000;";
726
727 #endif /* def IMAGE_BLOCKING */
728
729 #if defined(FAST_REDIRECTS) || defined(IMAGE_BLOCKING)
730
731 static const char HTTP_REDIRECT_TEMPLATE[] =
732       "HTTP/1.0 302 Local Redirect from Junkbuster\r\n" 
733       "Pragma: no-cache\r\n"
734       "Last-Modified: Thu Jul 31, 1997 07:42:22 pm GMT\r\n"
735       "Expires:       Thu Jul 31, 1997 07:42:22 pm GMT\r\n"
736       "Location: %s\r\n\r\n";
737
738 #endif /*  defined(FAST_REDIRECTS) || defined(IMAGE_BLOCKING) */
739
740 #ifdef __cplusplus
741 } /* extern "C" */
742 #endif
743
744 #endif /* ndef _PROJECT_H */
745
746 /*
747   Local Variables:
748   tab-width: 3
749   end:
750 */