initial capture of my stuff
[rrq/thttpd.git] / cgi-src / redirect.c
1 /* redirect - simple redirection CGI program
2 **
3 ** Copyright © 1995 by Jef Poskanzer <jef@mail.acme.com>.
4 ** All rights reserved.
5 **
6 ** Redistribution and use in source and binary forms, with or without
7 ** modification, are permitted provided that the following conditions
8 ** are met:
9 ** 1. Redistributions of source code must retain the above copyright
10 **    notice, this list of conditions and the following disclaimer.
11 ** 2. Redistributions in binary form must reproduce the above copyright
12 **    notice, this list of conditions and the following disclaimer in the
13 **    documentation and/or other materials provided with the distribution.
14 **
15 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 ** ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 ** SUCH DAMAGE.
26 */
27
28 /* Three steps to set up a redirection:
29 **  1. Make sure your web server is set up to allow CGI programs.
30 **  2. Make a symbolic link from the file you want to redirect,
31 **     pointing at this program in the CGI bin directory.
32 **  3. Add an entry to the file ".redirects" in the directory where your
33 **     http server runs CGI programs.  For most servers, this is the
34 **     directory where the given CGI program lives.  The format of the
35 **     file is a bunch of lines with a filename, whitespace, and the new
36 **     URL.  For example:
37
38 /test/oldfile.html    http://www.acme.com/test/newfile.html
39
40 **     The easiest way to figure out precisely what filename to put into
41 **     .redirects is to set up the symlink and then click on it.  You'll get
42 **     back a "404 Not Found" page which includes the filename as received by
43 **     the redirect program, and that's what you want to use.
44 **
45 ** Note: this is designed for thttpd (http://www.acme.com/software/thttpd/)
46 ** and using it with other web servers may require some hacking.  A possible
47 ** gotcha is with the symbolic link from the old file pointing at this
48 ** script - servers other than thttpd may not allow that link to be run
49 ** as a CGI program, because they don't check the link to see that it
50 ** points into the allowed CGI directory.
51 **
52 ** Note two: It would be really cool to have this program look for
53 ** the .redirects file in the same directory as the file being redirected,
54 ** instead of in the binaries directory.  Unfortunately, this appears
55 ** to be impossible with the information CGI gives, plus the non-standardized
56 ** but widespread practice of running CGI programs in the directory where
57 ** the binary lives.  Perhaps CGI 1.2 will address this.
58 */
59
60 #include <sys/types.h>
61
62 #include <stdio.h>
63 #include <stdlib.h>
64 #include <string.h>
65
66 #include "config.h"
67
68
69 static char* argv0;
70
71
72 static void
73 internal_error( char* reason )
74     {
75     char* title = "500 Internal Error";
76
77     (void) printf( "\
78 Status: %s\n\
79 Content-type: text/html\n\
80 \n\
81 <HTML><HEAD><TITLE>%s</TITLE></HEAD>\n\
82 <BODY><H2>%s</H2>\n\
83 Something unusual went wrong during a redirection request:\n\
84 <BLOCKQUOTE>\n\
85 %s\n\
86 </BLOCKQUOTE>\n\
87 </BODY></HTML>\n", title, title, title, reason );
88     }
89
90
91 static void
92 not_found( char* script_name )
93     {
94     char* title = "404 Not Found";
95
96     (void) printf( "\
97 Status: %s\n\
98 Content-type: text/html\n\
99 \n\
100 <HTML><HEAD><TITLE>%s</TITLE></HEAD>\n\
101 <BODY><H2>%s</H2>\n\
102 The requested filename, %s, is set up to be redirected to another URL;\n\
103 however, the new URL has not yet been specified.\n\
104 </BODY></HTML>\n", title, title, title, script_name );
105     }
106
107
108 static void
109 moved( char* script_name, char* url )
110     {
111     char* title = "Moved";
112
113     (void) printf( "\
114 Location: %s\n\
115 Content-type: text/html\n\
116 \n\
117 <HTML><HEAD><TITLE>%s</TITLE></HEAD>\n\
118 <BODY><H2>%s</H2>\n\
119 The requested filename, %s, has moved to a new URL:\n\
120 <A HREF=\"%s\">%s</A>.\n\
121 </BODY></HTML>\n", url, title, title, script_name, url, url );
122     }
123         
124
125 int
126 main( int argc, char** argv )
127     {
128     char* script_name;
129     char* path_info;
130     char* cp;
131     FILE* fp;
132     char *star;
133     char buf[5000], file[5000], url[5000];
134
135     argv0 = argv[0];
136
137     /* Get the name that we were run as, which is the filename being
138     ** redirected.
139     */
140     script_name = getenv( "SCRIPT_NAME" );
141     if ( script_name == (char*) 0 )
142         {
143         internal_error( "Couldn't get SCRIPT_NAME environment variable." );
144         exit( 1 );
145         }
146
147     /* Append the PATH_INFO, if any.  This allows redirection of whole
148     ** directories.
149     */
150     path_info = getenv( "PATH_INFO" );
151     if ( path_info != (char*) 0 )
152         {
153         cp = (char*) malloc( strlen( script_name ) + strlen( path_info ) + 1 );
154         if ( cp == (char*) 0 )
155             {
156             internal_error( "Out of memory." );
157             exit( 1 );
158             }
159         (void) sprintf( cp, "%s%s", script_name, path_info );
160         script_name = cp;
161         }
162
163     /* Open the redirects file. */
164     fp = fopen( ".redirects", "r" );
165     if ( fp == (FILE*) 0 )
166         {
167         internal_error( "Couldn't open .redirects file." );
168         exit( 1 );
169         }
170
171     /* Search the file for a matching entry. */
172     while ( fgets( buf, sizeof(buf), fp ) != (char*) 0 )
173         {
174         /* Remove comments. */
175         cp = strchr( buf, '#' );
176         if ( cp != (char*) 0 )
177             *cp = '\0';
178         /* Skip leading whitespace. */
179         cp = buf;
180         cp += strspn( cp, " \t" );
181         /* Check for blank line. */
182         if ( *cp != '\0' )
183             {
184             /* Parse line. */
185             if ( sscanf( cp, "%[^ \t\n] %[^ \t\n]", file, url ) == 2 )
186                 {
187                 /* Check for wildcard match. */
188                 star = strchr( file, '*' );
189                 if ( star != (char*) 0 )
190                     {
191                     /* Check for leading match. */
192                     if ( strncmp( file, script_name, star - file ) == 0 )
193                         {
194                         /* Got it; put together the full name. */
195                         strcat( url, script_name + ( star - file ) );
196                         /* XXX Whack the script_name, too? */
197                         moved( script_name, url );
198                         exit( 0 );
199                         }
200                     }
201                 /* Check for exact match. */
202                 if ( strcmp( file, script_name ) == 0 )
203                     {
204                     /* Got it. */
205                     moved( script_name, url );
206                     exit( 0 );
207                     }
208                 }
209             }
210         }
211
212     /* No match found. */
213     not_found( script_name );
214     exit( 1 );
215     }