Book HomePHP CookbookSearch this book

19.13. Program: Web Server Directory Listing

The web-ls.php program shown in Example 19-4 provides a view of the files inside your web server's document root, formatted like the output of the Unix command ls. Filenames are linked so that you can download each file, and directory names are linked so that you can browse in each directory, as shown in Figure 19-1.

Figure 19-1

Figure 19-1. Web listing

Most lines in Example 19-4 are devoted to building an easy-to-read representation of the file's permissions, but the guts of the program are in the while loop at the end. The $d->read( ) method gets the name of each file in the directory. Then, lstat( ) retrieves information about that file, and printf( ) prints out the formatted information about that file.

The mode_string( ) functions and the constants it uses turn the octal representation of a file's mode (e.g., 35316) into an easier-to-read string (e.g., -rwsrw-r--).

Example 19-4. web-ls.php

/* Bit masks for determining file permissions and type. The names and values
 * listed below are POSIX-compliant, individual systems may have their own 
 * extensions.
 */

define('S_IFMT',0170000);   // mask for all types 
define('S_IFSOCK',0140000); // type: socket 
define('S_IFLNK',0120000);  // type: symbolic link 
define('S_IFREG',0100000);  // type: regular file 
define('S_IFBLK',0060000);  // type: block device 
define('S_IFDIR',0040000);  // type: directory 
define('S_IFCHR',0020000);  // type: character device 
define('S_IFIFO',0010000);  // type: fifo 
define('S_ISUID',0004000);  // set-uid bit 
define('S_ISGID',0002000);  // set-gid bit 
define('S_ISVTX',0001000);  // sticky bit 
define('S_IRWXU',00700);    // mask for owner permissions 
define('S_IRUSR',00400);    // owner: read permission 
define('S_IWUSR',00200);    // owner: write permission 
define('S_IXUSR',00100);    // owner: execute permission 
define('S_IRWXG',00070);    // mask for group permissions 
define('S_IRGRP',00040);    // group: read permission 
define('S_IWGRP',00020);    // group: write permission 
define('S_IXGRP',00010);    // group: execute permission 
define('S_IRWXO',00007);    // mask for others permissions 
define('S_IROTH',00004);    // others: read permission 
define('S_IWOTH',00002);    // others: write permission 
define('S_IXOTH',00001);    // others: execute permission 

/* mode_string() is a helper function that takes an octal mode and returns
 * a ten character string representing the file type and permissions that
 * correspond to the octal mode. This is a PHP version of the mode_string()
 * function in the GNU fileutils package.
 */
function mode_string($mode) {
  $s = array();

 // set type letter 
  if (($mode & S_IFMT) == S_IFBLK) {
    $s[0] = 'b';
  } elseif (($mode & S_IFMT) == S_IFCHR) {
    $s[0] = 'c';
  } elseif (($mode & S_IFMT) == S_IFDIR) {
    $s[0] = 'd';
  } elseif (($mode & S_IFMT) ==  S_IFREG) {
    $s[0] = '-';
  } elseif (($mode & S_IFMT) ==  S_IFIFO) {
    $s[0] = 'p';
  } elseif (($mode & S_IFMT) == S_IFLNK) {
    $s[0] = 'l';
  } elseif (($mode & S_IFMT) == S_IFSOCK) {
    $s[0] = 's';
  }

  // set user permissions 
  $s[1] = $mode & S_IRUSR ? 'r' : '-';
  $s[2] = $mode & S_IWUSR ? 'w' : '-';
  $s[3] = $mode & S_IXUSR ? 'x' : '-';

  // set group permissions 
  $s[4] = $mode & S_IRGRP ? 'r' : '-';
  $s[5] = $mode & S_IWGRP ? 'w' : '-';
  $s[6] = $mode & S_IXGRP ? 'x' : '-';

  // set other permissions 
  $s[7] = $mode & S_IROTH ? 'r' : '-';
  $s[8] = $mode & S_IWOTH ? 'w' : '-';
  $s[9] = $mode & S_IXOTH ? 'x' : '-';

  // adjust execute letters for set-uid, set-gid, and sticky 
  if ($mode & S_ISUID) {
    if ($s[3] != 'x') {
      // set-uid but not executable by owner 
      $s[3] = 'S';
    } else {
      $s[3] = 's';
    }
  }

  if ($mode & S_ISGID) {
    if ($s[6] != 'x') {
      // set-gid but not executable by group 
      $s[6] = 'S';
    } else {
      $s[6] = 's';
    }
  }

  if ($mode & S_ISVTX) {
    if ($s[9] != 'x') {
      // sticky but not executable by others 
      $s[9] = 'T';
    } else {
      $s[9] = 't';
    }
  }

  // return formatted string 
  return join('',$s);

}

// Start at the document root if not specified
if (isset($_REQUEST['dir'])) {
    $dir = $_REQUEST['dir'];
} else {
    $dir = '';
}

// locate $dir in the filesystem
$real_dir = realpath($_SERVER['DOCUMENT_ROOT'].$dir);

// make sure $real_dir is inside document root
if (! preg_match('/^'.preg_quote($_SERVER['DOCUMENT_ROOT'],'/').'/',
                 $real_dir)) {
    die("$dir is not inside the document root");
}

// canonicalize $dir by removing the document root from its beginning 
$dir = substr_replace($real_dir,'',0,strlen($_SERVER['DOCUMENT_ROOT']));

// are we opening a directory?
if (! is_dir($real_dir)) {
    die("$real_dir is not a directory");
}

// open the specified directory 
$d = dir($real_dir) or die("can't open $real_dir: $php_errormsg");

print '<table>';

// read each entry in the directory 
while (false !== ($f = $d->read())) {

    // get information about this file 
    $s = lstat($d->path.'/'.$f);
    
    // translate uid into user name 
    $user_info = posix_getpwuid($s['uid']);

    // translate gid into group name 
    $group_info = posix_getgrgid($s['gid']);

    // format the date for readability 
    $date = strftime('%b %e %H:%M',$s['mtime']);

    // translate the octal mode into a readable string 
    $mode = mode_string($s['mode']);

    $mode_type = substr($mode,0,1);
    if (($mode_type == 'c') || ($mode_type == 'b')) {
        /* if it's a block or character device, print out the major and
         * minor device type instead of the file size */
        $major = ($s['rdev'] >> 8) & 0xff;
        $minor = $s['rdev'] & 0xff;
        $size = sprintf('%3u, %3u',$major,$minor);
    } else {
        $size = $s['size'];
    }

    // format the <a href=""> around the filename
    // no link for the current directory
    if ('.' == $f) {
        $href = $f;
    } else {
        // don't include the ".." in the parent directory link
        if ('..' == $f) {
            $href = urlencode(dirname($dir));
        } else {
            $href = urlencode($dir) . '/' . urlencode($f);
        }
        
        /* everything but "/" should be urlencoded */
        $href = str_replace('%2F','/',$href);

        // browse other directories with web-ls
        if (is_dir(realpath($d->path . '/' . $f))) {
            $href = sprintf('<a href="%s?dir=%s">%s</a>',
                            $_SERVER['PHP_SELF'],$href,$f);
        } else {
            // link to files to download them
            $href= sprintf('<a href="%s">%s</a>',$href,$f);
        }

        // if it's a link, show the link target, too
        if ('l' == $mode_type) {
            $href .= ' -&gt; ' . readlink($d->path.'/'.$f);
        }
    }

    // print out the appropriate info for this file 
    printf('<tr><td>%s</td><td>%3u</td><td align="right">%s</td>
            <td align="right">%s</td><td align="right">%s</td>
            <td align="right">%s</td><td>%s</td></tr>',
           $mode,                // formatted mode string 
           $s['nlink'],          // number of links to this file 
           $user_info['name'],   // owner's user name 
           $group_info['name'],  // group name 
           $size,                // file size (or device numbers) 
           $date,                // last modified date and time 
           $href);               // link to browse or download 
}

print '</table>';



Library Navigation Links

Copyright © 2003 O'Reilly & Associates. All rights reserved.