[ Index ]

PHP Cross Reference of phpBB 3.0 Beta 3

title

Body

[close]

/ -> download.php (source)

   1  <?php
   2  /** 
   3  *
   4  * @package phpBB3
   5  * @version $Id: download.php,v 1.46 2006/11/12 15:35:42 acydburn Exp $
   6  * @copyright (c) 2005 phpBB Group 
   7  * @license http://opensource.org/licenses/gpl-license.php GNU Public License 
   8  *
   9  */
  10  
  11  /**
  12  * @ignore
  13  */
  14  define('IN_PHPBB', true);
  15  $phpbb_root_path = './';
  16  $phpEx = substr(strrchr(__FILE__, '.'), 1);
  17  include($phpbb_root_path . 'common.' . $phpEx);
  18  
  19  $download_id = request_var('id', 0);
  20  $thumbnail = request_var('t', false);
  21  
  22  // Start session management, do not update session page.
  23  $user->session_begin(false);
  24  $auth->acl($user->data);
  25  $user->setup('viewtopic');
  26  
  27  if (!$download_id)
  28  {
  29      trigger_error('NO_ATTACHMENT_SELECTED');
  30  }
  31  
  32  if (!$config['allow_attachments'] && !$config['allow_pm_attach'])
  33  {
  34      trigger_error('ATTACHMENT_FUNCTIONALITY_DISABLED');
  35  }
  36  
  37  $sql = 'SELECT attach_id, in_message, post_msg_id, extension, is_orphan, poster_id
  38      FROM ' . ATTACHMENTS_TABLE . "
  39      WHERE attach_id = $download_id";
  40  $result = $db->sql_query_limit($sql, 1);
  41  $attachment = $db->sql_fetchrow($result);
  42  $db->sql_freeresult($result);
  43  
  44  if (!$attachment)
  45  {
  46      trigger_error('ERROR_NO_ATTACHMENT');
  47  }
  48  
  49  if ((!$attachment['in_message'] && !$config['allow_attachments']) || ($attachment['in_message'] && !$config['allow_pm_attach']))
  50  {
  51      trigger_error('ATTACHMENT_FUNCTIONALITY_DISABLED');
  52  }
  53  
  54  $row = array();
  55  
  56  if ($attachment['is_orphan'])
  57  {
  58      // We allow admins having attachment permissions to see orphan attachments...
  59      $own_attachment = ($auth->acl_get('a_attach') || $attachment['poster_id'] == $user->data['user_id']) ? true : false;
  60  
  61      if (!$own_attachment || ($attachment['in_message'] && !$auth->acl_get('u_pm_download')) || (!$attachment['in_message'] && !$auth->acl_get('u_download')))
  62      {
  63          trigger_error('ERROR_NO_ATTACHMENT');
  64      }
  65  
  66      $extensions = $cache->obtain_attach_extensions();
  67  }
  68  else
  69  {
  70      if (!$attachment['in_message'])
  71      {
  72          // 
  73          $sql = 'SELECT p.forum_id, f.forum_password, f.parent_id
  74              FROM ' . POSTS_TABLE . ' p, ' . FORUMS_TABLE . ' f
  75              WHERE p.post_id = ' . $attachment['post_msg_id'] . '
  76                  AND p.forum_id = f.forum_id';
  77          $result = $db->sql_query_limit($sql, 1);
  78          $row = $db->sql_fetchrow($result);
  79          $db->sql_freeresult($result);
  80  
  81          // Global announcement?
  82          if (!$row)
  83          {
  84              $forum_id = request_var('f', 0);
  85  
  86              $sql = 'SELECT forum_id, forum_password, parent_id
  87                  FROM ' . FORUMS_TABLE . '
  88                  WHERE forum_id = ' . $forum_id;
  89              $result = $db->sql_query($sql);
  90              $row = $db->sql_fetchrow($result);
  91              $db->sql_freeresult($result);
  92          }
  93  
  94          if ($auth->acl_get('u_download') && $auth->acl_get('f_download', $row['forum_id']))
  95          {
  96              if ($row['forum_password'])
  97              {
  98                  // Do something else ... ?
  99                  login_forum_box($row);
 100              }
 101          }
 102          else
 103          {
 104              trigger_error('SORRY_AUTH_VIEW_ATTACH');
 105          }
 106      }
 107      else
 108      {
 109          $row['forum_id'] = 0;
 110          if (!$auth->acl_get('u_pm_download'))
 111          {
 112              trigger_error('SORRY_AUTH_VIEW_ATTACH');
 113          }
 114      }
 115  
 116      // disallowed ?
 117      $extensions = array();
 118      if (!extension_allowed($row['forum_id'], $attachment['extension'], $extensions))
 119      {
 120          trigger_error(sprintf($user->lang['EXTENSION_DISABLED_AFTER_POSTING'], $attachment['extension']));
 121      }
 122  }
 123  
 124  if (!download_allowed())
 125  {
 126      trigger_error($user->lang['LINKAGE_FORBIDDEN']);
 127  }
 128  
 129  $download_mode = (int) $extensions[$attachment['extension']]['download_mode'];
 130  
 131  // Fetching filename here to prevent sniffing of filename
 132  $sql = 'SELECT attach_id, is_orphan, in_message, post_msg_id, extension, physical_filename, real_filename, mimetype
 133      FROM ' . ATTACHMENTS_TABLE . "
 134      WHERE attach_id = $download_id";
 135  $result = $db->sql_query_limit($sql, 1);
 136  $attachment = $db->sql_fetchrow($result);
 137  $db->sql_freeresult($result);
 138  
 139  if (!$attachment)
 140  {
 141      trigger_error('ERROR_NO_ATTACHMENT');
 142  }
 143  
 144  $attachment['physical_filename'] = basename($attachment['physical_filename']);
 145  $display_cat = $extensions[$attachment['extension']]['display_cat'];
 146  
 147  if ($thumbnail)
 148  {
 149      $attachment['physical_filename'] = 'thumb_' . $attachment['physical_filename'];
 150  }
 151  else if (($display_cat == ATTACHMENT_CATEGORY_NONE || $display_cat == ATTACHMENT_CATEGORY_IMAGE) && !$attachment['is_orphan'])
 152  {
 153      // Update download count
 154      $sql = 'UPDATE ' . ATTACHMENTS_TABLE . ' 
 155          SET download_count = download_count + 1 
 156          WHERE attach_id = ' . $attachment['attach_id'];
 157      $db->sql_query($sql);
 158  }
 159  
 160  // Determine the 'presenting'-method
 161  if ($download_mode == PHYSICAL_LINK)
 162  {
 163      if (!@is_dir($phpbb_root_path . $config['upload_path']))
 164      {
 165          trigger_error($user->lang['PHYSICAL_DOWNLOAD_NOT_POSSIBLE']);
 166      }
 167  
 168      redirect($phpbb_root_path . $config['upload_path'] . '/' . $attachment['physical_filename']);
 169      exit;
 170  }
 171  else
 172  {
 173      send_file_to_browser($attachment, $config['upload_path'], $extensions[$attachment['extension']]['display_cat']);
 174      exit;
 175  }
 176  
 177  
 178  /**
 179  * Send file to browser
 180  */
 181  function send_file_to_browser($attachment, $upload_dir, $category)
 182  {
 183      global $user, $db, $config, $phpbb_root_path;
 184  
 185      $filename = $phpbb_root_path . $upload_dir . '/' . $attachment['physical_filename'];
 186  
 187      if (!@file_exists($filename))
 188      {
 189          trigger_error($user->lang['ERROR_NO_ATTACHMENT'] . '<br /><br />' . sprintf($user->lang['FILE_NOT_FOUND_404'], $filename));
 190      }
 191  
 192      // Correct the mime type - we force application/octetstream for all files, except images
 193      // Please do not change this, it is a security precaution
 194      if (strpos($attachment['mimetype'], 'image') !== 0)
 195      {
 196          $attachment['mimetype'] = (strpos(strtolower($user->browser), 'msie') !== false || strpos(strtolower($user->browser), 'opera') !== false) ? 'application/octetstream' : 'application/octet-stream';
 197      }
 198  
 199      if (@ob_get_length())
 200      {
 201          @ob_end_clean();
 202      }
 203  
 204      // Now send the File Contents to the Browser
 205      $size = @filesize($filename);
 206  
 207      // To correctly display further errors we need to make sure we are using the correct headers for both (unsetting content-length may not work)
 208  
 209      // Check if headers already sent or not able to get the file contents.
 210      if (headers_sent() || !@file_exists($filename) || !@is_readable($filename))
 211      {
 212          // PHP track_errors setting On?
 213          if (!empty($php_errormsg))
 214          {
 215              trigger_error($user->lang['UNABLE_TO_DELIVER_FILE'] . '<br />' . sprintf($user->lang['TRACKED_PHP_ERROR'], $php_errormsg));
 216          }
 217  
 218          trigger_error('UNABLE_TO_DELIVER_FILE');
 219      }
 220  
 221      // Now the tricky part... let's dance
 222      header('Pragma: public');
 223  
 224      /**
 225      * Commented out X-Sendfile support. To not expose the physical filename within the header if xsendfile is absent we need to look into methods of checking it's status.
 226      *
 227      * Try X-Sendfile since it is much more server friendly - only works if the path is *not* outside of the root path...
 228      * lighttpd has core support for it. An apache2 module is available at http://celebnamer.celebworld.ws/stuff/mod_xsendfile/
 229      *
 230      * Not really ideal, but should work fine...
 231      * <code>
 232      *    if (strpos($upload_dir, '/') !== 0 && strpos($upload_dir, '../') === false)
 233      *    {
 234      *        header('X-Sendfile: ' . $filename);
 235      *    }
 236      * </code>
 237      */
 238  
 239      // Send out the Headers. Do not set Content-Disposition to inline please, it is a security measure for users using the Internet Explorer.
 240      header('Content-Type: ' . $attachment['mimetype']);
 241      header('Content-Disposition: ' . ((strpos($attachment['mimetype'], 'image') === 0) ? 'inline' : 'attachment') . '; ' . header_filename($attachment['real_filename']));
 242  
 243      if ($size)
 244      {
 245          header("Content-Length: $size");
 246      }
 247  
 248      // Might not be ideal to store the contents, but file_get_contents is binary-safe as well as the recommended method
 249      echo @file_get_contents($filename);
 250  
 251      flush();
 252      exit;
 253  }
 254  
 255  /*
 256  * Get a browser friendly UTF-8 encoded filename
 257  */
 258  function header_filename($file)
 259  {
 260      // There be dragons here...
 261      // IE follows no RFC, follow the RFC for extended filename for the rest
 262      if (strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE') !== false)
 263      {
 264          return "filename=" . rawurlencode($file);
 265      }
 266      else
 267      {
 268          return "filename*=UTF-8''" . rawurlencode($file);
 269      }
 270  }
 271  
 272  /**
 273  * Check if downloading item is allowed
 274  */
 275  function download_allowed()
 276  {
 277      global $config, $user, $db;
 278  
 279      if (!$config['secure_downloads'])
 280      {
 281          return true;
 282      }
 283  
 284      $url = (!empty($_SERVER['HTTP_REFERER'])) ? trim($_SERVER['HTTP_REFERER']) : trim(getenv('HTTP_REFERER'));
 285  
 286      if (!$url)
 287      {
 288          return ($config['secure_allow_empty_referer']) ? true : false;
 289      }
 290  
 291      // Split URL into domain and script part
 292      $url = @parse_url($url);
 293  
 294      if ($url === false)
 295      {
 296          return ($config['secure_allow_empty_referer']) ? true : false;
 297      }
 298  
 299      $hostname = $url['host'];
 300      unset($url);
 301  
 302      $allowed = ($config['secure_allow_deny']) ? false : true;
 303      $iplist = array();
 304  
 305      if (($ip_ary = @gethostbynamel($hostname)) !== false)
 306      {
 307          foreach ($ip_ary as $ip)
 308          {
 309              if ($ip)
 310              {
 311                  $iplist[] = $ip;
 312              }
 313          }
 314      }
 315      
 316      // Check for own server...
 317      $server_name = (!empty($_SERVER['SERVER_NAME'])) ? $_SERVER['SERVER_NAME'] : getenv('SERVER_NAME');
 318  
 319      // Forcing server vars is the only way to specify/override the protocol
 320      if ($config['force_server_vars'] || !$server_name)
 321      {
 322          $server_name = $config['server_name'];
 323      }
 324  
 325      if (preg_match('#^.*?' . preg_quote($server_name, '#') . '.*?$#i', $hostname))
 326      {
 327          $allowed = true;
 328      }
 329      
 330      // Get IP's and Hostnames
 331      if (!$allowed)
 332      {
 333          $sql = 'SELECT site_ip, site_hostname, ip_exclude
 334              FROM ' . SITELIST_TABLE;
 335          $result = $db->sql_query($sql);
 336  
 337          while ($row = $db->sql_fetchrow($result))
 338          {
 339              $site_ip = trim($row['site_ip']);
 340              $site_hostname = trim($row['site_hostname']);
 341  
 342              if ($site_ip)
 343              {
 344                  foreach ($iplist as $ip)
 345                  {
 346                      if (preg_match('#^' . str_replace('*', '.*?', preg_quote($site_ip, '#')) . '$#i', $ip))
 347                      {
 348                          if ($row['ip_exclude'])
 349                          {
 350                              $allowed = ($config['secure_allow_deny']) ? false : true;
 351                              break 2;
 352                          }
 353                          else
 354                          {
 355                              $allowed = ($config['secure_allow_deny']) ? true : false;
 356                          }
 357                      }
 358                  }
 359              }
 360  
 361              if ($site_hostname)
 362              {
 363                  if (preg_match('#^' . str_replace('*', '.*?', preg_quote($site_hostname, '#')) . '$#i', $hostname))
 364                  {
 365                      if ($row['ip_exclude'])
 366                      {
 367                          $allowed = ($config['secure_allow_deny']) ? false : true;
 368                          break;
 369                      }
 370                      else
 371                      {
 372                          $allowed = ($config['secure_allow_deny']) ? true : false;
 373                      }
 374                  }
 375              }
 376          }
 377          $db->sql_freeresult($result);
 378      }
 379      
 380      return $allowed;
 381  }
 382  
 383  ?>


Generated: Wed Nov 22 00:35:05 2006 Cross-referenced by PHPXref 0.6