| 1 |
/* redirect - simple redirection CGI program |
| 2 |
** |
| 3 |
** Copyright © 1995 by Jef Poskanzer <jef@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 |
} |