[ Index ] |
PHP Cross Reference of phpBB-3.3.14-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, $phpbb_dispatcher, $phpbb_root_path, $request; 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: private'); 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 (empty($user->browser) || ((strpos(strtolower($user->browser), 'msie') !== false) && !phpbb_is_greater_ie_version($user->browser, 7))) 210 { 211 header('Content-Disposition: attachment; ' . header_filename(html_entity_decode($attachment['real_filename'], ENT_COMPAT))); 212 if (empty($user->browser) || (strpos(strtolower($user->browser), 'msie 6.0') !== false)) 213 { 214 header('Expires: ' . gmdate('D, d M Y H:i:s', time()) . ' GMT'); 215 } 216 } 217 else 218 { 219 header('Content-Disposition: ' . ((strpos($attachment['mimetype'], 'image') === 0) ? 'inline' : 'attachment') . '; ' . header_filename(html_entity_decode($attachment['real_filename'], ENT_COMPAT))); 220 if (phpbb_is_greater_ie_version($user->browser, 7) && (strpos($attachment['mimetype'], 'image') !== 0)) 221 { 222 header('X-Download-Options: noopen'); 223 } 224 } 225 226 // Close the db connection before sending the file etc. 227 file_gc(false); 228 229 if (!set_modified_headers($attachment['filetime'], $user->browser)) 230 { 231 // We make sure those have to be enabled manually by defining a constant 232 // because of the potential disclosure of full attachment path 233 // in case support for features is absent in the webserver software. 234 if (defined('PHPBB_ENABLE_X_ACCEL_REDIRECT') && PHPBB_ENABLE_X_ACCEL_REDIRECT) 235 { 236 // X-Accel-Redirect - http://wiki.nginx.org/XSendfile 237 header('X-Accel-Redirect: ' . $user->page['root_script_path'] . $upload_dir . '/' . $attachment['physical_filename']); 238 exit; 239 } 240 else if (defined('PHPBB_ENABLE_X_SENDFILE') && PHPBB_ENABLE_X_SENDFILE && !phpbb_http_byte_range($size)) 241 { 242 // X-Sendfile - http://blog.lighttpd.net/articles/2006/07/02/x-sendfile 243 // Lighttpd's X-Sendfile does not support range requests as of 1.4.26 244 // and always requires an absolute path. 245 header('X-Sendfile: ' . __DIR__ . "/../$upload_dir/{$attachment['physical_filename']}"); 246 exit; 247 } 248 249 if ($size) 250 { 251 header("Content-Length: $size"); 252 } 253 254 // Try to deliver in chunks 255 @set_time_limit(0); 256 257 $fp = @fopen($filename, 'rb'); 258 259 if ($fp !== false) 260 { 261 // Deliver file partially if requested 262 if ($range = phpbb_http_byte_range($size)) 263 { 264 fseek($fp, $range['byte_pos_start']); 265 266 send_status_line(206, 'Partial Content'); 267 header('Content-Range: bytes ' . $range['byte_pos_start'] . '-' . $range['byte_pos_end'] . '/' . $range['bytes_total']); 268 header('Content-Length: ' . $range['bytes_requested']); 269 270 // First read chunks 271 while (!feof($fp) && ftell($fp) < $range['byte_pos_end'] - 8192) 272 { 273 echo fread($fp, 8192); 274 } 275 // Then, read the remainder 276 echo fread($fp, $range['bytes_requested'] % 8192); 277 } 278 else 279 { 280 while (!feof($fp)) 281 { 282 echo fread($fp, 8192); 283 } 284 } 285 fclose($fp); 286 } 287 else 288 { 289 @readfile($filename); 290 } 291 292 flush(); 293 } 294 295 exit; 296 } 297 298 /** 299 * Get a browser friendly UTF-8 encoded filename 300 */ 301 function header_filename($file) 302 { 303 global $request; 304 305 $user_agent = $request->header('User-Agent'); 306 307 // There be dragons here. 308 // Not many follows the RFC... 309 if (strpos($user_agent, 'MSIE') !== false || strpos($user_agent, 'Konqueror') !== false) 310 { 311 return "filename=" . rawurlencode($file); 312 } 313 314 // follow the RFC for extended filename for the rest 315 return "filename*=UTF-8''" . rawurlencode($file); 316 } 317 318 /** 319 * Check if downloading item is allowed 320 */ 321 function download_allowed() 322 { 323 global $config, $user, $db, $request; 324 325 if (!$config['secure_downloads']) 326 { 327 return true; 328 } 329 330 $url = html_entity_decode($request->header('Referer'), ENT_COMPAT); 331 332 if (!$url) 333 { 334 return ($config['secure_allow_empty_referer']) ? true : false; 335 } 336 337 // Split URL into domain and script part 338 $url = @parse_url($url); 339 340 if ($url === false) 341 { 342 return ($config['secure_allow_empty_referer']) ? true : false; 343 } 344 345 $hostname = $url['host']; 346 unset($url); 347 348 $allowed = ($config['secure_allow_deny']) ? false : true; 349 $iplist = array(); 350 351 if (($ip_ary = @gethostbynamel($hostname)) !== false) 352 { 353 foreach ($ip_ary as $ip) 354 { 355 if ($ip) 356 { 357 $iplist[] = $ip; 358 } 359 } 360 } 361 362 // Check for own server... 363 $server_name = $user->host; 364 365 // Forcing server vars is the only way to specify/override the protocol 366 if ($config['force_server_vars'] || !$server_name) 367 { 368 $server_name = $config['server_name']; 369 } 370 371 if (preg_match('#^.*?' . preg_quote($server_name, '#') . '.*?$#i', $hostname)) 372 { 373 $allowed = true; 374 } 375 376 // Get IP's and Hostnames 377 if (!$allowed) 378 { 379 $sql = 'SELECT site_ip, site_hostname, ip_exclude 380 FROM ' . SITELIST_TABLE; 381 $result = $db->sql_query($sql); 382 383 while ($row = $db->sql_fetchrow($result)) 384 { 385 $site_ip = trim($row['site_ip']); 386 $site_hostname = trim($row['site_hostname']); 387 388 if ($site_ip) 389 { 390 foreach ($iplist as $ip) 391 { 392 if (preg_match('#^' . str_replace('\*', '.*?', preg_quote($site_ip, '#')) . '$#i', $ip)) 393 { 394 if ($row['ip_exclude']) 395 { 396 $allowed = ($config['secure_allow_deny']) ? false : true; 397 break 2; 398 } 399 else 400 { 401 $allowed = ($config['secure_allow_deny']) ? true : false; 402 } 403 } 404 } 405 } 406 407 if ($site_hostname) 408 { 409 if (preg_match('#^' . str_replace('\*', '.*?', preg_quote($site_hostname, '#')) . '$#i', $hostname)) 410 { 411 if ($row['ip_exclude']) 412 { 413 $allowed = ($config['secure_allow_deny']) ? false : true; 414 break; 415 } 416 else 417 { 418 $allowed = ($config['secure_allow_deny']) ? true : false; 419 } 420 } 421 } 422 } 423 $db->sql_freeresult($result); 424 } 425 426 return $allowed; 427 } 428 429 /** 430 * Check if the browser has the file already and set the appropriate headers- 431 * @returns false if a resend is in order. 432 */ 433 function set_modified_headers($stamp, $browser) 434 { 435 global $request; 436 437 // let's see if we have to send the file at all 438 $last_load = $request->header('If-Modified-Since') ? strtotime(trim($request->header('If-Modified-Since'))) : false; 439 440 if (strpos(strtolower($browser), 'msie 6.0') === false && !phpbb_is_greater_ie_version($browser, 7)) 441 { 442 if ($last_load !== false && $last_load >= $stamp) 443 { 444 send_status_line(304, 'Not Modified'); 445 // seems that we need those too ... browsers 446 header('Cache-Control: private'); 447 header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 31536000) . ' GMT'); 448 return true; 449 } 450 else 451 { 452 header('Last-Modified: ' . gmdate('D, d M Y H:i:s', $stamp) . ' GMT'); 453 } 454 } 455 return false; 456 } 457 458 /** 459 * Garbage Collection 460 * 461 * @param bool $exit Whether to die or not. 462 * 463 * @return null 464 */ 465 function file_gc($exit = true) 466 { 467 global $cache, $db; 468 469 if (!empty($cache)) 470 { 471 $cache->unload(); 472 } 473 474 $db->sql_close(); 475 476 if ($exit) 477 { 478 exit; 479 } 480 } 481 482 /** 483 * HTTP range support (RFC 2616 Section 14.35) 484 * 485 * Allows browsers to request partial file content 486 * in case a download has been interrupted. 487 * 488 * @param int $filesize the size of the file in bytes we are about to deliver 489 * 490 * @return mixed false if the whole file has to be delivered 491 * associative array on success 492 */ 493 function phpbb_http_byte_range($filesize) 494 { 495 // Only call find_range_request() once. 496 static $request_array; 497 498 if (!$filesize) 499 { 500 return false; 501 } 502 503 if (!isset($request_array)) 504 { 505 $request_array = phpbb_find_range_request(); 506 } 507 508 return (empty($request_array)) ? false : phpbb_parse_range_request($request_array, $filesize); 509 } 510 511 /** 512 * Searches for HTTP range request in request headers. 513 * 514 * @return mixed false if no request found 515 * array of strings containing the requested ranges otherwise 516 * e.g. array(0 => '0-0', 1 => '123-125') 517 */ 518 function phpbb_find_range_request() 519 { 520 global $request; 521 522 $value = $request->header('Range'); 523 524 // Make sure range request starts with "bytes=" 525 if (strpos($value, 'bytes=') === 0) 526 { 527 // Strip leading 'bytes=' 528 // Multiple ranges can be separated by a comma 529 return explode(',', substr($value, 6)); 530 } 531 532 return false; 533 } 534 535 /** 536 * Analyses a range request array. 537 * 538 * A range request can contain multiple ranges, 539 * we however only handle the first request and 540 * only support requests from a given byte to the end of the file. 541 * 542 * @param array $request_array array of strings containing the requested ranges 543 * @param int $filesize the full size of the file in bytes that has been requested 544 * 545 * @return mixed false if the whole file has to be delivered 546 * associative array on success 547 * byte_pos_start the first byte position, can be passed to fseek() 548 * byte_pos_end the last byte position 549 * bytes_requested the number of bytes requested 550 * bytes_total the full size of the file 551 */ 552 function phpbb_parse_range_request($request_array, $filesize) 553 { 554 $first_byte_pos = -1; 555 $last_byte_pos = -1; 556 557 // Go through all ranges 558 foreach ($request_array as $range_string) 559 { 560 $range = explode('-', trim($range_string)); 561 562 // "-" is invalid, "0-0" however is valid and means the very first byte. 563 if (count($range) != 2 || $range[0] === '' && $range[1] === '') 564 { 565 continue; 566 } 567 568 // Substitute defaults 569 if ($range[0] === '') 570 { 571 $range[0] = 0; 572 } 573 574 if ($range[1] === '') 575 { 576 $range[1] = $filesize - 1; 577 } 578 579 if ($last_byte_pos >= 0 && $last_byte_pos + 1 != $range[0]) 580 { 581 // We only support contiguous ranges, no multipart stuff :( 582 return false; 583 } 584 585 if ($range[1] && $range[1] < $range[0]) 586 { 587 // The requested range contains 0 bytes. 588 continue; 589 } 590 591 // Return bytes from $range[0] to $range[1] 592 if ($first_byte_pos < 0) 593 { 594 $first_byte_pos = (int) $range[0]; 595 } 596 597 $last_byte_pos = (int) $range[1]; 598 599 if ($first_byte_pos >= $filesize) 600 { 601 // Requested range not satisfiable 602 return false; 603 } 604 605 // Adjust last-byte-pos if it is absent or greater than the content. 606 if ($range[1] === '' || $last_byte_pos >= $filesize) 607 { 608 $last_byte_pos = $filesize - 1; 609 } 610 } 611 612 if ($first_byte_pos < 0 || $last_byte_pos < 0) 613 { 614 return false; 615 } 616 617 return array( 618 'byte_pos_start' => $first_byte_pos, 619 'byte_pos_end' => $last_byte_pos, 620 'bytes_requested' => $last_byte_pos - $first_byte_pos + 1, 621 'bytes_total' => $filesize, 622 ); 623 } 624 625 /** 626 * Increments the download count of all provided attachments 627 * 628 * @param \phpbb\db\driver\driver_interface $db The database object 629 * @param array|int $ids The attach_id of each attachment 630 * 631 * @return null 632 */ 633 function phpbb_increment_downloads($db, $ids) 634 { 635 if (!is_array($ids)) 636 { 637 $ids = array($ids); 638 } 639 640 $sql = 'UPDATE ' . ATTACHMENTS_TABLE . ' 641 SET download_count = download_count + 1 642 WHERE ' . $db->sql_in_set('attach_id', $ids); 643 $db->sql_query($sql); 644 } 645 646 /** 647 * Handles authentication when downloading attachments from a post or topic 648 * 649 * @param \phpbb\db\driver\driver_interface $db The database object 650 * @param \phpbb\auth\auth $auth The authentication object 651 * @param int $topic_id The id of the topic that we are downloading from 652 * 653 * @return null 654 */ 655 function phpbb_download_handle_forum_auth($db, $auth, $topic_id) 656 { 657 global $phpbb_container; 658 659 $sql_array = [ 660 'SELECT' => 't.forum_id, t.topic_poster, t.topic_visibility, f.forum_name, f.forum_password, f.parent_id', 661 'FROM' => [ 662 TOPICS_TABLE => 't', 663 FORUMS_TABLE => 'f', 664 ], 665 'WHERE' => 't.topic_id = ' . (int) $topic_id . ' 666 AND t.forum_id = f.forum_id', 667 ]; 668 669 $sql = $db->sql_build_query('SELECT', $sql_array); 670 $result = $db->sql_query($sql); 671 $row = $db->sql_fetchrow($result); 672 $db->sql_freeresult($result); 673 674 $phpbb_content_visibility = $phpbb_container->get('content.visibility'); 675 676 if ($row && !$phpbb_content_visibility->is_visible('topic', $row['forum_id'], $row)) 677 { 678 send_status_line(404, 'Not Found'); 679 trigger_error('ERROR_NO_ATTACHMENT'); 680 } 681 else if ($row && $auth->acl_get('u_download') && $auth->acl_get('f_download', $row['forum_id'])) 682 { 683 if ($row['forum_password']) 684 { 685 // Do something else ... ? 686 login_forum_box($row); 687 } 688 } 689 else 690 { 691 send_status_line(403, 'Forbidden'); 692 trigger_error('SORRY_AUTH_VIEW_ATTACH'); 693 } 694 } 695 696 /** 697 * Handles authentication when downloading attachments from PMs 698 * 699 * @param \phpbb\db\driver\driver_interface $db The database object 700 * @param \phpbb\auth\auth $auth The authentication object 701 * @param int $user_id The user id 702 * @param int $msg_id The id of the PM that we are downloading from 703 * 704 * @return null 705 */ 706 function phpbb_download_handle_pm_auth($db, $auth, $user_id, $msg_id) 707 { 708 global $phpbb_dispatcher; 709 710 if (!$auth->acl_get('u_pm_download')) 711 { 712 send_status_line(403, 'Forbidden'); 713 trigger_error('SORRY_AUTH_VIEW_ATTACH'); 714 } 715 716 $allowed = phpbb_download_check_pm_auth($db, $user_id, $msg_id); 717 718 /** 719 * Event to modify PM attachments download auth 720 * 721 * @event core.modify_pm_attach_download_auth 722 * @var bool allowed Whether the user is allowed to download from that PM or not 723 * @var int msg_id The id of the PM to download from 724 * @var int user_id The user id for auth check 725 * @since 3.1.11-RC1 726 */ 727 $vars = array('allowed', 'msg_id', 'user_id'); 728 extract($phpbb_dispatcher->trigger_event('core.modify_pm_attach_download_auth', compact($vars))); 729 730 if (!$allowed) 731 { 732 send_status_line(403, 'Forbidden'); 733 trigger_error('ERROR_NO_ATTACHMENT'); 734 } 735 } 736 737 /** 738 * Checks whether a user can download from a particular PM 739 * 740 * @param \phpbb\db\driver\driver_interface $db The database object 741 * @param int $user_id The user id 742 * @param int $msg_id The id of the PM that we are downloading from 743 * 744 * @return bool Whether the user is allowed to download from that PM or not 745 */ 746 function phpbb_download_check_pm_auth($db, $user_id, $msg_id) 747 { 748 // Check if the attachment is within the users scope... 749 $sql = 'SELECT msg_id 750 FROM ' . PRIVMSGS_TO_TABLE . ' 751 WHERE msg_id = ' . (int) $msg_id . ' 752 AND ( 753 user_id = ' . (int) $user_id . ' 754 OR author_id = ' . (int) $user_id . ' 755 )'; 756 $result = $db->sql_query_limit($sql, 1); 757 $allowed = (bool) $db->sql_fetchfield('msg_id'); 758 $db->sql_freeresult($result); 759 760 return $allowed; 761 } 762 763 /** 764 * Check if the browser is internet explorer version 7+ 765 * 766 * @param string $user_agent User agent HTTP header 767 * @param int $version IE version to check against 768 * 769 * @return bool true if internet explorer version is greater than $version 770 */ 771 function phpbb_is_greater_ie_version($user_agent, $version) 772 { 773 if (preg_match('/msie (\d+)/', strtolower($user_agent), $matches)) 774 { 775 $ie_version = (int) $matches[1]; 776 return ($ie_version > $version); 777 } 778 else 779 { 780 return false; 781 } 782 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Mon Nov 25 19:05:08 2024 | Cross-referenced by PHPXref 0.7.1 |