| [ Index ] |
PHP Cross Reference of Drupal 6 (gatewave) |
[Summary view] [Print] [Text view]
1 <?php 2 3 // $Id: project-release-private-download.php,v 1.3 2009/08/07 05:28:23 dww Exp $ 4 5 /** 6 * @file 7 * Script to serve private file downloads for project releases. 8 * 9 * MOTIVATION: 10 * 11 * This script is intended for sites that use public file access by default 12 * (which is much better for regular performance) but wish to restrict access 13 * to file downloads for their release nodes, and which generates the files 14 * associated with release nodes (see package-release-nodes.php) instead of 15 * allowing users to upload files and attach them directly. 16 * 17 * INSTRUCTIONS: 18 * 19 * 1. (Optional) See if your web server supports the 'X-Sendfile' header. In 20 * Apache, you need mod_xsendfile (http://tn123.ath.cx/mod_xsendfile). If 21 * your server supports this header, set the 'USE_XSENDFILE' setting below to 22 * TRUE and the file downloads will be significantly faster and use less RAM. 23 * 24 * 2. Configure package-release-nodes.php to generate files into a directory 25 * outside of your site's webroot (via the "$dest_root" variable). For 26 * example: 27 * 28 * $dest_root = '/home/package-system'; 29 * $dest_rel = 'releases'; 30 * 31 * That will generate files like: /home/package-system/releases/foo-1.0.tar.gz 32 * 33 * 3. Set the "Downlod link base URL" setting (which can be found at 34 * http://example.com/admin/project/project-release-settings) to point to an 35 * unused location (for example "download/"). That will prevent links 36 * pointing into the public "files" directory, and will cause the URLs to 37 * download the files attached to release nodes to look something like: 38 * http://example.com/download/releases/foo-1.0.tar.gz 39 * 40 * 4. Create a directory in your web root with the same name specifed above 41 * (e.g. "download") and place a copy of this script in that directory. In 42 * the same directory, you should put a .htaccess file with the following: 43 * 44 * # Begin .htaccess 45 * DirectoryIndex project-release-private-download.php 46 * <IfModule mod_rewrite.c> 47 * RewriteEngine on 48 * RewriteRule ^(.*)$ project-release-private-download.php?q=$1 [L,QSA] 49 * </IfModule> 50 * <IfModule mod_xsendfile.c> 51 * XSendFile on 52 * XSendFileAllowAbove on 53 * </IfModule> 54 * # End .htaccess 55 * 56 * 5. Configure the FILE_ROOT, DRUPAL_ROOT and SITE_NAME constants below. 57 * - FILE_ROOT should point to whatever you set $dest_root in step 1. 58 * - DRUPAL_ROOT should point to the web root for your site. 59 * - SITE_NAME should match the name of your site (e.g. the 'xxx' part of 60 * where your 'sites/xxx/settings.php' file lives). 61 * - Don't forget to set USE_XSENDFILE to TRUE if your server supports it. 62 * 63 * 6. Start creating release nodes and running package-release-nodes.php 64 * 65 * 7. Enjoy your private downloads! 66 * 67 * 68 * @author Derek Wright (http://drupal.org/user/46549) 69 */ 70 71 72 /** 73 * Required configuration: directory tree where the real files live. 74 */ 75 define('FILE_ROOT', ''); 76 77 /** 78 * Required configuration: location of your Drupal installation. 79 */ 80 define('DRUPAL_ROOT', ''); 81 82 /** 83 * Required configuration: name of your site. 84 * 85 * Needed to find the right settings.php file to bootstrap Drupal with. 86 */ 87 define('SITE_NAME', ''); 88 89 /** 90 * Optional configuration: does your server support the X-Sendfile header? 91 */ 92 define('USE_XSENDFILE', FALSE); 93 94 95 /* 96 * -------------------------------------------------- 97 * Real work begins, nothing to configure below this. 98 * -------------------------------------------------- 99 */ 100 101 // Need to be inside the Drupal webroot to properly bootstrap. 102 if (!chdir(DRUPAL_ROOT)) { 103 exit(1); 104 } 105 106 // Setup variables for bootstrap. 107 $script_name = $argv[0]; 108 $_SERVER['HTTP_HOST'] = SITE_NAME; 109 $_SERVER['REMOTE_ADDR'] = '127.0.0.1'; 110 $_SERVER['REQUEST_URI'] = '/' . $script_name; 111 $_SERVER['SCRIPT_NAME'] = '/' . $script_name; 112 $_SERVER['PHP_SELF'] = '/' . $script_name; 113 $_SERVER['SCRIPT_FILENAME'] = $_SERVER['PWD'] .'/'. $script_name; 114 $_SERVER['PATH_TRANSLATED'] = $_SERVER['SCRIPT_FILENAME']; 115 116 // Actually do the bootstrap. Since we're relying on db_rewrite_sql() to 117 // enforce the access checks on the release node, and since that invokes a 118 // hook, we need a full bootstrap here, not just DRUPAL_BOOTSTRAP_DATABASE. 119 include_once './includes/bootstrap.inc'; 120 drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL); 121 122 // Make sure we have the path argument for the file to download. 123 $path = $_GET['q']; 124 if (empty($path)) { 125 drupal_not_found(); 126 exit(1); 127 } 128 129 // Figure out the filename for the release history we want to serve, and make 130 // sure that file actually exists in our directory. 131 $full_path = FILE_ROOT . '/' . $path; 132 if (!is_file($full_path)) { 133 drupal_not_found(); 134 exit(1); 135 } 136 137 // Find the release this file is associated with. Due to the db_rewrite_sql(), 138 // this will enforce node access checks for us, so a user without permission 139 // to view the given file will be denied. Since we're testing if there's a 140 // node with a file that uses the given path as its filepath, this protects us 141 // against prying eyes that might try to access "/../../../etc/passwd" or 142 // something. Even if they managed to find a file that actually exists that 143 // way, it's not going to match a valid release node. So they're going to get 144 // a 403, not the file they're trying to steal. 145 $nid = db_result(db_query(db_rewrite_sql("SELECT n.nid FROM {node} n INNER JOIN {project_release_file} prf ON n.nid = prf.nid INNER JOIN {files} f ON prf.fid = f.fid WHERE n.status = 1 AND f.filepath = '%s'"), $path)); 146 if (empty($nid)) { 147 drupal_access_denied(); 148 exit(1); 149 } 150 151 // If we found the release, serve up the file. 152 $stat = stat($full_path); 153 $file_size = $stat[7]; 154 $file_mtime = $stat[9]; 155 header('Expires: 0'); 156 header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); 157 header('Last-Modified: '. gmdate('D, d M Y H:i:s', $file_mtime) .' GMT'); 158 header('Content-Type: application/octet-stream'); 159 header('Content-Description: File Transfer'); 160 header('Content-Disposition: attachment; filename="' . basename($path) . '"'); 161 header('Content-Length: ' . $file_size); 162 163 if (USE_XSENDFILE) { 164 // Faster and better. 165 header('X-Sendfile: ' . $full_path); 166 } 167 else { 168 // More portable. 169 flush(); 170 readfile($full_path); 171 } 172 173 // If we got this far, invoke our hook to let modules know this happened. 174 global $user; 175 module_invoke_all('project_release_download', $nid, $path, $user->uid); 176
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Thu Mar 24 11:18:33 2011 | Cross-referenced by PHPXref 0.7 |