/*
* Name:
* StarZ
* Purpose:
* File Browser/Getter for World Wide Web Browsers.
* Language:
* C
* Description:
* This is a Common Gateway Interface program which when accessed
* with an HTTP GET as SCRIPT_URL (see #defines below) will return
* a directory listing of files available on the host machine.
*
* The directory listing is in the shape of an HTML form with a
* "checkbox" button for each directory entry. The size of each file
* or an indication that it is a directory are given.
*
* A button for "mode select" is also given.
*
* The default mode is File Browse.
* A selected file is displayed, or a selected directory generates a
* new HTML form for that directory.
*
* The other mode is File Get.
* Selected files and/or directories are put in a tar archive and
* compressed back to the Web browser program.
* Authors:
* MJC: Martin Clayton (Starlink)
* {enter_new_authors_here}
* History:
* 14-DEC-1994 (MJC):
* Original Version.
* 30-DEC-1994 (MJC):
* Added a direct "change directory to" text entry field.
* Changed note relating to Netscape to reflect change in Company name.
* 12-APR-1995 (MJC):
* Limited access to directories under /star tree.
* {enter_further_changes_here}
* Problems:
* Nothing checks that a file for browse is a text file.
* Default file protections are used, so a remote user can access
* any unprotected files on the system hosting this program.
* Some large HTML forms are slowly processed by the common
* Web browser Mosaic, this is best overcome by using a more advanced
* browser such as Netscape Communications' Netscape but remains a general
* problem.
* The code may well be OSF/1 specific.
* {note_further_problems_here}
* Bugs:
* {note_any_bugs_here}
*-
*/
/*Standard Include files:
*/
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <strings.h>
#include <time.h>
/*Local Macros.
*/
#define MAX_ENTRIES (10240) /* Maximum number of lines in cgi request. */
#define LIST_ENTRIES (10240) /* Maximum number of files in a Get request. */
#define TEXT_BUFSIZE (1024) /* Internal buffer size. */
#define ENT_WID (64) /* Maximum length of a file name. */
#define MODE_FLAG "spoingmode" /*Internal flag for Browse/Get mode. */
#define DEFAULT_DIR "/star" /* First directory to be listed. */
/*Where the program will be placed.
*/
#define SCRIPT_URL "http://www.star.ucl.ac.uk/mjc-cgi/S.tar.Z"
/*Maintenance details.
*/
#define AUTHOR_NAME "Martin Clayton"
#define AUTHOR_URL "<A HREF=\"mailto:mjc@starlink.ucl.ac.uk\">mjc@starlink.ucl.ac.uk</A>"
/*Structure to hold a cgi request list entry.
*/
typedef struct {
char *name;
char *val;
} entry;
/*Function prototypes, local.
*/
void html_file( char *a, char *b, int nf );
void html_directory( char *a, int nf );
void dir_dot_parse( char *s, char *d );
void tar_zed( entry *e, int m );
/*
*Function prototypes, external.
*/
char *makeword( char *line, char stop );
char *fmakeword( FILE *f, char stop, int *len );
void unescape_url( char *url );
void plustospace (char *str );
/*
*Function prototyping for lclint checking
*/
/*@nullterminated@*/char *getenv(/*@nullterminated@*/char *);
int atoi(/*@nullterminated@*/char *);
/*@nullterminated@*/char *strcpy(char *, /*@nullterminated@*/char *);
int strcmp(/*@nullterminated@*/ const char *s1, /*@nullterminated@*/ const char *s2);
/*@nullterminated@*/char *strcat(/*@nullterminated@*/char *dst, /*@nullterminated@*/const char *src);
int stat(/*@nullterminated@*/const char *path, struct stat *buf);
int strncmp(const char *s1, const char *s2, size_t n);
DIR *opendir(/*@nullterminated@*/const char *dirname);
char *strncpy(char *dst, const char *src, size_t n);
void qsort(void *base, size_t nel, size_t width,
int (*compar) (const void *, const void *));
struct tm *localtime(const time_t *clock);
void *memcpy(void *s1, const void *s2, size_t n);
int sprintf(char *s, /*@nullterminated@*/const char *format, /* args */ ...);
int system(const char *string);
/*****************************************************************************/
main()
{
/*
* Local Variables:
*/
entry entries[MAX_ENTRIES];
int x;
int m; /*number of lines in request*/
int cl;
char wdir[TEXT_BUFSIZE];
/*@nullterminated@*/ char adir[TEXT_BUFSIZE];
struct stat statbuf;
/*.
*/
/*No request given.
*/
if ( !getenv( "CONTENT_LENGTH" ) ) {
m = -1;
/*Move request lines into "entry" array.
*/
} else {
cl = atoi( getenv( "CONTENT_LENGTH" ) );
m = 0;
for ( x = 0; cl && ( !feof( stdin ) ); x++ ) {
m = x;
entries[x].val = fmakeword( stdin, '&', &cl );
plustospace( entries[x].val );
unescape_url( entries[x].val );
entries[x].name = makeword( entries[x].val, '=' );
}
}
/*No request made. Initial inquiry from remote machine.
*/
if ( ( m == -1 ) || ( !entries[0].name ) ) {
html_directory( "", 0 );
/*Decode the request.
*/
} else {
/* Request for compressed archive data.
*/
if ( !strcmp( entries[0].name, MODE_FLAG ) ) {
tar_zed( &entries[1], m - 1 );
/* Browse request.
*/
} else if ( !strcmp( entries[0].name, "Explicit" ) ) {
/* Check for explicit directory or file name.
*/
if ( strcmp( entries[0].val, "" ) ) {
strcpy( adir, entries[0].val );
} else if ( m > 0 ) {
/* Build required file name, first directory part.
*/
strcpy( adir, entries[1].val );
/* Add in a / if not root directory.
*/
if ( strcmp( adir, "/" ) ) {
strcat( adir, "/" );
}
/* Append the actual file name.
*/
strcat( adir, entries[1].name );
} else {
strcpy( adir, DEFAULT_DIR );
}
/* Modify the file name if it is . or ..
*/
dir_dot_parse( adir, wdir );
/* Get file information.
*/
stat( wdir, &statbuf );
/* Build a directory listing.
*/
if ( statbuf.st_mode & S_IFDIR) {
html_directory( wdir, m - 1 );
/* Display a file's contents.
*/
} else {
html_file( wdir, entries[1].name, m - 1 );
}
}
}
/*Done.
*/
exit ( EXIT_SUCCESS );
}
/*****************************************************************************/
void html_directory(
/*+
* Name:
* html_directory
* Purpose:
* Build directory listing in HTML.
* Language:
* C
* Invocation:
* html_directory( dirname, nf )
* Arguments:
* dirname = char * (Given)
* Pointer to name of the directory to be listed.
* nf = int (Given)
* Total number of files in request.
* Authors:
* MJC: Martin Clayton (Starlink)
* {enter_new_authors_here}
* History:
* 14-DEC-1994 (MJC):
* Original Version.
* 20-DEC-1994 (MJC):
* Added support for file last modification time to be displayed.
* 21-DEC-1994 (MJC):
* Removed spurious <P> from second control button pair.
* 21-JAN-1995 (MJC):
* Added gopher images to directory listings.
* 12-APR-1995 (MJC):
* Limited access to directories under /star tree.
* {enter_further_changes_here}
* Bugs:
* {note_any_bugs_here}
*-
* Arguments Given:
*/
char *dirname, /* Name of directory to be displayed. */
int nf /* Number of files in request. */
)
{
/*
* Local Variables:
*/
DIR *the_directory;
struct dirent *the_file;
struct stat statbuf;
struct tm ttime;
char tbuf[128];
char full_path[TEXT_BUFSIZE];
char adir[TEXT_BUFSIZE];
char ent_tab[LIST_ENTRIES][ENT_WID];
int entries;
int notvalid = 0;
int i;
/*.
*/
/*When not an empty string, use supplied file name.
*/
if ( *dirname != '\0' ) {
strcpy( full_path, dirname );
/*Otherwise set to default directory.
*/
} else {
strcpy( full_path, DEFAULT_DIR );
}
/*Make sure request is in /star tree, if not go to default directory.
*/
if ( strncmp( full_path, DEFAULT_DIR, 5 ) ) {
notvalid = 1;
strcpy( full_path, DEFAULT_DIR );
}
/*Try to open the directory.
*/
the_directory = opendir( full_path );
/*Start response output.
*/
www_begin( T_HTML );
/*HTML title.
*/
www_head( "StarZ: File browser-getter." );
hline( "<BODY>" );
/*Heading.
*/
hrule( );
hheading( "StarZ: File browser-getter", 1 );
hrule( );
/*Print error message if unable to open directory.
*/
if ( !the_directory ) {
printf( "Could not open %s directory.%c", full_path, NEW_LINE );
/*Otherwise start to build listing.
*/
} else {
/* Currently, only one directory can be selected for listing.
* The first chosen is displayed, others are ignored except that
* the following message is displayed.
*/
if ( nf > 0 ) {
printf( "<B>Only first entry in list will be displayed.</B>%c",
NEW_LINE );
}
/* Print warning that request is not in /star tree.
*/
if ( notvalid ) {
printf( "<B>Your request has been denied, default %s directory selected instead.</B>%c",
DEFAULT_DIR, NEW_LINE );
}
/* Start HTML FORM.
* URL of where script will reside.
*/
printf( "<FORM action=\"%s\" method=\"POST\">%c",
SCRIPT_URL, NEW_LINE );
/* Submit and reset buttons. These are duplicated later.
*/
printf( "<INPUT type=submit value=\" Submit Request \">%c", NEW_LINE );
printf( "<INPUT type=reset value=\" Reset \"><P>%c", NEW_LINE );
/* Browse or Get mode-select button.
*/
printf( "<INPUT TYPE=\"checkbox\" NAME=\"%s\" ", MODE_FLAG );
printf( "VALUE=\"%s\"> <B>Get or Browse (default) mode</B><BR>%c",
full_path, NEW_LINE);
/* Text input field for entry of explicit file or directory name.
*/
printf( "<INPUT SIZE=32 TYPE=TEXT NAME=\"Explicit\"> <B>Change Directory</B><BR>%c", NEW_LINE );
/* List heading.
*/
printf( "<H2>Directory of %s</H2>%c", full_path, NEW_LINE );
/* Build directory listing.
*/
entries = 0;
while ( ( the_file = readdir( the_directory ) ) &&
( entries < LIST_ENTRIES ) ) {
/* Don't include . and .. in list for sort.
*/
if ( !strcmp( the_file->d_name, "." ) ) {
continue;
} else if ( !strcmp( the_file->d_name, ".." ) ) {
continue;
/* Other file names added to list.
*/
} else {
strncpy( ent_tab[entries], the_file->d_name, ENT_WID );
ent_tab[entries][ENT_WID - 1] = '\0';
entries++;
}
}
/* Close the directory.
*/
closedir( the_directory );
/* Sort the directory list.
*/
qsort( (void *)(ent_tab), entries + 1, ENT_WID, strcmp );
/* Start output of the listing.
*/
printf( "<PRE WIDTH=64>%c", NEW_LINE );
/* Current directory button.
*/
printf( "<INPUT TYPE=\"checkbox\" NAME=\".\" VALUE=\"%s\">",
full_path);
printf( " <-current directory->%c", NEW_LINE );
/* Parent directory button.
*/
if ( strcmp( full_path, "/" ) ) {
printf( "<INPUT TYPE=\"checkbox\" NAME=\"..\" VALUE=\"%s\">",
full_path);
printf( " <-parent directory%c", NEW_LINE );
}
/* Files themselves.
*/
for ( i = 1; i <= entries; i++ ) {
/* Start with button.
*/
printf( "<INPUT TYPE=\"checkbox\" NAME=\"%s\" VALUE=\"%s\">",
ent_tab[i], full_path );
/* Get file information.
*/
strcpy( adir, full_path );
if ( strcmp( adir, "/" ) ) {
strcat( adir, "/" );
}
strcat( adir, ent_tab[i] );
stat( adir, &statbuf );
/* Mark as a directory if that is the case.
*/
if ( statbuf.st_mode & S_IFDIR) {
printf( " <IMG ALIGN=absbottom BORDER=0 SRC=\"internal-gopher-menu\">" );
printf( " %-32s", ent_tab[i] );
printf( "%9s", "directory" );
/* Otherwise print the size of the file.
*/
} else {
if ( extension( ent_tab[i], "html" ) ) {
printf( " <IMG ALIGN=absbottom BORDER=0 SRC=\"internal-gopher-text\">" );
} else {
printf( " <IMG ALIGN=absbottom BORDER=0 SRC=\"internal-gopher-unknown\">" );
}
printf( " %-32s", ent_tab[i] );
printf( "%9d", (int)( statbuf.st_size ) );
}
/* Last modification date/time information.
*/
(void)(memcpy( &ttime, localtime(&statbuf.st_mtime),
sizeof(struct tm)) );
(void)(strncpy( tbuf, asctime( &ttime ), 26 ) );
*( tbuf + 24 ) = '\0';
printf( " %s%c", tbuf, NEW_LINE );
}
/* End of the directory listing.
*/
printf( "</PRE>%c", NEW_LINE );
/* Output a second set of control buttons.
*/
printf( "<INPUT type=submit value=\" Submit Request \">%c",
NEW_LINE );
printf( "<INPUT type=reset value=\" Reset \">%c", NEW_LINE );
/* End of form.
*/
printf( "</FORM>%c", NEW_LINE );
}
/*End main part of response
*/
hrule( );
/*HTML Address field.
*/
www_address( AUTHOR_NAME, AUTHOR_URL, NULL );
/*End response.
*/
hline( "</BODY>" );
www_end( );
/*Done.
*/
return;
}
/*****************************************************************************/
void dir_dot_parse(
/*+
* Name:
* dir_dot_parse
* Purpose:
* Finds simplest version of a file specifier with . or .. at end.
* Language:
* C
* Invocation:
* dir_dot_parse( indir, outdir )
* Arguments:
* indir = char * (Given)
* Pointer to the file specification to be checked.
* outdir = char * (Given and Returned)
* Pointer to space for parsed specification, must be big enough
* to hold converted file name.
* Authors:
* MJC: Martin Clayton (Starlink)
* {enter_new_authors_here}
* History:
* 14-DEC-1994 (MJC):
* Original Version.
* {enter_further_changes_here}
* Bugs:
* {note_any_bugs_here}
*-
* Arguments Given:
*/
char *indir,
char *outdir
)
{
/*
* Local Variables:
*/
char *cptr;
char *eptr;
/*.
*/
/*Mark default return value as a blank string.
*/
strcpy( outdir, "" );
if ( !strcmp( indir, "" ) ) {
return;
}
/*Find the end of the supplied string.
*/
cptr = indir;
while ( *cptr ) {
cptr++;
}
eptr = cptr;
/*Point to last non-null character in string.
*/
if ( cptr > indir ) {
cptr--;
} else {
return;
}
/*Does string end in a '.'?
*/
if ( *cptr == '.' ) {
cptr--;
/* Current directory, remove /. part of file name.
*/
if ( *cptr == '/' ) {
*cptr++ = '\0';
*cptr = '\0';
} else if ( *cptr == '.' ) {
cptr--;
/* Parent directory, remove /.. part of file name and
* name of the current directory, unless the current
* directory is root, in which case just remove the ..
*/
if ( *cptr == '/' ) {
while ( cptr > indir ) {
cptr--;
if ( *cptr == '/' ) {
break;
}
}
if ( cptr > indir) {
while ( cptr < eptr ) {
*cptr++ = '\0';
}
} else {
*(indir + 1) = '\0';
}
}
}
}
/*Copy the parsed file specifier to output buffer.
*/
strcpy ( outdir, indir );
/*Done.
*/
return;
}
/*****************************************************************************/
void html_file(
/*+
* Name:
* html_file
* Purpose:
* Display a text file in an HTML 'wrapper'.
* Language:
* C
* Invocation:
* html_file( fname, sname, nf )
* Arguments:
* fname = char * (Given)
* Pointer to full path of the file to be displayed.
* sname = char * (Given)
* Pointer to name of the file only.
* nf = int (Given)
* Total number of files in request.
* Authors:
* MJC: Martin Clayton (Starlink)
* {enter_new_authors_here}
* History:
* 14-DEC-1994 (MJC):
* Original Version.
* 21-DEC-1994 (MJC):
* Added missing <BODY>.
* {enter_further_changes_here}
* Bugs:
* {note_any_bugs_here}
*-
* Arguments Given:
*/
char *fname, /* Full path of file. */
char *sname, /* Name only of file. */
int nf /* Total number of files in request. */
)
{
/*
* Local Variables:
*/
char thecommand[4 * TEXT_BUFSIZE];
char title[1024]; /* Workspace for page title. */
/*.
*/
/*It's a gif wrap in simple HTML page.
*/
if ( extension( fname, "gif" ) ) {
/* Start response.
*/
www_begin( T_HTML );
sprintf( title, "StarZ: %s", sname );
www_head( title );
hline( "<BODY>" );
/*Pointer to image.
*/
printf( "<IMG SRC=\"%s\"><P>%c", fname, NEW_LINE );
/* Attach an address field.
*/
www_address( AUTHOR_NAME, AUTHOR_URL, NULL );
/* End response.
*/
hline( "</BODY>" );
www_end( );
/*If the file appears NOT to be an HTML file, then wrap it in
*some appropriate HTML.
*/
} else if ( !extension( fname, "html" ) ) {
/* Start response.
*/
www_begin( T_HTML );
sprintf( title, "StarZ: %s", sname );
www_head( title );
hline( "<BODY>" );
/* Only the first file of a multiple-file display request is piped back.
*/
if ( nf > 0 ) {
hline( "<B>Only first entry in list will be displayed.</B>" );
}
/* Wrap the file in preformatted HTML style.
*/
hline( "<PRE>" );
/* Flush stdout prior to calling cat to display file.
*/
fflush( stdout );
/* Build command line to display the file.
*/
sprintf( thecommand, "cat %s\0x00", fname );
/* Pipe out file.
*/
if ( system ( thecommand ) ) {
printf( "Could not access file %s%c", fname, NEW_LINE );
}
/* End of wrap.
*/
hline( "</PRE>" );
/* Attach an address field.
*/
www_address( AUTHOR_NAME, AUTHOR_URL, NULL );
/* End response.
*/
hline( "</BODY>" );
www_end( );
/*Other case is a file we expect to be in HTML format.
*/
} else if ( extension( fname, "html" ) ) {
/* Start response.
*/
printf( "Content-type: text/html%c%c", NEW_LINE, NEW_LINE );
/* Only the first file of a multiple-file display request is piped back.
*/
if ( nf > 0 ) {
printf( "<B>Only first entry in list will be displayed.</B>%c",
NEW_LINE );
}
/* Flush stdout prior to calling cat
*/
fflush( stdout );
/* Pipe out HTML file.
*/
sprintf( thecommand, "cat %s\0x00", fname );
if ( system ( thecommand ) ) {
printf( "Could not access file %s%c", fname, NEW_LINE );
}
}
/*Done.
*/
return;
}
/*****************************************************************************/
int extension(
/*+
* Name:
* extension
* Purpose:
* Compare the last part of a file specifier with a supplied extension.
* Language:
* C
* Invocation:
* extension( fname, fext )
* Arguments:
* fname = char * (Given)
* Pointer to name of the file to be checked.
* fext = char * (Given)
* Pointer to the extension to be tested for.
* Returned:
* Returns the result of a strcmp( ) call comparing the relevant
* part of the supplied text with the supplied extension.
* Authors:
* MJC: Martin Clayton (Starlink)
* {enter_new_authors_here}
* History:
* 14-DEC-1994 (MJC):
* Original Version.
* {enter_further_changes_here}
* Bugs:
* {note_any_bugs_here}
*-
* Arguments Given:
*/
char *fname, /* The file specification. */
char *fext /* A file extension, without ".". */
)
{
/*
* Local Variables:
*/
char *cptr;
/*.
*/
/*Find end of file name.
*/
cptr = fname;
while ( *cptr ) {
cptr++;
}
if ( cptr > fname ) {
cptr--;
}
/*Find last . in file name and point to character after it.
*/
while ( ( *cptr != '.' ) && ( cptr > fname ) ) {
cptr--;
}
if ( cptr > fname ) {
cptr++;
}
/*Compare supplied extension with part of file name.
*/
return ( !strcmp( cptr, fext) );
}
/*****************************************************************************/
void tar_zed(
/*+
* Name:
* tar_zed
* Purpose:
* Pipe a compressed tar archive of the requested files to stdout.
* Language:
* C
* Invocation:
* tar_zed( ent, n )
* Arguments:
* ent = entry * (Given)
* Pointer to first item in a list of "entry" structures.
* n = int (Given)
* Total number of items in list.
* Authors:
* MJC: Martin Clayton (Starlink)
* {enter_new_authors_here}
* History:
* 14-DEC-1994 (MJC):
* Original Version.
* {enter_further_changes_here}
* Bugs:
* {note_any_bugs_here}
*-
* Arguments Given:
*/
entry *ent, /* Pointer to start of list. */
int n /* Number of items in list. */
)
{
/*
* Local Variables:
*/
char request_text[65536]; /* Buffer for command line. */
int i; /* Loop index. */
/*.
*/
/*A null request was received, return message.
*/
if ( n < 1 ) {
printf( "Content-type: text/html%c%c", NEW_LINE, NEW_LINE );
printf( "<B>Compressed tar file down-load request detected.</B><P>%c",
NEW_LINE );
printf( "<B>Your request was empty!</B>%c", NEW_LINE );
/*Otherwise build command line to compress tar file to stdout.
*/
} else {
/* ent[].val is the same foe all entries, the base directory for
* the archive build. Mark a "change directory" command for this.
*/
strcpy( request_text, "cd " );
strcat( request_text, ent[1].val );
/* Add tar command with flags for pipe to stdout and follow soft links.
*/
strcat( request_text, " ; tar -cfh - " );
/* Now add all the list entries.
*/
for ( i = 1; i <= n; i++ ) {
strcat( request_text, ent[i].name );
strcat( request_text, " " );
}
/* Finally, the pipe to compress with flag to send output to stdout.
*/
strcat( request_text, " | compress -c" );
/* Start transfer.
*/
printf( "Content-Encoding: x-compress%c%c", NEW_LINE, NEW_LINE );
/* Flush output.
*/
fflush ( stdout );
/* Execute compress archive command line.
*/
if ( system( request_text ) ) {
printf( "Content-type: text/html%c%c", NEW_LINE, NEW_LINE );
printf( "<B>Transfer failed.</B><P>%c", NEW_LINE );
}
}
/*Done.
*/
return;
}
/*End-of-file.
*/
syntax highlighted by Code2HTML, v. 0.9.1