Scripts

When a directory listing is generated through a script, the server gathers the information related to the files inside the listed directory and packs it in a special string. Then the script is executed and the special string is POSTed to it inside a variable called files. The server also POSTs the directory virtual path in a variable which name is path as well as an URL encoded (escaped) version of the virtual path in a variable named encoded_path.

This special string passed in the files variable is of the form:

name1url1size1date1MIME-type1[CR]
name2url2size2date2MIME-type2[CR]
.... 
.... 
namen-1urln-1sizen-1daten-1MIME-typen-1[CR]
namenurlnsizendatenMIME-typen

→ represents the tabulation character (ASCII code 9 - \t) and [CR] represents the carriage return character (ASCII code 13 - \n). The tuple (namei, urli, sizei, datei, MIME-typei) is the information related to the ith file in the listing.

The name and the URL of every file are UTF-8 encoded. The size is in bytes. The date is conforming to the format YYYY-MM-DD hh:mm:ss which is equivalent in strftime semantics to %Y-%m-%d %H:%M:%S.

The MIME type of a directory is empty, i.e. it is equal to the empty string. The name of a directory contains always a trailing slash.

The script should split the files variable value on the carriage return characters then split each line on the tabulation character to have the information related to each file.

Example 8-2. A very simple directory listing script (PHP version)

   <HTML>
     <HEAD>
       <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf-8">
       <TITLE>
           Index of <?php echo $_POST['path']; ?>
       </TITLE>
     </HEAD>
   
     <BODY>
   
       <TABLE BORDER="0">
   
         <TR>
           <TD>Name</TD>
           <TD>Size</TD>
           <TD>Date</TD>
           <TD>MIME Type</TD>
         </TR>
   
         <?php
   
           /* Split and get the lines */
           $lines = explode("\n", $_POST['files']);
   
           /* For each line do... */
           foreach ($lines as $line)
           {
             /* Split the line and get the file information */
             list($name, $url, $size, $date, $mimetype) = explode("\t", $line);
   	  
             if ($mimetype == "")
               $mimetype = "Directory";
   
             echo "<TR><TD><A HREF=\"$url\">" . htmlentities($name) .
                       "</A></TD><TD>$size</TD><TD>$date</TD><TD>$mimetype</TD></TR>";
           }
        ?>
   
        </TABLE>
      </BODY>
   </HTML>

Example 8-3. A very simple directory listing script (Python 3 version with the legacy cgi package)

   # This script makes use of the cgi package which was available by default
   # until Python 3.12.
   # If using Python 3.13 or later, you'll need to install the legacy-cgi 
   # package from https://pypi.org/project/legacy-cgi/ or with the command:
   #     python3 -m pip install legacy-cgi
   # Alternatively, check the next example for a way to write an equivalent
   # script without using the cgi package.
   
   import cgi, html
   
   posted_data = cgi.FieldStorage()
   
   # Write the CGI header
   print ('Content-Type: text/html; charset=utf-8')
   print ()
   
   print ('<HTML><HEAD>')
   print ('<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf-8">')
   print ('<TITLE>Index of %s</TITLE></HEAD>' % posted_data['path'].value)
   
   print ('<TABLE><TR><TD>Name</TD><TD>Size</TD><TD>Date</TD><TD>MIME Type</TD></TR>')
   
   # Split and get the lines
   lines = (posted_data['files'].value).split('\n')
   
   # for each line do...
   for line in lines:
   	# Split the line and get the file information
   	(name, url, size, date, mimetype) = line.split('\t')
   
   	if (mimetype == ''):
   		mimetype = 'Directory'
   
   	print ('<TR><TD><A HREF="%s">%s</A></TD>' % ( url, html.escape(name) ))
   	print ('<TD>%d</TD><TD>%s</TD><TD>%s</TD></TR>' % (int(size), date, mimetype))
   
   print ('</TABLE></BODY></HTML>')

Example 8-4. A very simple directory listing script (Python 3 version with no legacy or deprecated packages)

   # Alternative version which works with the default
   # wsgiref.handlers.CGIHandler module and requires
   # no additional packages.
   
   from wsgiref.handlers import CGIHandler
   from urllib.parse import parse_qs
   import html
   
   def app(environ, start_response):
   	# the environment variable CONTENT_LENGTH may be empty or missing
   	try:
   		request_body_size = int(environ.get('CONTENT_LENGTH', 0))
   	except (ValueError):
   		request_body_size = 0
   
   	# When the method is POST, the variables are sent
   	# in the HTTP request body which is passed by the WSGI server
   	# in a file inside the wsgi.input environment variable.
   	request_body = environ['wsgi.input'].read(request_body_size).decode('utf-8')
   	d = parse_qs(request_body)
   
   	path = d.get('path', [''])[0] # Returns the first path value.
   	files = d.get('files', [''])[0] # Returns the first files value.
   
   	response_body = [ '<HTML><HEAD>' ]
   	response_body.append('<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf-8">')
   	response_body.append('<TITLE>Index of %s</TITLE></HEAD>' % path)
   
   	response_body.append('<TABLE><TR><TD>Name</TD><TD>Size</TD><TD>Date</TD><TD>MIME Type</TD></TR>')
   
   	# Split and get the lines
   	lines = files.split('\n')
   
   	# for each line do...
   	for line in lines:
   		# Split the line and get the file information
   		(name, url, size, date, mimetype) = line.split('\t')
   
   		if (mimetype == ''):
   			mimetype = 'Directory'
   
   		response_body.append('<TR><TD><A HREF="%s">%s</A></TD>' % ( url, html.escape(name) ))
   		response_body.append('<TD>%d</TD><TD>%s</TD><TD>%s</TD></TR>' % (int(size), date, mimetype))
   
   	response_body.append('</TABLE></BODY></HTML>')
   
   	status = '200 OK'
   
   	response_headers = [
   		('Content-Type', 'text/html; charset=utf-8')
   	]
   
   	start_response(status, response_headers)
   	return ( r.encode('utf-8') for r in response_body )
   
   CGIHandler().run(app)