]>
git.gir.st - ttxd.git/blob - src/thttpd-2.27/cgi-src/ssi.c
1 /* ssi - server-side-includes CGI program
3 ** Copyright © 1995 by Jef Poskanzer <jef@mail.acme.com>.
4 ** All rights reserved.
6 ** Redistribution and use in source and binary forms, with or without
7 ** modification, are permitted provided that the following conditions
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.
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
32 #include <sys/types.h>
46 static void read_file( char* vfilename
, char* filename
, FILE* fp
);
52 static char timefmt
[100];
56 static struct stat sb
;
60 internal_error( char* reason
)
62 char* title
= "500 Internal Error";
65 <HTML><HEAD><TITLE>%s</TITLE></HEAD>\n\
67 Something unusual went wrong during a server-side-includes request:\n\
71 </BODY></HTML>\n", title
, title
, reason
);
76 not_found( char* filename
)
78 char* title
= "404 Not Found";
81 <HTML><HEAD><TITLE>%s</TITLE></HEAD>\n\
83 The requested server-side-includes filename, %s,\n\
84 does not seem to exist.\n\
85 </BODY></HTML>\n", title
, title
, filename
);
90 not_found2( char* directive
, char* tag
, char* filename2
)
92 char* title
= "Not Found";
96 The filename requested in a %s %s directive, %s,\n\
97 does not seem to exist.\n\
98 <HR>\n", title
, directive
, tag
, filename2
);
103 not_permitted( char* directive
, char* tag
, char* val
)
105 char* title
= "Not Permitted";
109 The filename requested in the %s %s=%s directive\n\
110 may not be fetched.\n\
111 <HR>\n", title
, directive
, tag
, val
);
116 unknown_directive( char* filename
, char* directive
)
118 char* title
= "Unknown Directive";
122 The requested server-side-includes filename, %s,\n\
123 tried to use an unknown directive, %s.\n\
124 <HR>\n", title
, filename
, directive
);
129 unknown_tag( char* filename
, char* directive
, char* tag
)
131 char* title
= "Unknown Tag";
135 The requested server-side-includes filename, %s,\n\
136 tried to use the directive %s with an unknown tag, %s.\n\
137 <HR>\n", title
, filename
, directive
, tag
);
142 unknown_value( char* filename
, char* directive
, char* tag
, char* val
)
144 char* title
= "Unknown Value";
148 The requested server-side-includes filename, %s,\n\
149 tried to use the directive %s %s with an unknown value, %s.\n\
150 <HR>\n", title
, filename
, directive
, tag
, val
);
155 get_filename( char* vfilename
, char* filename
, char* directive
, char* tag
, char* val
, char* fn
, int fnsize
)
160 /* Used for the various commands that accept a file name.
161 ** These commands accept two tags:
163 ** Gives a virtual path to a document on the server.
165 ** Gives a pathname relative to the current directory. ../ cannot
166 ** be used in this pathname, nor can absolute paths be used.
168 vl
= strlen( vfilename
);
169 fl
= strlen( filename
);
170 if ( strcmp( tag
, "virtual" ) == 0 )
172 if ( strstr( val
, "../" ) != (char*) 0 )
174 not_permitted( directive
, tag
, val
);
177 /* Figure out root using difference between vfilename and filename. */
179 strcmp( vfilename
, &filename
[fl
- vl
] ) != 0 )
181 if ( fl
- vl
+ strlen( val
) >= fnsize
)
183 (void) strncpy( fn
, filename
, fl
- vl
);
184 (void) strcpy( &fn
[fl
- vl
], val
);
186 else if ( strcmp( tag
, "file" ) == 0 )
188 if ( val
[0] == '/' || strstr( val
, "../" ) != (char*) 0 )
190 not_permitted( directive
, tag
, val
);
193 if ( fl
+ 1 + strlen( val
) >= fnsize
)
195 (void) strcpy( fn
, filename
);
196 cp
= strrchr( fn
, '/' );
197 if ( cp
== (char*) 0 )
199 cp
= &fn
[strlen( fn
)];
202 (void) strcpy( ++cp
, val
);
206 unknown_tag( filename
, directive
, tag
);
214 check_filename( char* filename
)
216 static int inited
= 0;
217 static char* cgi_pattern
;
227 /* Get the cgi pattern. */
228 cgi_pattern
= getenv( "CGI_PATTERN" );
230 if ( cgi_pattern
== (char*) 0 )
231 cgi_pattern
= CGI_PATTERN
;
232 #endif /* CGI_PATTERN */
236 /* ../ is not permitted. */
237 if ( strstr( filename
, "../" ) != (char*) 0 )
241 /* Ensure that we are not reading a basic auth password file. */
242 fnl
= strlen(filename
);
243 if ( strcmp( filename
, AUTH_FILE
) == 0 ||
244 ( fnl
>= sizeof(AUTH_FILE
) &&
245 strcmp( &filename
[fnl
- sizeof(AUTH_FILE
) + 1], AUTH_FILE
) == 0 &&
246 filename
[fnl
- sizeof(AUTH_FILE
)] == '/' ) )
249 /* Check for an auth file in the same directory. We can't do an actual
250 ** auth password check here because CGI programs are not given the
251 ** authorization header, for security reasons. So instead we just
252 ** prohibit access to all auth-protected files.
254 dirname
= strdup( filename
);
255 if ( dirname
== (char*) 0 )
256 return 0; /* out of memory */
257 cp
= strrchr( dirname
, '/' );
258 if ( cp
== (char*) 0 )
259 (void) strcpy( dirname
, "." );
262 authname
= malloc( strlen( dirname
) + 1 + sizeof(AUTH_FILE
) );
263 if ( authname
== (char*) 0 )
264 return 0; /* out of memory */
265 (void) sprintf( authname
, "%s/%s", dirname
, AUTH_FILE
);
266 r
= stat( authname
, &sb2
);
271 #endif /* AUTH_FILE */
273 /* Ensure that we are not reading a CGI file. */
274 if ( cgi_pattern
!= (char*) 0 && match( cgi_pattern
, filename
) )
282 show_time( time_t t
, int gmt
)
290 tmP
= localtime( &t
);
291 if ( strftime( tbuf
, sizeof(tbuf
), timefmt
, tmP
) > 0 )
292 (void) fputs( tbuf
, stdout
);
297 show_size( off_t size
)
302 (void) printf( "%ld", (long) size
); /* spec says should have commas */
306 (void) printf( "%ld", (long) size
);
307 else if ( size
< 1024 )
308 (void) printf( "%ldK", (long) size
/ 1024L );
309 else if ( size
< 1024*1024 )
310 (void) printf( "%ldM", (long) size
/ (1024L*1024L) );
312 (void) printf( "%ldG", (long) size
/ (1024L*1024L*1024L) );
319 do_config( char* vfilename
, char* filename
, FILE* fp
, char* directive
, char* tag
, char* val
)
321 /* The config directive controls various aspects of the file parsing.
322 ** There are two valid tags:
324 ** Gives the server a new format to use when providing dates. This
325 ** is a string compatible with the strftime library call.
327 ** Determines the formatting to be used when displaying the size of
328 ** a file. Valid choices are bytes, for a formatted byte count
329 ** (formatted as 1,234,567), or abbrev for an abbreviated version
330 ** displaying the number of kilobytes or megabytes the file occupies.
333 if ( strcmp( tag
, "timefmt" ) == 0 )
335 (void) strncpy( timefmt
, val
, sizeof(timefmt
) - 1 );
336 timefmt
[sizeof(timefmt
) - 1] = '\0';
338 else if ( strcmp( tag
, "sizefmt" ) == 0 )
340 if ( strcmp( val
, "bytes" ) == 0 )
342 else if ( strcmp( val
, "abbrev" ) == 0 )
345 unknown_value( filename
, directive
, tag
, val
);
348 unknown_tag( filename
, directive
, tag
);
353 do_include( char* vfilename
, char* filename
, FILE* fp
, char* directive
, char* tag
, char* val
)
355 char vfilename2
[1000];
356 char filename2
[1000];
359 /* Inserts the text of another document into the parsed document. */
362 vfilename
, filename
, directive
, tag
, val
, filename2
,
363 sizeof(filename2
) ) < 0 )
366 if ( ! check_filename( filename2
) )
368 not_permitted( directive
, tag
, filename2
);
372 fp2
= fopen( filename2
, "r" );
373 if ( fp2
== (FILE*) 0 )
375 not_found2( directive
, tag
, filename2
);
379 if ( strcmp( tag
, "virtual" ) == 0 )
381 if ( strlen( val
) < sizeof( vfilename2
) )
382 (void) strcpy( vfilename2
, val
);
384 (void) strcpy( vfilename2
, filename2
); /* same size, has to fit */
388 if ( strlen( vfilename
) + 1 + strlen( val
) < sizeof(vfilename2
) )
391 (void) strcpy( vfilename2
, vfilename
);
392 cp
= strrchr( vfilename2
, '/' );
393 if ( cp
== (char*) 0 )
395 cp
= &vfilename2
[strlen( vfilename2
)];
398 (void) strcpy( ++cp
, val
);
401 (void) strcpy( vfilename2
, filename2
); /* same size, has to fit */
404 read_file( vfilename2
, filename2
, fp2
);
405 (void) fclose( fp2
);
410 do_echo( char* vfilename
, char* filename
, FILE* fp
, char* directive
, char* tag
, char* val
)
415 /* Prints the value of one of the include variables. Any dates are
416 ** printed subject to the currently configured timefmt. The only valid
417 ** tag is var, whose value is the name of the variable you wish to echo.
420 if ( strcmp( tag
, "var" ) != 0 )
421 unknown_tag( filename
, directive
, tag
);
424 if ( strcmp( val
, "DOCUMENT_NAME" ) == 0 )
426 /* The current filename. */
427 (void) fputs( filename
, stdout
);
429 else if ( strcmp( val
, "DOCUMENT_URI" ) == 0 )
431 /* The virtual path to this file (such as /~robm/foo.shtml). */
432 (void) fputs( vfilename
, stdout
);
434 else if ( strcmp( val
, "QUERY_STRING_UNESCAPED" ) == 0 )
436 /* The unescaped version of any search query the client sent. */
437 cp
= getenv( "QUERY_STRING" );
438 if ( cp
!= (char*) 0 )
439 (void) fputs( cp
, stdout
);
441 else if ( strcmp( val
, "DATE_LOCAL" ) == 0 )
443 /* The current date, local time zone. */
444 t
= time( (time_t*) 0 );
447 else if ( strcmp( val
, "DATE_GMT" ) == 0 )
449 /* Same as DATE_LOCAL but in Greenwich mean time. */
450 t
= time( (time_t*) 0 );
453 else if ( strcmp( val
, "LAST_MODIFIED" ) == 0 )
455 /* The last modification date of the current document. */
456 if ( fstat( fileno( fp
), &sb
) >= 0 )
457 show_time( sb
.st_mtime
, 0 );
461 /* Try an environment variable. */
463 if ( cp
== (char*) 0 )
464 unknown_value( filename
, directive
, tag
, val
);
466 (void) fputs( cp
, stdout
);
473 do_fsize( char* vfilename
, char* filename
, FILE* fp
, char* directive
, char* tag
, char* val
)
475 char filename2
[1000];
477 /* Prints the size of the specified file. */
480 vfilename
, filename
, directive
, tag
, val
, filename2
,
481 sizeof(filename2
) ) < 0 )
483 if ( stat( filename2
, &sb
) < 0 )
485 not_found2( directive
, tag
, filename2
);
488 show_size( sb
.st_size
);
493 do_flastmod( char* vfilename
, char* filename
, FILE* fp
, char* directive
, char* tag
, char* val
)
495 char filename2
[1000];
497 /* Prints the last modification date of the specified file. */
500 vfilename
, filename
, directive
, tag
, val
, filename2
,
501 sizeof(filename2
) ) < 0 )
503 if ( stat( filename2
, &sb
) < 0 )
505 not_found2( directive
, tag
, filename2
);
508 show_time( sb
.st_mtime
, 0 );
513 parse( char* vfilename
, char* filename
, FILE* fp
, char* str
)
524 #define DI_FLASTMOD 4
529 directive
+= strspn( directive
, " \t\n\r" );
535 cp
= strpbrk( cp
, " \t\n\r\"" );
536 if ( cp
== (char*) 0 )
540 cp
= strpbrk( cp
+ 1, "\"" );
546 cp
+= strspn( cp
, " \t\n\r" );
549 if ( ntags
< sizeof(tags
)/sizeof(*tags
) )
553 if ( strcmp( directive
, "config" ) == 0 )
555 else if ( strcmp( directive
, "include" ) == 0 )
557 else if ( strcmp( directive
, "echo" ) == 0 )
559 else if ( strcmp( directive
, "fsize" ) == 0 )
561 else if ( strcmp( directive
, "flastmod" ) == 0 )
565 unknown_directive( filename
, directive
);
569 for ( i
= 0; i
< ntags
; ++i
)
573 val
= strchr( tags
[i
], '=' );
574 if ( val
== (char*) 0 )
578 if ( *val
== '"' && val
[strlen( val
) - 1] == '"' )
580 val
[strlen( val
) - 1] = '\0';
586 do_config( vfilename
, filename
, fp
, directive
, tags
[i
], val
);
589 do_include( vfilename
, filename
, fp
, directive
, tags
[i
], val
);
592 do_echo( vfilename
, filename
, fp
, directive
, tags
[i
], val
);
595 do_fsize( vfilename
, filename
, fp
, directive
, tags
[i
], val
);
598 do_flastmod( vfilename
, filename
, fp
, directive
, tags
[i
], val
);
606 slurp( char* vfilename
, char* filename
, FILE* fp
)
613 /* Now slurp in the rest of the comment from the input file. */
616 while ( ( ich
= getc( fp
) ) != EOF
)
634 parse( vfilename
, filename
, fp
, buf
);
637 else if ( ich
!= '-' )
641 if ( i
< sizeof(buf
) - 1 )
642 buf
[i
++] = (char) ich
;
648 read_file( char* vfilename
, char* filename
, FILE* fp
)
653 /* Copy it to output, while running a state-machine to look for
657 while ( ( ich
= getc( fp
) ) != EOF
)
663 { state
= ST_LESSTHAN
; continue; }
667 { state
= ST_BANG
; continue; }
669 { state
= ST_GROUND
; putchar( '<' ); }
673 { state
= ST_MINUS1
; continue; }
675 { state
= ST_GROUND
; (void) fputs ( "<!", stdout
); }
679 { state
= ST_MINUS2
; continue; }
681 { state
= ST_GROUND
; (void) fputs ( "<!-", stdout
); }
686 slurp( vfilename
, filename
, fp
);
691 { state
= ST_GROUND
; (void) fputs ( "<!--", stdout
); }
694 putchar( (char) ich
);
700 main( int argc
, char** argv
)
704 char* path_translated
;
709 /* Default formats. */
710 (void) strcpy( timefmt
, "%a %b %e %T %Z %Y" );
713 /* The MIME type has to be text/html. */
714 (void) fputs( "Content-type: text/html\n\n", stdout
);
716 /* Get the name that we were run as. */
717 script_name
= getenv( "SCRIPT_NAME" );
718 if ( script_name
== (char*) 0 )
720 internal_error( "Couldn't get SCRIPT_NAME environment variable." );
724 /* Append the PATH_INFO, if any, to get the full URL. */
725 path_info
= getenv( "PATH_INFO" );
726 if ( path_info
== (char*) 0 )
728 url
= (char*) malloc( strlen( script_name
) + strlen( path_info
) + 1 );
729 if ( url
== (char*) 0 )
731 internal_error( "Out of memory." );
734 (void) sprintf( url
, "%s%s", script_name
, path_info
);
736 /* Get the name of the file to parse. */
737 path_translated
= getenv( "PATH_TRANSLATED" );
738 if ( path_translated
== (char*) 0 )
740 internal_error( "Couldn't get PATH_TRANSLATED environment variable." );
744 if ( ! check_filename( path_translated
) )
746 not_permitted( "initial", "PATH_TRANSLATED", path_translated
);
751 fp
= fopen( path_translated
, "r" );
752 if ( fp
== (FILE*) 0 )
754 not_found( path_translated
);
758 /* Read and handle the file. */
759 read_file( path_info
, path_translated
, fp
);