1 |
/* ssi - server-side-includes 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 |
#include <stdio.h> |
29 |
#include <stdlib.h> |
30 |
#include <string.h> |
31 |
#include <time.h> |
32 |
#include <sys/types.h> |
33 |
#include <sys/stat.h> |
34 |
|
35 |
#include "config.h" |
36 |
#include "match.h" |
37 |
|
38 |
|
39 |
#define ST_GROUND 0 |
40 |
#define ST_LESSTHAN 1 |
41 |
#define ST_BANG 2 |
42 |
#define ST_MINUS1 3 |
43 |
#define ST_MINUS2 4 |
44 |
|
45 |
|
46 |
static void read_file( char* vfilename, char* filename, FILE* fp ); |
47 |
|
48 |
|
49 |
static char* argv0; |
50 |
static char* url; |
51 |
|
52 |
static char timefmt[100]; |
53 |
static int sizefmt; |
54 |
#define SF_BYTES 0 |
55 |
#define SF_ABBREV 1 |
56 |
static struct stat sb; |
57 |
|
58 |
|
59 |
static void |
60 |
internal_error( char* reason ) |
61 |
{ |
62 |
char* title = "500 Internal Error"; |
63 |
|
64 |
(void) printf( "\ |
65 |
<HTML><HEAD><TITLE>%s</TITLE></HEAD>\n\ |
66 |
<BODY><H2>%s</H2>\n\ |
67 |
Something unusual went wrong during a server-side-includes request:\n\ |
68 |
<BLOCKQUOTE>\n\ |
69 |
%s\n\ |
70 |
</BLOCKQUOTE>\n\ |
71 |
</BODY></HTML>\n", title, title, reason ); |
72 |
} |
73 |
|
74 |
|
75 |
static void |
76 |
not_found( char* filename ) |
77 |
{ |
78 |
char* title = "404 Not Found"; |
79 |
|
80 |
(void) printf( "\ |
81 |
<HTML><HEAD><TITLE>%s</TITLE></HEAD>\n\ |
82 |
<BODY><H2>%s</H2>\n\ |
83 |
The requested server-side-includes filename, %s,\n\ |
84 |
does not seem to exist.\n\ |
85 |
</BODY></HTML>\n", title, title, filename ); |
86 |
} |
87 |
|
88 |
|
89 |
static void |
90 |
not_found2( char* directive, char* tag, char* filename2 ) |
91 |
{ |
92 |
char* title = "Not Found"; |
93 |
|
94 |
(void) printf( "\ |
95 |
<HR><H2>%s</H2>\n\ |
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 ); |
99 |
} |
100 |
|
101 |
|
102 |
static void |
103 |
not_permitted( char* directive, char* tag, char* val ) |
104 |
{ |
105 |
char* title = "Not Permitted"; |
106 |
|
107 |
(void) printf( "\ |
108 |
<HR><H2>%s</H2>\n\ |
109 |
The filename requested in the %s %s=%s directive\n\ |
110 |
may not be fetched.\n\ |
111 |
<HR>\n", title, directive, tag, val ); |
112 |
} |
113 |
|
114 |
|
115 |
static void |
116 |
unknown_directive( char* filename, char* directive ) |
117 |
{ |
118 |
char* title = "Unknown Directive"; |
119 |
|
120 |
(void) printf( "\ |
121 |
<HR><H2>%s</H2>\n\ |
122 |
The requested server-side-includes filename, %s,\n\ |
123 |
tried to use an unknown directive, %s.\n\ |
124 |
<HR>\n", title, filename, directive ); |
125 |
} |
126 |
|
127 |
|
128 |
static void |
129 |
unknown_tag( char* filename, char* directive, char* tag ) |
130 |
{ |
131 |
char* title = "Unknown Tag"; |
132 |
|
133 |
(void) printf( "\ |
134 |
<HR><H2>%s</H2>\n\ |
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 ); |
138 |
} |
139 |
|
140 |
|
141 |
static void |
142 |
unknown_value( char* filename, char* directive, char* tag, char* val ) |
143 |
{ |
144 |
char* title = "Unknown Value"; |
145 |
|
146 |
(void) printf( "\ |
147 |
<HR><H2>%s</H2>\n\ |
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 ); |
151 |
} |
152 |
|
153 |
|
154 |
static int |
155 |
get_filename( char* vfilename, char* filename, char* directive, char* tag, char* val, char* fn, int fnsize ) |
156 |
{ |
157 |
int vl, fl; |
158 |
char* cp; |
159 |
|
160 |
/* Used for the various commands that accept a file name. |
161 |
** These commands accept two tags: |
162 |
** virtual |
163 |
** Gives a virtual path to a document on the server. |
164 |
** file |
165 |
** Gives a pathname relative to the current directory. ../ cannot |
166 |
** be used in this pathname, nor can absolute paths be used. |
167 |
*/ |
168 |
vl = strlen( vfilename ); |
169 |
fl = strlen( filename ); |
170 |
if ( strcmp( tag, "virtual" ) == 0 ) |
171 |
{ |
172 |
if ( strstr( val, "../" ) != (char*) 0 ) |
173 |
{ |
174 |
not_permitted( directive, tag, val ); |
175 |
return -1; |
176 |
} |
177 |
/* Figure out root using difference between vfilename and filename. */ |
178 |
if ( vl > fl || |
179 |
strcmp( vfilename, &filename[fl - vl] ) != 0 ) |
180 |
return -1; |
181 |
if ( fl - vl + strlen( val ) >= fnsize ) |
182 |
return -1; |
183 |
(void) strncpy( fn, filename, fl - vl ); |
184 |
(void) strcpy( &fn[fl - vl], val ); |
185 |
} |
186 |
else if ( strcmp( tag, "file" ) == 0 ) |
187 |
{ |
188 |
if ( val[0] == '/' || strstr( val, "../" ) != (char*) 0 ) |
189 |
{ |
190 |
not_permitted( directive, tag, val ); |
191 |
return -1; |
192 |
} |
193 |
if ( fl + 1 + strlen( val ) >= fnsize ) |
194 |
return -1; |
195 |
(void) strcpy( fn, filename ); |
196 |
cp = strrchr( fn, '/' ); |
197 |
if ( cp == (char*) 0 ) |
198 |
{ |
199 |
cp = &fn[strlen( fn )]; |
200 |
*cp = '/'; |
201 |
} |
202 |
(void) strcpy( ++cp, val ); |
203 |
} |
204 |
else |
205 |
{ |
206 |
unknown_tag( filename, directive, tag ); |
207 |
return -1; |
208 |
} |
209 |
return 0; |
210 |
} |
211 |
|
212 |
|
213 |
static int |
214 |
check_filename( char* filename ) |
215 |
{ |
216 |
static int inited = 0; |
217 |
static char* cgi_pattern; |
218 |
int fnl; |
219 |
char* cp; |
220 |
char* dirname; |
221 |
char* authname; |
222 |
struct stat sb; |
223 |
int r; |
224 |
|
225 |
if ( ! inited ) |
226 |
{ |
227 |
/* Get the cgi pattern. */ |
228 |
cgi_pattern = getenv( "CGI_PATTERN" ); |
229 |
#ifdef CGI_PATTERN |
230 |
if ( cgi_pattern == (char*) 0 ) |
231 |
cgi_pattern = CGI_PATTERN; |
232 |
#endif /* CGI_PATTERN */ |
233 |
inited = 1; |
234 |
} |
235 |
|
236 |
/* ../ is not permitted. */ |
237 |
if ( strstr( filename, "../" ) != (char*) 0 ) |
238 |
return 0; |
239 |
|
240 |
#ifdef AUTH_FILE |
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)] == '/' ) ) |
247 |
return 0; |
248 |
|
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. |
253 |
*/ |
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, "." ); |
260 |
else |
261 |
*cp = '\0'; |
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, &sb ); |
267 |
free( dirname ); |
268 |
free( authname ); |
269 |
if ( r == 0 ) |
270 |
return 0; |
271 |
#endif /* AUTH_FILE */ |
272 |
|
273 |
/* Ensure that we are not reading a CGI file. */ |
274 |
if ( cgi_pattern != (char*) 0 && match( cgi_pattern, filename ) ) |
275 |
return 0; |
276 |
|
277 |
return 1; |
278 |
} |
279 |
|
280 |
|
281 |
static void |
282 |
show_time( time_t t, int gmt ) |
283 |
{ |
284 |
struct tm* tmP; |
285 |
char tbuf[500]; |
286 |
|
287 |
if ( gmt ) |
288 |
tmP = gmtime( &t ); |
289 |
else |
290 |
tmP = localtime( &t ); |
291 |
if ( strftime( tbuf, sizeof(tbuf), timefmt, tmP ) > 0 ) |
292 |
(void) fputs( tbuf, stdout ); |
293 |
} |
294 |
|
295 |
|
296 |
static void |
297 |
show_size( off_t size ) |
298 |
{ |
299 |
switch ( sizefmt ) |
300 |
{ |
301 |
case SF_BYTES: |
302 |
(void) printf( "%ld", (long) size ); /* spec says should have commas */ |
303 |
break; |
304 |
case SF_ABBREV: |
305 |
if ( size < 1024 ) |
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) ); |
311 |
else |
312 |
(void) printf( "%ldG", (long) size / (1024L*1024L*1024L) ); |
313 |
break; |
314 |
} |
315 |
} |
316 |
|
317 |
|
318 |
static void |
319 |
do_config( char* vfilename, char* filename, FILE* fp, char* directive, char* tag, char* val ) |
320 |
{ |
321 |
/* The config directive controls various aspects of the file parsing. |
322 |
** There are two valid tags: |
323 |
** timefmt |
324 |
** Gives the server a new format to use when providing dates. This |
325 |
** is a string compatible with the strftime library call. |
326 |
** sizefmt |
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. |
331 |
*/ |
332 |
|
333 |
if ( strcmp( tag, "timefmt" ) == 0 ) |
334 |
{ |
335 |
(void) strncpy( timefmt, val, sizeof(timefmt) - 1 ); |
336 |
timefmt[sizeof(timefmt) - 1] = '\0'; |
337 |
} |
338 |
else if ( strcmp( tag, "sizefmt" ) == 0 ) |
339 |
{ |
340 |
if ( strcmp( val, "bytes" ) == 0 ) |
341 |
sizefmt = SF_BYTES; |
342 |
else if ( strcmp( val, "abbrev" ) == 0 ) |
343 |
sizefmt = SF_ABBREV; |
344 |
else |
345 |
unknown_value( filename, directive, tag, val ); |
346 |
} |
347 |
else |
348 |
unknown_tag( filename, directive, tag ); |
349 |
} |
350 |
|
351 |
|
352 |
static void |
353 |
do_include( char* vfilename, char* filename, FILE* fp, char* directive, char* tag, char* val ) |
354 |
{ |
355 |
char vfilename2[1000]; |
356 |
char filename2[1000]; |
357 |
FILE* fp2; |
358 |
|
359 |
/* Inserts the text of another document into the parsed document. */ |
360 |
|
361 |
if ( get_filename( |
362 |
vfilename, filename, directive, tag, val, filename2, |
363 |
sizeof(filename2) ) < 0 ) |
364 |
return; |
365 |
|
366 |
if ( ! check_filename( filename2 ) ) |
367 |
{ |
368 |
not_permitted( directive, tag, filename2 ); |
369 |
return; |
370 |
} |
371 |
|
372 |
fp2 = fopen( filename2, "r" ); |
373 |
if ( fp2 == (FILE*) 0 ) |
374 |
{ |
375 |
not_found2( directive, tag, filename2 ); |
376 |
return; |
377 |
} |
378 |
|
379 |
if ( strcmp( tag, "virtual" ) == 0 ) |
380 |
{ |
381 |
if ( strlen( val ) < sizeof( vfilename2 ) ) |
382 |
(void) strcpy( vfilename2, val ); |
383 |
else |
384 |
(void) strcpy( vfilename2, filename2 ); /* same size, has to fit */ |
385 |
} |
386 |
else |
387 |
{ |
388 |
if ( strlen( vfilename ) + 1 + strlen( val ) < sizeof(vfilename2) ) |
389 |
{ |
390 |
char* cp; |
391 |
(void) strcpy( vfilename2, vfilename ); |
392 |
cp = strrchr( vfilename2, '/' ); |
393 |
if ( cp == (char*) 0 ) |
394 |
{ |
395 |
cp = &vfilename2[strlen( vfilename2 )]; |
396 |
*cp = '/'; |
397 |
} |
398 |
(void) strcpy( ++cp, val ); |
399 |
} |
400 |
else |
401 |
(void) strcpy( vfilename2, filename2 ); /* same size, has to fit */ |
402 |
} |
403 |
|
404 |
read_file( vfilename2, filename2, fp2 ); |
405 |
(void) fclose( fp2 ); |
406 |
} |
407 |
|
408 |
|
409 |
static void |
410 |
do_echo( char* vfilename, char* filename, FILE* fp, char* directive, char* tag, char* val ) |
411 |
{ |
412 |
char* cp; |
413 |
time_t t; |
414 |
|
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. |
418 |
*/ |
419 |
|
420 |
if ( strcmp( tag, "var" ) != 0 ) |
421 |
unknown_tag( filename, directive, tag ); |
422 |
else |
423 |
{ |
424 |
if ( strcmp( val, "DOCUMENT_NAME" ) == 0 ) |
425 |
{ |
426 |
/* The current filename. */ |
427 |
(void) fputs( filename, stdout ); |
428 |
} |
429 |
else if ( strcmp( val, "DOCUMENT_URI" ) == 0 ) |
430 |
{ |
431 |
/* The virtual path to this file (such as /~robm/foo.shtml). */ |
432 |
(void) fputs( vfilename, stdout ); |
433 |
} |
434 |
else if ( strcmp( val, "QUERY_STRING_UNESCAPED" ) == 0 ) |
435 |
{ |
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 ); |
440 |
} |
441 |
else if ( strcmp( val, "DATE_LOCAL" ) == 0 ) |
442 |
{ |
443 |
/* The current date, local time zone. */ |
444 |
t = time( (time_t*) 0 ); |
445 |
show_time( t, 0 ); |
446 |
} |
447 |
else if ( strcmp( val, "DATE_GMT" ) == 0 ) |
448 |
{ |
449 |
/* Same as DATE_LOCAL but in Greenwich mean time. */ |
450 |
t = time( (time_t*) 0 ); |
451 |
show_time( t, 1 ); |
452 |
} |
453 |
else if ( strcmp( val, "LAST_MODIFIED" ) == 0 ) |
454 |
{ |
455 |
/* The last modification date of the current document. */ |
456 |
if ( fstat( fileno( fp ), &sb ) >= 0 ) |
457 |
show_time( sb.st_mtime, 0 ); |
458 |
} |
459 |
else |
460 |
{ |
461 |
/* Try an environment variable. */ |
462 |
cp = getenv( val ); |
463 |
if ( cp == (char*) 0 ) |
464 |
unknown_value( filename, directive, tag, val ); |
465 |
else |
466 |
(void) fputs( cp, stdout ); |
467 |
} |
468 |
} |
469 |
} |
470 |
|
471 |
|
472 |
static void |
473 |
do_fsize( char* vfilename, char* filename, FILE* fp, char* directive, char* tag, char* val ) |
474 |
{ |
475 |
char filename2[1000]; |
476 |
|
477 |
/* Prints the size of the specified file. */ |
478 |
|
479 |
if ( get_filename( |
480 |
vfilename, filename, directive, tag, val, filename2, |
481 |
sizeof(filename2) ) < 0 ) |
482 |
return; |
483 |
if ( stat( filename2, &sb ) < 0 ) |
484 |
{ |
485 |
not_found2( directive, tag, filename2 ); |
486 |
return; |
487 |
} |
488 |
show_size( sb.st_size ); |
489 |
} |
490 |
|
491 |
|
492 |
static void |
493 |
do_flastmod( char* vfilename, char* filename, FILE* fp, char* directive, char* tag, char* val ) |
494 |
{ |
495 |
char filename2[1000]; |
496 |
|
497 |
/* Prints the last modification date of the specified file. */ |
498 |
|
499 |
if ( get_filename( |
500 |
vfilename, filename, directive, tag, val, filename2, |
501 |
sizeof(filename2) ) < 0 ) |
502 |
return; |
503 |
if ( stat( filename2, &sb ) < 0 ) |
504 |
{ |
505 |
not_found2( directive, tag, filename2 ); |
506 |
return; |
507 |
} |
508 |
show_time( sb.st_mtime, 0 ); |
509 |
} |
510 |
|
511 |
|
512 |
static void |
513 |
parse( char* vfilename, char* filename, FILE* fp, char* str ) |
514 |
{ |
515 |
char* directive; |
516 |
char* cp; |
517 |
int ntags; |
518 |
char* tags[200]; |
519 |
int dirn; |
520 |
#define DI_CONFIG 0 |
521 |
#define DI_INCLUDE 1 |
522 |
#define DI_ECHO 2 |
523 |
#define DI_FSIZE 3 |
524 |
#define DI_FLASTMOD 4 |
525 |
int i; |
526 |
char* val; |
527 |
|
528 |
directive = str; |
529 |
directive += strspn( directive, " \t\n\r" ); |
530 |
|
531 |
ntags = 0; |
532 |
cp = directive; |
533 |
for (;;) |
534 |
{ |
535 |
cp = strpbrk( cp, " \t\n\r\"" ); |
536 |
if ( cp == (char*) 0 ) |
537 |
break; |
538 |
if ( *cp == '"' ) |
539 |
{ |
540 |
cp = strpbrk( cp + 1, "\"" ); |
541 |
++cp; |
542 |
if ( *cp == '\0' ) |
543 |
break; |
544 |
} |
545 |
*cp++ = '\0'; |
546 |
cp += strspn( cp, " \t\n\r" ); |
547 |
if ( *cp == '\0' ) |
548 |
break; |
549 |
if ( ntags < sizeof(tags)/sizeof(*tags) ) |
550 |
tags[ntags++] = cp; |
551 |
} |
552 |
|
553 |
if ( strcmp( directive, "config" ) == 0 ) |
554 |
dirn = DI_CONFIG; |
555 |
else if ( strcmp( directive, "include" ) == 0 ) |
556 |
dirn = DI_INCLUDE; |
557 |
else if ( strcmp( directive, "echo" ) == 0 ) |
558 |
dirn = DI_ECHO; |
559 |
else if ( strcmp( directive, "fsize" ) == 0 ) |
560 |
dirn = DI_FSIZE; |
561 |
else if ( strcmp( directive, "flastmod" ) == 0 ) |
562 |
dirn = DI_FLASTMOD; |
563 |
else |
564 |
{ |
565 |
unknown_directive( filename, directive ); |
566 |
return; |
567 |
} |
568 |
|
569 |
for ( i = 0; i < ntags; ++i ) |
570 |
{ |
571 |
if ( i > 0 ) |
572 |
putchar( ' ' ); |
573 |
val = strchr( tags[i], '=' ); |
574 |
if ( val == (char*) 0 ) |
575 |
val = ""; |
576 |
else |
577 |
*val++ = '\0'; |
578 |
if ( *val == '"' && val[strlen( val ) - 1] == '"' ) |
579 |
{ |
580 |
val[strlen( val ) - 1] = '\0'; |
581 |
++val; |
582 |
} |
583 |
switch( dirn ) |
584 |
{ |
585 |
case DI_CONFIG: |
586 |
do_config( vfilename, filename, fp, directive, tags[i], val ); |
587 |
break; |
588 |
case DI_INCLUDE: |
589 |
do_include( vfilename, filename, fp, directive, tags[i], val ); |
590 |
break; |
591 |
case DI_ECHO: |
592 |
do_echo( vfilename, filename, fp, directive, tags[i], val ); |
593 |
break; |
594 |
case DI_FSIZE: |
595 |
do_fsize( vfilename, filename, fp, directive, tags[i], val ); |
596 |
break; |
597 |
case DI_FLASTMOD: |
598 |
do_flastmod( vfilename, filename, fp, directive, tags[i], val ); |
599 |
break; |
600 |
} |
601 |
} |
602 |
} |
603 |
|
604 |
|
605 |
static void |
606 |
slurp( char* vfilename, char* filename, FILE* fp ) |
607 |
{ |
608 |
char buf[1000]; |
609 |
int i; |
610 |
int state; |
611 |
int ich; |
612 |
|
613 |
/* Now slurp in the rest of the comment from the input file. */ |
614 |
i = 0; |
615 |
state = ST_GROUND; |
616 |
while ( ( ich = getc( fp ) ) != EOF ) |
617 |
{ |
618 |
switch ( state ) |
619 |
{ |
620 |
case ST_GROUND: |
621 |
if ( ich == '-' ) |
622 |
state = ST_MINUS1; |
623 |
break; |
624 |
case ST_MINUS1: |
625 |
if ( ich == '-' ) |
626 |
state = ST_MINUS2; |
627 |
else |
628 |
state = ST_GROUND; |
629 |
break; |
630 |
case ST_MINUS2: |
631 |
if ( ich == '>' ) |
632 |
{ |
633 |
buf[i - 2] = '\0'; |
634 |
parse( vfilename, filename, fp, buf ); |
635 |
return; |
636 |
} |
637 |
else if ( ich != '-' ) |
638 |
state = ST_GROUND; |
639 |
break; |
640 |
} |
641 |
if ( i < sizeof(buf) - 1 ) |
642 |
buf[i++] = (char) ich; |
643 |
} |
644 |
} |
645 |
|
646 |
|
647 |
static void |
648 |
read_file( char* vfilename, char* filename, FILE* fp ) |
649 |
{ |
650 |
int ich; |
651 |
int state; |
652 |
|
653 |
/* Copy it to output, while running a state-machine to look for |
654 |
** SSI directives. |
655 |
*/ |
656 |
state = ST_GROUND; |
657 |
while ( ( ich = getc( fp ) ) != EOF ) |
658 |
{ |
659 |
switch ( state ) |
660 |
{ |
661 |
case ST_GROUND: |
662 |
if ( ich == '<' ) |
663 |
{ state = ST_LESSTHAN; continue; } |
664 |
break; |
665 |
case ST_LESSTHAN: |
666 |
if ( ich == '!' ) |
667 |
{ state = ST_BANG; continue; } |
668 |
else |
669 |
{ state = ST_GROUND; putchar( '<' ); } |
670 |
break; |
671 |
case ST_BANG: |
672 |
if ( ich == '-' ) |
673 |
{ state = ST_MINUS1; continue; } |
674 |
else |
675 |
{ state = ST_GROUND; (void) fputs ( "<!", stdout ); } |
676 |
break; |
677 |
case ST_MINUS1: |
678 |
if ( ich == '-' ) |
679 |
{ state = ST_MINUS2; continue; } |
680 |
else |
681 |
{ state = ST_GROUND; (void) fputs ( "<!-", stdout ); } |
682 |
break; |
683 |
case ST_MINUS2: |
684 |
if ( ich == '#' ) |
685 |
{ |
686 |
slurp( vfilename, filename, fp ); |
687 |
state = ST_GROUND; |
688 |
continue; |
689 |
} |
690 |
else |
691 |
{ state = ST_GROUND; (void) fputs ( "<!--", stdout ); } |
692 |
break; |
693 |
} |
694 |
putchar( (char) ich ); |
695 |
} |
696 |
} |
697 |
|
698 |
|
699 |
int |
700 |
main( int argc, char** argv ) |
701 |
{ |
702 |
char* script_name; |
703 |
char* path_info; |
704 |
char* path_translated; |
705 |
FILE* fp; |
706 |
|
707 |
argv0 = argv[0]; |
708 |
|
709 |
/* Default formats. */ |
710 |
(void) strcpy( timefmt, "%a %b %e %T %Z %Y" ); |
711 |
sizefmt = SF_BYTES; |
712 |
|
713 |
/* The MIME type has to be text/html. */ |
714 |
(void) fputs( "Content-type: text/html\n\n", stdout ); |
715 |
|
716 |
/* Get the name that we were run as. */ |
717 |
script_name = getenv( "SCRIPT_NAME" ); |
718 |
if ( script_name == (char*) 0 ) |
719 |
{ |
720 |
internal_error( "Couldn't get SCRIPT_NAME environment variable." ); |
721 |
exit( 1 ); |
722 |
} |
723 |
|
724 |
/* Append the PATH_INFO, if any, to get the full URL. */ |
725 |
path_info = getenv( "PATH_INFO" ); |
726 |
if ( path_info == (char*) 0 ) |
727 |
{ |
728 |
internal_error( "Couldn't get PATH_INFO environment variable." ); |
729 |
exit( 1 ); |
730 |
} |
731 |
url = (char*) malloc( strlen( script_name ) + strlen( path_info ) + 1 ); |
732 |
if ( url == (char*) 0 ) |
733 |
{ |
734 |
internal_error( "Out of memory." ); |
735 |
exit( 1 ); |
736 |
} |
737 |
(void) sprintf( url, "%s%s", script_name, path_info ); |
738 |
|
739 |
/* Get the name of the file to parse. */ |
740 |
path_translated = getenv( "PATH_TRANSLATED" ); |
741 |
if ( path_translated == (char*) 0 ) |
742 |
{ |
743 |
internal_error( "Couldn't get PATH_TRANSLATED environment variable." ); |
744 |
exit( 1 ); |
745 |
} |
746 |
|
747 |
if ( ! check_filename( path_translated ) ) |
748 |
{ |
749 |
not_permitted( "initial", "PATH_TRANSLATED", path_translated ); |
750 |
exit( 1 ); |
751 |
} |
752 |
|
753 |
/* Open it. */ |
754 |
fp = fopen( path_translated, "r" ); |
755 |
if ( fp == (FILE*) 0 ) |
756 |
{ |
757 |
not_found( path_translated ); |
758 |
exit( 1 ); |
759 |
} |
760 |
|
761 |
/* Read and handle the file. */ |
762 |
read_file( path_info, path_translated, fp ); |
763 |
|
764 |
(void) fclose( fp ); |
765 |
exit( 0 ); |
766 |
} |