/* * 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 #include #include #include #include #include #include /*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 "mjc@starlink.ucl.ac.uk" /*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

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( "" ); /*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( "Only first entry in list will be displayed.%c", NEW_LINE ); } /* Print warning that request is not in /star tree. */ if ( notvalid ) { printf( "Your request has been denied, default %s directory selected instead.%c", DEFAULT_DIR, NEW_LINE ); } /* Start HTML FORM. * URL of where script will reside. */ printf( "

%c", SCRIPT_URL, NEW_LINE ); /* Submit and reset buttons. These are duplicated later. */ printf( "%c", NEW_LINE ); printf( "

%c", NEW_LINE ); /* Browse or Get mode-select button. */ printf( " Get or Browse (default) mode
%c", full_path, NEW_LINE); /* Text input field for entry of explicit file or directory name. */ printf( " Change Directory
%c", NEW_LINE ); /* List heading. */ printf( "

Directory of %s

%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( "
%c", NEW_LINE );

/*    Current directory button.
 */
        printf( "",
                full_path);
        printf( " <-current directory->%c", NEW_LINE );

/*    Parent directory button.
 */
        if ( strcmp( full_path, "/" ) ) {
            printf( "",
                    full_path);
            printf( " <-parent directory%c", NEW_LINE );
        }

/*    Files themselves.
 */
        for ( i = 1; i <= entries; i++ ) {

/*       Start with button.
 */
           printf( "",
                   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( " " );
               printf( " %-32s", ent_tab[i] );
               printf( "%9s", "directory" );

/*       Otherwise print the size of the file.
 */
           } else {
               if ( extension( ent_tab[i], "html" ) ) {
                   printf( " " );

               } else {
                   printf( " " );
               }
               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( "
%c", NEW_LINE ); /* Output a second set of control buttons. */ printf( "%c", NEW_LINE ); printf( "%c", NEW_LINE ); /* End of form. */ printf( "
%c", NEW_LINE ); } /*End main part of response */ hrule( ); /*HTML Address field. */ www_address( AUTHOR_NAME, AUTHOR_URL, NULL ); /*End response. */ hline( "" ); 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 . * {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( "" ); /*Pointer to image. */ printf( "

%c", fname, NEW_LINE ); /* Attach an address field. */ www_address( AUTHOR_NAME, AUTHOR_URL, NULL ); /* End response. */ hline( "" ); 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( "" ); /* Only the first file of a multiple-file display request is piped back. */ if ( nf > 0 ) { hline( "Only first entry in list will be displayed." ); } /* Wrap the file in preformatted HTML style. */ hline( "

" );

/*    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( "
" ); /* Attach an address field. */ www_address( AUTHOR_NAME, AUTHOR_URL, NULL ); /* End response. */ hline( "" ); 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( "Only first entry in list will be displayed.%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( "Compressed tar file down-load request detected.

%c", NEW_LINE ); printf( "Your request was empty!%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( "Transfer failed.

%c", NEW_LINE ); } } /*Done. */ return; } /*End-of-file. */