[ Index ] |
PHP Cross Reference of phpBB-3.1.12-deutsch |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * 4 * This file is part of the phpBB Forum Software package. 5 * 6 * @copyright (c) phpBB Limited <https://www.phpbb.com> 7 * @license GNU General Public License, version 2 (GPL-2.0) 8 * 9 * For full copyright and license information, please see 10 * the docs/CREDITS.txt file. 11 * 12 */ 13 14 /** 15 * @ignore 16 */ 17 if (!defined('IN_PHPBB')) 18 { 19 exit; 20 } 21 22 /** 23 * A simplified function to deliver avatars 24 * The argument needs to be checked before calling this function. 25 */ 26 function send_avatar_to_browser($file, $browser) 27 { 28 global $config, $phpbb_root_path; 29 30 $prefix = $config['avatar_salt'] . '_'; 31 $image_dir = $config['avatar_path']; 32 33 // Adjust image_dir path (no trailing slash) 34 if (substr($image_dir, -1, 1) == '/' || substr($image_dir, -1, 1) == '\\') 35 { 36 $image_dir = substr($image_dir, 0, -1) . '/'; 37 } 38 $image_dir = str_replace(array('../', '..\\', './', '.\\'), '', $image_dir); 39 40 if ($image_dir && ($image_dir[0] == '/' || $image_dir[0] == '\\')) 41 { 42 $image_dir = ''; 43 } 44 $file_path = $phpbb_root_path . $image_dir . '/' . $prefix . $file; 45 46 if ((@file_exists($file_path) && @is_readable($file_path)) && !headers_sent()) 47 { 48 header('Cache-Control: public'); 49 50 $image_data = @getimagesize($file_path); 51 header('Content-Type: ' . image_type_to_mime_type($image_data[2])); 52 53 if ((strpos(strtolower($browser), 'msie') !== false) && !phpbb_is_greater_ie_version($browser, 7)) 54 { 55 header('Content-Disposition: attachment; ' . header_filename($file)); 56 57 if (strpos(strtolower($browser), 'msie 6.0') !== false) 58 { 59 header('Expires: ' . gmdate('D, d M Y H:i:s', time()) . ' GMT'); 60 } 61 else 62 { 63 header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 31536000) . ' GMT'); 64 } 65 } 66 else 67 { 68 header('Content-Disposition: inline; ' . header_filename($file)); 69 header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 31536000) . ' GMT'); 70 } 71 72 $size = @filesize($file_path); 73 if ($size) 74 { 75 header("Content-Length: $size"); 76 } 77 78 if (@readfile($file_path) == false) 79 { 80 $fp = @fopen($file_path, 'rb'); 81 82 if ($fp !== false) 83 { 84 while (!feof($fp)) 85 { 86 echo fread($fp, 8192); 87 } 88 fclose($fp); 89 } 90 } 91 92 flush(); 93 } 94 else 95 { 96 header('HTTP/1.0 404 Not Found'); 97 } 98 } 99 100 /** 101 * Wraps an url into a simple html page. Used to display attachments in IE. 102 * this is a workaround for now; might be moved to template system later 103 * direct any complaints to 1 Microsoft Way, Redmond 104 */ 105 function wrap_img_in_html($src, $title) 106 { 107 echo '<!DOCTYPE html>'; 108 echo '<html>'; 109 echo '<head>'; 110 echo '<meta charset="utf-8">'; 111 echo '<meta http-equiv="X-UA-Compatible" content="IE=edge">'; 112 echo '<title>' . $title . '</title>'; 113 echo '</head>'; 114 echo '<body>'; 115 echo '<div>'; 116 echo '<img src="' . $src . '" alt="' . $title . '" />'; 117 echo '</div>'; 118 echo '</body>'; 119 echo '</html>'; 120 } 121 122 /** 123 * Send file to browser 124 */ 125 function send_file_to_browser($attachment, $upload_dir, $category) 126 { 127 global $user, $db, $config, $phpbb_dispatcher, $phpbb_root_path; 128 129 $filename = $phpbb_root_path . $upload_dir . '/' . $attachment['physical_filename']; 130 131 if (!@file_exists($filename)) 132 { 133 send_status_line(404, 'Not Found'); 134 trigger_error('ERROR_NO_ATTACHMENT'); 135 } 136 137 // Correct the mime type - we force application/octetstream for all files, except images 138 // Please do not change this, it is a security precaution 139 if ($category != ATTACHMENT_CATEGORY_IMAGE || strpos($attachment['mimetype'], 'image') !== 0) 140 { 141 $attachment['mimetype'] = (strpos(strtolower($user->browser), 'msie') !== false || strpos(strtolower($user->browser), 'opera') !== false) ? 'application/octetstream' : 'application/octet-stream'; 142 } 143 144 if (@ob_get_length()) 145 { 146 @ob_end_clean(); 147 } 148 149 // Now send the File Contents to the Browser 150 $size = @filesize($filename); 151 152 /** 153 * Event to alter attachment before it is sent to browser. 154 * 155 * @event core.send_file_to_browser_before 156 * @var array attachment Attachment data 157 * @var string upload_dir Relative path of upload directory 158 * @var int category Attachment category 159 * @var string filename Path to file, including filename 160 * @var int size File size 161 * @since 3.1.11-RC1 162 */ 163 $vars = array( 164 'attachment', 165 'upload_dir', 166 'category', 167 'filename', 168 'size', 169 ); 170 extract($phpbb_dispatcher->trigger_event('core.send_file_to_browser_before', compact($vars))); 171 172 // To correctly display further errors we need to make sure we are using the correct headers for both (unsetting content-length may not work) 173 174 // Check if headers already sent or not able to get the file contents. 175 if (headers_sent() || !@file_exists($filename) || !@is_readable($filename)) 176 { 177 // PHP track_errors setting On? 178 if (!empty($php_errormsg)) 179 { 180 send_status_line(500, 'Internal Server Error'); 181 trigger_error($user->lang['UNABLE_TO_DELIVER_FILE'] . '<br />' . sprintf($user->lang['TRACKED_PHP_ERROR'], $php_errormsg)); 182 } 183 184 send_status_line(500, 'Internal Server Error'); 185 trigger_error('UNABLE_TO_DELIVER_FILE'); 186 } 187 188 // Make sure the database record for the filesize is correct 189 if ($size > 0 && $size != $attachment['filesize'] && strpos($attachment['physical_filename'], 'thumb_') === false) 190 { 191 // Update database record 192 $sql = 'UPDATE ' . ATTACHMENTS_TABLE . ' 193 SET filesize = ' . (int) $size . ' 194 WHERE attach_id = ' . (int) $attachment['attach_id']; 195 $db->sql_query($sql); 196 } 197 198 // Now the tricky part... let's dance 199 header('Cache-Control: public'); 200 201 // Send out the Headers. Do not set Content-Disposition to inline please, it is a security measure for users using the Internet Explorer. 202 header('Content-Type: ' . $attachment['mimetype']); 203 204 if (phpbb_is_greater_ie_version($user->browser, 7)) 205 { 206 header('X-Content-Type-Options: nosniff'); 207 } 208 209 if ($category == ATTACHMENT_CATEGORY_FLASH && request_var('view', 0) === 1) 210 { 211 // We use content-disposition: inline for flash files and view=1 to let it correctly play with flash player 10 - any other disposition will fail to play inline 212 header('Content-Disposition: inline'); 213 } 214 else 215 { 216 if (empty($user->browser) || ((strpos(strtolower($user->browser), 'msie') !== false) && !phpbb_is_greater_ie_version($user->browser, 7))) 217 { 218 header('Content-Disposition: attachment; ' . header_filename(htmlspecialchars_decode($attachment['real_filename']))); 219 if (empty($user->browser) || (strpos(strtolower($user->browser), 'msie 6.0') !== false)) 220 { 221 header('Expires: ' . gmdate('D, d M Y H:i:s', time()) . ' GMT'); 222 } 223 } 224 else 225 { 226 header('Content-Disposition: ' . ((strpos($attachment['mimetype'], 'image') === 0) ? 'inline' : 'attachment') . '; ' . header_filename(htmlspecialchars_decode($attachment['real_filename']))); 227 if (phpbb_is_greater_ie_version($user->browser, 7) && (strpos($attachment['mimetype'], 'image') !== 0)) 228 { 229 header('X-Download-Options: noopen'); 230 } 231 } 232 } 233 234 // Close the db connection before sending the file etc. 235 file_gc(false); 236 237 if (!set_modified_headers($attachment['filetime'], $user->browser)) 238 { 239 // We make sure those have to be enabled manually by defining a constant 240 // because of the potential disclosure of full attachment path 241 // in case support for features is absent in the webserver software. 242 if (defined('PHPBB_ENABLE_X_ACCEL_REDIRECT') && PHPBB_ENABLE_X_ACCEL_REDIRECT) 243 { 244 // X-Accel-Redirect - http://wiki.nginx.org/XSendfile 245 header('X-Accel-Redirect: ' . $user->page['root_script_path'] . $upload_dir . '/' . $attachment['physical_filename']); 246 exit; 247 } 248 else if (defined('PHPBB_ENABLE_X_SENDFILE') && PHPBB_ENABLE_X_SENDFILE && !phpbb_http_byte_range($size)) 249 { 250 // X-Sendfile - http://blog.lighttpd.net/articles/2006/07/02/x-sendfile 251 // Lighttpd's X-Sendfile does not support range requests as of 1.4.26 252 // and always requires an absolute path. 253 header('X-Sendfile: ' . dirname(__FILE__) . "/../$upload_dir/{$attachment['physical_filename']}"); 254 exit; 255 } 256 257 if ($size) 258 { 259 header("Content-Length: $size"); 260 } 261 262 // Try to deliver in chunks 263 @set_time_limit(0); 264 265 $fp = @fopen($filename, 'rb'); 266 267 if ($fp !== false) 268 { 269 // Deliver file partially if requested 270 if ($range = phpbb_http_byte_range($size)) 271 { 272 fseek($fp, $range['byte_pos_start']); 273 274 send_status_line(206, 'Partial Content'); 275 header('Content-Range: bytes ' . $range['byte_pos_start'] . '-' . $range['byte_pos_end'] . '/' . $range['bytes_total']); 276 header('Content-Length: ' . $range['bytes_requested']); 277 } 278 279 while (!feof($fp)) 280 { 281 echo fread($fp, 8192); 282 } 283 fclose($fp); 284 } 285 else 286 { 287 @readfile($filename); 288 } 289 290 flush(); 291 } 292 293 exit; 294 } 295 296 /** 297 * Get a browser friendly UTF-8 encoded filename 298 */ 299 function header_filename($file) 300 { 301 global $request; 302 303 $user_agent = $request->header('User-Agent'); 304 305 // There be dragons here. 306 // Not many follows the RFC... 307 if (strpos($user_agent, 'MSIE') !== false || strpos($user_agent, 'Konqueror') !== false) 308 { 309 return "filename=" . rawurlencode($file); 310 } 311 312 // follow the RFC for extended filename for the rest 313 return "filename*=UTF-8''" . rawurlencode($file); 314 } 315 316 /** 317 * Check if downloading item is allowed 318 */ 319 function download_allowed() 320 { 321 global $config, $user, $db, $request; 322 323 if (!$config['secure_downloads']) 324 { 325 return true; 326 } 327 328 $url = htmlspecialchars_decode($request->header('Referer')); 329 330 if (!$url) 331 { 332 return ($config['secure_allow_empty_referer']) ? true : false; 333 } 334 335 // Split URL into domain and script part 336 $url = @parse_url($url); 337 338 if ($url === false) 339 { 340 return ($config['secure_allow_empty_referer']) ? true : false; 341 } 342 343 $hostname = $url['host']; 344 unset($url); 345 346 $allowed = ($config['secure_allow_deny']) ? false : true; 347 $iplist = array(); 348 349 if (($ip_ary = @gethostbynamel($hostname)) !== false) 350 { 351 foreach ($ip_ary as $ip) 352 { 353 if ($ip) 354 { 355 $iplist[] = $ip; 356 } 357 } 358 } 359 360 // Check for own server... 361 $server_name = $user->host; 362 363 // Forcing server vars is the only way to specify/override the protocol 364 if ($config['force_server_vars'] || !$server_name) 365 { 366 $server_name = $config['server_name']; 367 } 368 369 if (preg_match('#^.*?' . preg_quote($server_name, '#') . '.*?$#i', $hostname)) 370 { 371 $allowed = true; 372 } 373 374 // Get IP's and Hostnames 375 if (!$allowed) 376 { 377 $sql = 'SELECT site_ip, site_hostname, ip_exclude 378 FROM ' . SITELIST_TABLE; 379 $result = $db->sql_query($sql); 380 381 while ($row = $db->sql_fetchrow($result)) 382 { 383 $site_ip = trim($row['site_ip']); 384 $site_hostname = trim($row['site_hostname']); 385 386 if ($site_ip) 387 { 388 foreach ($iplist as $ip) 389 { 390 if (preg_match('#^' . str_replace('\*', '.*?', preg_quote($site_ip, '#')) . '$#i', $ip)) 391 { 392 if ($row['ip_exclude']) 393 { 394 $allowed = ($config['secure_allow_deny']) ? false : true; 395 break 2; 396 } 397 else 398 { 399 $allowed = ($config['secure_allow_deny']) ? true : false; 400 } 401 } 402 } 403 } 404 405 if ($site_hostname) 406 { 407 if (preg_match('#^' . str_replace('\*', '.*?', preg_quote($site_hostname, '#')) . '$#i', $hostname)) 408 { 409 if ($row['ip_exclude']) 410 { 411 $allowed = ($config['secure_allow_deny']) ? false : true; 412 break; 413 } 414 else 415 { 416 $allowed = ($config['secure_allow_deny']) ? true : false; 417 } 418 } 419 } 420 } 421 $db->sql_freeresult($result); 422 } 423 424 return $allowed; 425 } 426 427 /** 428 * Check if the browser has the file already and set the appropriate headers- 429 * @returns false if a resend is in order. 430 */ 431 function set_modified_headers($stamp, $browser) 432 { 433 global $request; 434 435 // let's see if we have to send the file at all 436 $last_load = $request->header('If-Modified-Since') ? strtotime(trim($request->header('If-Modified-Since'))) : false; 437 438 if (strpos(strtolower($browser), 'msie 6.0') === false && !phpbb_is_greater_ie_version($browser, 7)) 439 { 440 if ($last_load !== false && $last_load >= $stamp) 441 { 442 send_status_line(304, 'Not Modified'); 443 // seems that we need those too ... browsers 444 header('Cache-Control: public'); 445 header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 31536000) . ' GMT'); 446 return true; 447 } 448 else 449 { 450 header('Last-Modified: ' . gmdate('D, d M Y H:i:s', $stamp) . ' GMT'); 451 } 452 } 453 return false; 454 } 455 456 /** 457 * Garbage Collection 458 * 459 * @param bool $exit Whether to die or not. 460 * 461 * @return null 462 */ 463 function file_gc($exit = true) 464 { 465 global $cache, $db; 466 467 if (!empty($cache)) 468 { 469 $cache->unload(); 470 } 471 472 $db->sql_close(); 473 474 if ($exit) 475 { 476 exit; 477 } 478 } 479 480 /** 481 * HTTP range support (RFC 2616 Section 14.35) 482 * 483 * Allows browsers to request partial file content 484 * in case a download has been interrupted. 485 * 486 * @param int $filesize the size of the file in bytes we are about to deliver 487 * 488 * @return mixed false if the whole file has to be delivered 489 * associative array on success 490 */ 491 function phpbb_http_byte_range($filesize) 492 { 493 // Only call find_range_request() once. 494 static $request_array; 495 496 if (!$filesize) 497 { 498 return false; 499 } 500 501 if (!isset($request_array)) 502 { 503 $request_array = phpbb_find_range_request(); 504 } 505 506 return (empty($request_array)) ? false : phpbb_parse_range_request($request_array, $filesize); 507 } 508 509 /** 510 * Searches for HTTP range request in request headers. 511 * 512 * @return mixed false if no request found 513 * array of strings containing the requested ranges otherwise 514 * e.g. array(0 => '0-0', 1 => '123-125') 515 */ 516 function phpbb_find_range_request() 517 { 518 global $request; 519 520 $value = $request->header('Range'); 521 522 // Make sure range request starts with "bytes=" 523 if (strpos($value, 'bytes=') === 0) 524 { 525 // Strip leading 'bytes=' 526 // Multiple ranges can be separated by a comma 527 return explode(',', substr($value, 6)); 528 } 529 530 return false; 531 } 532 533 /** 534 * Analyses a range request array. 535 * 536 * A range request can contain multiple ranges, 537 * we however only handle the first request and 538 * only support requests from a given byte to the end of the file. 539 * 540 * @param array $request_array array of strings containing the requested ranges 541 * @param int $filesize the full size of the file in bytes that has been requested 542 * 543 * @return mixed false if the whole file has to be delivered 544 * associative array on success 545 * byte_pos_start the first byte position, can be passed to fseek() 546 * byte_pos_end the last byte position 547 * bytes_requested the number of bytes requested 548 * bytes_total the full size of the file 549 */ 550 function phpbb_parse_range_request($request_array, $filesize) 551 { 552 // Go through all ranges 553 foreach ($request_array as $range_string) 554 { 555 $range = explode('-', trim($range_string)); 556 557 // "-" is invalid, "0-0" however is valid and means the very first byte. 558 if (sizeof($range) != 2 || $range[0] === '' && $range[1] === '') 559 { 560 continue; 561 } 562 563 if ($range[0] === '') 564 { 565 // Return last $range[1] bytes. 566 567 if (!$range[1]) 568 { 569 continue; 570 } 571 572 if ($range[1] >= $filesize) 573 { 574 return false; 575 } 576 577 $first_byte_pos = $filesize - (int) $range[1]; 578 $last_byte_pos = $filesize - 1; 579 } 580 else 581 { 582 // Return bytes from $range[0] to $range[1] 583 584 $first_byte_pos = (int) $range[0]; 585 $last_byte_pos = (int) $range[1]; 586 587 if ($last_byte_pos && $last_byte_pos < $first_byte_pos) 588 { 589 // The requested range contains 0 bytes. 590 continue; 591 } 592 593 if ($first_byte_pos >= $filesize) 594 { 595 // Requested range not satisfiable 596 return false; 597 } 598 599 // Adjust last-byte-pos if it is absent or greater than the content. 600 if ($range[1] === '' || $last_byte_pos >= $filesize) 601 { 602 $last_byte_pos = $filesize - 1; 603 } 604 } 605 606 // We currently do not support range requests that end before the end of the file 607 if ($last_byte_pos != $filesize - 1) 608 { 609 continue; 610 } 611 612 return array( 613 'byte_pos_start' => $first_byte_pos, 614 'byte_pos_end' => $last_byte_pos, 615 'bytes_requested' => $last_byte_pos - $first_byte_pos + 1, 616 'bytes_total' => $filesize, 617 ); 618 } 619 } 620 621 /** 622 * Increments the download count of all provided attachments 623 * 624 * @param \phpbb\db\driver\driver_interface $db The database object 625 * @param array|int $ids The attach_id of each attachment 626 * 627 * @return null 628 */ 629 function phpbb_increment_downloads($db, $ids) 630 { 631 if (!is_array($ids)) 632 { 633 $ids = array($ids); 634 } 635 636 $sql = 'UPDATE ' . ATTACHMENTS_TABLE . ' 637 SET download_count = download_count + 1 638 WHERE ' . $db->sql_in_set('attach_id', $ids); 639 $db->sql_query($sql); 640 } 641 642 /** 643 * Handles authentication when downloading attachments from a post or topic 644 * 645 * @param \phpbb\db\driver\driver_interface $db The database object 646 * @param \phpbb\auth\auth $auth The authentication object 647 * @param int $topic_id The id of the topic that we are downloading from 648 * 649 * @return null 650 */ 651 function phpbb_download_handle_forum_auth($db, $auth, $topic_id) 652 { 653 $sql_array = array( 654 'SELECT' => 't.topic_visibility, t.forum_id, f.forum_name, f.forum_password, f.parent_id', 655 'FROM' => array( 656 TOPICS_TABLE => 't', 657 FORUMS_TABLE => 'f', 658 ), 659 'WHERE' => 't.topic_id = ' . (int) $topic_id . ' 660 AND t.forum_id = f.forum_id', 661 ); 662 663 $sql = $db->sql_build_query('SELECT', $sql_array); 664 $result = $db->sql_query($sql); 665 $row = $db->sql_fetchrow($result); 666 $db->sql_freeresult($result); 667 668 if ($row && $row['topic_visibility'] != ITEM_APPROVED && !$auth->acl_get('m_approve', $row['forum_id'])) 669 { 670 send_status_line(404, 'Not Found'); 671 trigger_error('ERROR_NO_ATTACHMENT'); 672 } 673 else if ($row && $auth->acl_get('u_download') && $auth->acl_get('f_download', $row['forum_id'])) 674 { 675 if ($row['forum_password']) 676 { 677 // Do something else ... ? 678 login_forum_box($row); 679 } 680 } 681 else 682 { 683 send_status_line(403, 'Forbidden'); 684 trigger_error('SORRY_AUTH_VIEW_ATTACH'); 685 } 686 } 687 688 /** 689 * Handles authentication when downloading attachments from PMs 690 * 691 * @param \phpbb\db\driver\driver_interface $db The database object 692 * @param \phpbb\auth\auth $auth The authentication object 693 * @param int $user_id The user id 694 * @param int $msg_id The id of the PM that we are downloading from 695 * 696 * @return null 697 */ 698 function phpbb_download_handle_pm_auth($db, $auth, $user_id, $msg_id) 699 { 700 global $phpbb_dispatcher; 701 702 if (!$auth->acl_get('u_pm_download')) 703 { 704 send_status_line(403, 'Forbidden'); 705 trigger_error('SORRY_AUTH_VIEW_ATTACH'); 706 } 707 708 $allowed = phpbb_download_check_pm_auth($db, $user_id, $msg_id); 709 710 /** 711 * Event to modify PM attachments download auth 712 * 713 * @event core.modify_pm_attach_download_auth 714 * @var bool allowed Whether the user is allowed to download from that PM or not 715 * @var int msg_id The id of the PM to download from 716 * @var int user_id The user id for auth check 717 * @since 3.1.11-RC1 718 */ 719 $vars = array('allowed', 'msg_id', 'user_id'); 720 extract($phpbb_dispatcher->trigger_event('core.modify_pm_attach_download_auth', compact($vars))); 721 722 if (!$allowed) 723 { 724 send_status_line(403, 'Forbidden'); 725 trigger_error('ERROR_NO_ATTACHMENT'); 726 } 727 } 728 729 /** 730 * Checks whether a user can download from a particular PM 731 * 732 * @param \phpbb\db\driver\driver_interface $db The database object 733 * @param int $user_id The user id 734 * @param int $msg_id The id of the PM that we are downloading from 735 * 736 * @return bool Whether the user is allowed to download from that PM or not 737 */ 738 function phpbb_download_check_pm_auth($db, $user_id, $msg_id) 739 { 740 // Check if the attachment is within the users scope... 741 $sql = 'SELECT msg_id 742 FROM ' . PRIVMSGS_TO_TABLE . ' 743 WHERE msg_id = ' . (int) $msg_id . ' 744 AND ( 745 user_id = ' . (int) $user_id . ' 746 OR author_id = ' . (int) $user_id . ' 747 )'; 748 $result = $db->sql_query_limit($sql, 1); 749 $allowed = (bool) $db->sql_fetchfield('msg_id'); 750 $db->sql_freeresult($result); 751 752 return $allowed; 753 } 754 755 /** 756 * Check if the browser is internet explorer version 7+ 757 * 758 * @param string $user_agent User agent HTTP header 759 * @param int $version IE version to check against 760 * 761 * @return bool true if internet explorer version is greater than $version 762 */ 763 function phpbb_is_greater_ie_version($user_agent, $version) 764 { 765 if (preg_match('/msie (\d+)/', strtolower($user_agent), $matches)) 766 { 767 $ie_version = (int) $matches[1]; 768 return ($ie_version > $version); 769 } 770 else 771 { 772 return false; 773 } 774 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Jan 11 00:25:41 2018 | Cross-referenced by PHPXref 0.7.1 |