diff --git a/e107_handlers/iphandler_class.php b/e107_handlers/iphandler_class.php index 873d4dca1..cce019be3 100644 --- a/e107_handlers/iphandler_class.php +++ b/e107_handlers/iphandler_class.php @@ -2,7 +2,7 @@ /* * e107 website system * -* Copyright 2008-2011 e107 Inc (e107.org) +* Copyright 2008-2013 e107 Inc (e107.org) * Released under the terms and conditions of the * GNU General Public License (http://www.gnu.org/licenses/gpl.txt) * @@ -58,7 +58,6 @@ * */ -// define("BAN_TYPE_LEGACY",0); // Quick fix for PHP Notice. If the error is triggered from somewhere else, the class name needs adding - eIPHandler::BAN_TYPE_LEGACY class eIPHandler { @@ -271,8 +270,8 @@ class eIPHandler if ($ip == 'ff02:0000:0000:0000:0000:0000:0000:0001') return FALSE; if ($ip == '::1') return FALSE; // localhost if ($ip == '0000:0000:0000:0000:0000:0000:0000:0001') return FALSE; + if (substr($ip, 0, 5) == 'fc00:') return FALSE; // local addresses // @todo add: - // fc00::/7 - local addresses // ::0 (all zero) - invalid // ff02::1:ff00:0/104 - Solicited-Node multicast addresses - add? // 2001:0000::/29 through 2001:01f8::/29 - special purpose addresses @@ -717,7 +716,7 @@ class eIPHandler */ function makeDomainQuery($email, $fieldName = 'banlist_ip') { - global $tp; + $tp = e107::getParser(); if (($tv = strrpos('@', $email)) !== FALSE) { $email = substr($email, $tv+1); @@ -852,8 +851,8 @@ class eIPHandler $sql = e107::getDb(); $pref = e107::getPref(); $tp = e107::getParser(); - - //global $admin_log; + $admin_log = e107::getAdminLog(); + //$admin_log->e_log_event(4,__FILE__."|".__FUNCTION__."@".__LINE__,"DBG","Check for Ban",$query,FALSE,LOG_TO_ROLLING); if ($sql->db_Select('banlist', '*', $query.' ORDER BY `banlist_bantype` DESC')) { diff --git a/e107_handlers/upload_handler.php b/e107_handlers/upload_handler.php index 773139de6..b885755cc 100644 --- a/e107_handlers/upload_handler.php +++ b/e107_handlers/upload_handler.php @@ -1,860 +1,863 @@ - - e_log_event(10, debug_backtrace(), "DEBUG", "Upload Handler test", "Process uploads to {$uploaddir}, fileinfo ".$fileinfo, FALSE, LOG_TO_ROLLING); - // $admin_log->e_log_event(10,__FILE__."|".__FUNCTION__."@".__LINE__,"DEBUG","Upload Handler test","Intermediate directory: {$ul_temp_dir} ",FALSE,LOG_TO_ROLLING); - - $overwrite = varset($options['overwrite'], FALSE); - - $uploaddir = realpath($uploaddir); // Mostly to get rid of the grot that might be passed in from legacy code. Also strips any trailing '/' - if (!is_dir($uploaddir)) - { - if (UH_DEBUG) - $admin_log-> - e_log_event(10, __FILE__."|".__FUNCTION__."@".__LINE__, "DEBUG", "Upload Handler test", "Invalid directory: ".$uploaddir, FALSE, FALSE); - return FALSE; // Need a valid directory - } - if (UH_DEBUG) - $admin_log-> - e_log_event(10, __FILE__."|".__FUNCTION__."@".__LINE__, "DEBUG", "Upload Handler test", "Destination directory: ".$uploaddir, FALSE, FALSE); - - $final_chmod = varset($options['final_chmod'], 0644); - - if (isset($options['file_array_name'])) - { - $files = $_FILES[$options['file_array_name']]; - } - else - { - $files = $_FILES['file_userfile']; - } - - $max_file_count = varset($options['max_file_count'], 0); - - if (!is_array($files)) - { - if (UH_DEBUG) - $admin_log-> - e_log_event(10, __FILE__."|".__FUNCTION__."@".__LINE__, "DEBUG", "Upload Handler test", "No files uploaded", FALSE, FALSE); - return FALSE; - } - - $uploaded = array( - ); - - $max_upload_size = calc_max_upload_size(varset($options['max_upload_size'], -1)); // Find overriding maximum upload size - $allowed_filetypes = get_filetypes(varset($options['file_mask'], ''), varset($options['filetypes'], '')); - $max_upload_size = set_max_size($allowed_filetypes, $max_upload_size); - - // That's the basics set up - we can start processing files now - - if (UH_DEBUG) - $admin_log-> - e_log_event(10, __FILE__."|".__FUNCTION__."@".__LINE__, "DEBUG", "Upload Handler test", "Start individual files: ".count($files['name'])." Max upload: ".$max_upload_size, FALSE, FALSE); - - $c = 0; - foreach ($files['name'] as $key=>$name) - { - $first_error = FALSE; // Clear error flag - if (($name != '') || $files['size'][$key]) // Need this check for things like file manager which allow multiple possible uploads - { - $name = preg_replace("/[^a-z0-9._-]/", '', str_replace(' ', '_', str_replace('%20', '_', strtolower($name)))); - $raw_name = $name; // Save 'proper' file name - useful for display - $file_ext = trim(strtolower(substr(strrchr($name, "."), 1))); // File extension - forced to lower case internally - - if (!trim($files['type'][$key])) - $files['type'][$key] = 'Unknowm mime-type'; - - if (UH_DEBUG) - $admin_log-> - e_log_event(10, __FILE__."|".__FUNCTION__."@".__LINE__, "DEBUG", "Upload Handler test", "Process file {$name}, size ".$files['size'][$key], FALSE, FALSE); - - if ($max_file_count && ($c > $max_file_count)) - { - $first_error = 249; // 'Too many files uploaded' error - } - else - { - $first_error = $files['error'][$key]; // Start with whatever error PHP gives us for the file - } - - if (!$first_error) - { // Check file size early on - if ($files['size'][$key] == 0) - { - $first_error = 4; // Standard error code for zero size file - } - elseif ($files['size'][$key] > $max_upload_size) - { - $first_error = 254; - } - elseif (isset($allowed_filetypes[$file_ext]) && ($allowed_filetypes[$file_ext] > 0) && ($files['size'][$key] > $allowed_filetypes[$file_ext])) - { // XML file set limits per extension - $first_error = 254; - } - } - - if (!$first_error) - { - $uploadfile = $files['tmp_name'][$key]; // Name in temporary directory - if (!$uploadfile) - $first_error = 253; - } - - if (!$first_error) - { - // Need to support multiple files with the same 'real' name in some cases - if (strpos($fileinfo, "attachment") === 0) - { // For attachments, add in a prefix plus time and date to give a unique file name - $addbit = explode('+', $fileinfo, 2); - $name = time()."_".USERID."_".trim($addbit[1]).$name; - } - elseif (strpos($fileinfo, "prefix") === 0) - { // For attachments, avatars, photos etc alternatively just add a prefix we've been passed - $addbit = explode('+', $fileinfo, 2); - $name = trim($addbit[1]).$name; - } - - $destination_file = $uploaddir."/".$name; - - if ($fileinfo == "unique" && file_exists($destination_file)) - { // Modify destination name to make it unique - but only if target file name exists - $name = time()."_".$name; - $destination_file = $uploaddir."/".$name; - } - - if (file_exists($destination_file) && !$overwrite) - $first_error = 250; // Invent our own error number - duplicate file - } - - if (!$first_error) - { - $tpos = FALSE; - if ($file_ext != '') // Require any uploaded file to have an extension - { - if ($ul_temp_dir) - { // Need to move file to our own temporary directory - $tempfilename = $uploadfile; - $uploadfile = $ul_temp_dir.basename($uploadfile); - if (UH_DEBUG) - $admin_log-> - e_log_event(10, __FILE__."|".__FUNCTION__."@".__LINE__, "DEBUG", "Upload Handler test", "Move {$tempfilename} to {$uploadfile} ", FALSE, LOG_TO_ROLLING); - @move_uploaded_file($tempfilename, $uploadfile); // This should work on all hosts - } - $tpos = (($file_status = vet_file($uploadfile, $name, $allowed_filetypes, varset($options['extra_file_types'], FALSE))) === TRUE); - } - if ($tpos === FALSE) - { - // File type upload not permitted - error message and abort - $first_error = 251; // Invent our own error number - file type not permitted - } - } - - if (!$first_error) - { // All tests passed - can store it somewhere - $uploaded[$c]['name'] = $name; - $uploaded[$c]['rawname'] = $raw_name; - $uploaded[$c]['type'] = $files['type'][$key]; - $uploaded[$c]['size'] = 0; - $uploaded[$c]['index'] = $key; // Store the actual index from the file_userfile array - - // Store as flat file - if ((!$ul_temp_dir && @move_uploaded_file($uploadfile, $destination_file)) || ($ul_temp_dir && @rename($uploadfile, $destination_file))) // This should work on all hosts - { - @chmod($destination_file, $final_chmod); - if (UH_DEBUG) - $admin_log-> - e_log_event(10, __FILE__."|".__FUNCTION__."@".__LINE__, "DEBUG", "Upload Handler test", "Final chmod() file {$destination_file} to {$final_chmod} ", FALSE, FALSE); - - $uploaded[$c]['size'] = $files['size'][$key]; - if (UH_DEBUG) - $admin_log-> - e_log_event(10, __FILE__."|".__FUNCTION__."@".__LINE__, "DEBUG", "Upload Handler test", "Saved file {$c} OK: ".$uploaded[$c]['name'], FALSE, FALSE); - } - else - { - $first_error = 252; // Error - "couldn't save destination" - } - } - - if (!$first_error) - { // This file succeeded - $uploaded[$c]['message'] = LANUPLOAD_3." '".$raw_name."'"; - $uploaded[$c]['error'] = 0; - } - else - { - $uploaded[$c]['error'] = $first_error; - $uploaded[$c]['size'] = 0; - switch ($first_error) - { - case 1: // Exceeds upload_max_filesize in php.ini - $error = LANUPLOAD_5; - break; - case 2: // Exceeds MAX_FILE_SIZE in form - $error = LANUPLOAD_6; - break; - case 3: // Partial upload - $error = LANUPLOAD_7; - break; - case 4: // No file uploaded - $error = LANUPLOAD_8; - break; - case 5: // Undocumented code (zero file size) - $error = LANUPLOAD_9; - break; - case 6: // Missing temporary folder - $error = LANUPLOAD_13; - break; - case 7: // File write failed - $error = LANUPLOAD_14; - break; - case 8: // Upload stopped by extension - $error = LANUPLOAD_15; - break; - case 249: // Too many files (our error code) - $error = LANUPLOAD_19; - break; - case 250: // Duplicate File (our error code) - $error = LANUPLOAD_10; - break; - case 251: // File type not allowed (our error code) - $error = LANUPLOAD_1." ".$files['type'][$key]." ".LANUPLOAD_2." ({$file_status})"; - break; - case 252: // File uploaded OK, but couldn't save it - $error = LANUPLOAD_4." [".str_replace("../", "", $uploaddir)."]"; - break; - case 253: // Bad name for uploaded file (our error code) - $error = LANUPLOAD_17; - break; - case 254: // file size exceeds allowable limits (our error code) - $error = LANUPLOAD_18; - break; - default: // Shouldn't happen - but at least try and make it obvious if it does! - $error = LANUPLOAD_16; - } - - $uploaded[$c]['message'] = LANUPLOAD_11." '".$name."'
".LANUPLOAD_12.": ".$error; - $uploaded[$c]['line'] = __LINE__; - $uploaded[$c]['file'] = __FILE__; - if (UH_DEBUG) - $admin_log-> - e_log_event(10, __FILE__."|".__FUNCTION__."@".__LINE__, "DEBUG", "Upload Handler test", "Main routine error {$first_error} file {$c}: ".$uploaded[$c]['message'], FALSE, FALSE); - // If we need to abort on first error, do so here - could check for specific error codes - } - if (is_file($uploadfile)) - @unlink($uploadfile); // Don't leave the file on the server if error (although should be auto-deleted) - $c++; - } - } - return $uploaded; -} - - - -/** - * Utility routine to handle the messages returned by process_uploaded_files(). - * @param array $upload_array is the list of uploaded files (as returned by process_uploaded_files()) - * @param boolean $errors_only - if TRUE, no message is shown for a successful upload. - * @param boolean $use_handler - if TRUE, message_handler is used to display the message. - * @return string - a list of all accumulated messages. (Non-destructive call, so can be called several times with different options). - */ - -function handle_upload_messages(&$upload_array, $errors_only = TRUE, $use_handler = FALSE) -{ - // Display error messages, accumulate FMESSAGE - // Write as a separate routine - returns all messages displayed. Option to only display failures. - $f_message = ''; - foreach ($upload_array as $k=>$r) - { - if (!$errors_only || $r['error']) - { - if ($use_handler) - { - require_once (e_HANDLER."message_handler.php"); - message_handler("MESSAGE", $r['message'], $r['line'], $r['file']); - } - $f_message[] = $r['message']; - } - } - return implode("
", $f_message); -} - -/* -//==================================================================== -// LEGACY FILE UPLOAD HANDLER -//==================================================================== - -/** - * This is the 'legacy' interface, which handles various special cases etc. - * It was the only option in E107 0.7.8 and earlier, and is still used in some places in core. - * It also attempts to return in the same way as the original, especially when any errors occur - * - * @param string $uploaddir - target directory for file. Defaults to e_FILE/public - * @param boolean|string $avatar - sets the 'type' or destination of the file: - * FALSE - its a 'general' file - * 'attachment' - indicates an attachment (related to forum post or PM) - * 'unique' - indicates that file name must be unique - new name given (prefixed with time()_ ) - * 'avatar' - indicates an avatar is being uploaded - * @param string $fileinfo - included within the name of the saved file with attachments - can be an identifier of some sort - * (Forum adds 'FT{$tid}_' - where $tid is the thread ID. - * @param boolean $overwrite - if true, an uploaded file can overwrite an existing file of the same name (not used in 0.7 core) - * - * Preference used: - * $pref['upload_storagetype'] - now ignored - used to be 1 for files, 2 for database storage - * - * @return boolean|array - For backward compatibility, returns FALSE if only one file uploaded and an error; - * otherwise returns an array with per-file error codes as appropriate. - * On exit, F_MESSAGE is defined with the success/failure message(s) that have been displayed - one file per line - */ - -function file_upload($uploaddir, $avatar = FALSE, $fileinfo = "", $overwrite = "") -{ - global $admin_log; - $options = array( - 'extra_file_types'=>TRUE - ); // As default, allow any filetype enabled in filetypes.php - - if (!$uploaddir) - { - $uploaddir = e_UPLOAD; - } - - if (strpos($avatar, '=') !== FALSE) - { - list($avatar, $param) = explode('=', $avatar, 2); - } - else - { - $param = USERID; - } - switch ($avatar) - { - case 'attachment': - $avatar = "attachment+".$fileinfo; - break; - case 'avatar': - $avatar = 'prefix+ap_'.$param.'_'; // Prefix unique to user - $options['overwrite'] = TRUE; // Allow update of avatar with same file name - break; - } - - if (UH_DEBUG) - $admin_log-> - e_log_event(10, __FILE__."|".__FUNCTION__."@".__LINE__, "DEBUG", "Upload Handler test", "Legacy call, directory ".$uploaddir, FALSE, FALSE); - - $ret = process_uploaded_files(getcwd()."/".$uploaddir, $avatar, $options); // Well, that's the way it was done before - - if ($ret === FALSE) - { - if (UH_DEBUG) - $admin_log-> - e_log_event(10, __FILE__."|".__FUNCTION__."@".__LINE__, "DEBUG", "Upload Handler test", "Legacy return FALSE", FALSE, FALSE); - return FALSE; - } - - if (UH_DEBUG) - $admin_log-> - e_log_event(10, __FILE__."|".__FUNCTION__."@".__LINE__, "DEBUG", "Upload Handler test", "Legacy return with ".count($ret)." files", FALSE, FALSE); - $messages = handle_upload_messages($ret, FALSE, TRUE); // Show all the error and acknowledgment messages - define(F_MESSAGE, $messages); - - if (count($ret) == 1) - { - if ($ret[0]['error'] != 0) - return FALSE; // Special case if errors - } - return $ret; -} - - - - -//==================================================================== -// VETTING AND UTILITY ROUTINES -//==================================================================== - - -/** - * Check uploaded file to try and identify dodgy content. - * @param string $filename is the full path+name to the uploaded file on the server - * @param string $target_name is the intended name of the file once transferred - * @param array $allowed_filetypes is an array of permitted file extensions, in lower case, no leading '.' - * (usually generated from filetypes.xml/filetypes.php) - * @param boolean|string $unknown - handling of file types unknown to us/define additional types - * if FALSE, rejects totally unknown file extensions (even if in $allowed_filetypes). - * if $unknown is TRUE, accepts totally unknown file extensions. - * otherwise $unknown is a comma-separated list of additional permitted file extensions - * @return boolean|int - TRUE if file acceptable, a numeric 'fail' code if unacceptable: - * 1 - file type not allowed - * 2 - can't read file contents - * 3 - illegal file contents (usually '$f) - { - $file_array[$k] = trim($f); - } - } - - if ($def_file && is_readable(e_ADMIN.$def_file)) - { - $a_filetypes = trim(file_get_contents(e_ADMIN.$def_file)); - $a_filetypes = explode(',', $a_filetypes); - } - foreach ($a_filetypes as $ftype) - { - $ftype = strtolower(trim(str_replace('.', '', $ftype))); - if ($ftype && (!$file_mask || in_array($ftype, $file_array))) - { - $ret[$ftype] = -1; - } - } - return $ret; - } - - - - - /** - * Parse a file size string (e.g. 16M) and compute the simple numeric value. - * Proxy to e_file::file_size_decode(). - * - * @see e_file::file_size_decode() - * @param string $source - input string which may include 'multiplier' characters such as 'M' or 'G'. Converted to 'decoded value' - * @param int $compare - a 'compare' value - * @param string $action - values (gt|lt) - * - * @return int file size value. - * If the decoded value evaluates to zero, returns the value of $compare - * If $action == 'gt', return the larger of the decoded value and $compare - * If $action == 'lt', return the smaller of the decoded value and $compare - */ - function file_size_decode($source, $compare = 0, $action = '') - { - return e107::getFile(true)->file_size_decode($source, $compare, $action); - } - - - - /** - * Get array of file types (file extensions) which are permitted - reads an XML-formatted definition file. - * (Similar to @See{get_allowed_filetypes()}, but expects an XML file) - * - * @param string $def_file - name of an XML-formatted file, which is sought in the E_ADMIN directory - * @param string $file_mask - comma-separated list of allowed file types - only those specified in both $file_mask and $def_file are returned - * - * @return array - where key is the file type (extension); value is max upload size - */ - function get_XML_filetypes($def_file = FALSE, $file_mask = '') - { - $ret = array( - ); - if ($def_file === FALSE) - return $ret; - - if ($file_mask) - { - $file_array = explode(',', $file_mask); - foreach ($file_array as $k=>$f) - { - $file_array[$k] = trim($f); - } - } - - if ($def_file && is_readable(e_SYSTEM.$def_file)) - { - $xml = e107::getXml(); - // class tag should be always array - $xml->setOptArrayTags('class'); - $temp_vars = $xml->loadXMLfile(e_SYSTEM.$def_file, 'filetypes', false); - if ($temp_vars === FALSE) - { - echo "Error reading XML file: {$def_file}
"; - return $ret; - } - foreach ($temp_vars['class'] as $v1) - { - $v = $v1['@attributes']; - if (check_class($v['name'])) - { - $current_perms[$v['name']] = array( - 'type'=>$v['type'], 'maxupload'=>$v['maxupload'] - ); - $a_filetypes = explode(',', $v['type']); - foreach ($a_filetypes as $ftype) - { - $ftype = strtolower(trim(str_replace('.', '', $ftype))); // File extension - - if (!$file_mask || in_array($ftype, $file_array)) - { // We can load this extension - if (isset($ret[$ftype])) - { - $ret[$ftype] = file_size_decode($v['maxupload'], $ret[$ftype], 'gt'); // Use largest value - } - else - { - $ret[$ftype] = file_size_decode($v['maxupload']); - } - } - } - } - } - } - - return $ret; - } - - - - /** - * Calculate 'global' maximum upload size - the maximum before extension-specific restrictions taken into account - * - * @param int $max_up - if > 0, its a global maximum permitted. If < 0, $pref['upload_maxfilesize'] is used (if set) - * - * @return int maximum allowed upload size for file - */ - function calc_max_upload_size($max_up = -1) - { - global $pref,$admin_log; - // Work out maximum allowable file size - if (UH_DEBUG) - { - $admin_log-> - e_log_event(10, __FILE__."|".__FUNCTION__."@".__LINE__, "DEBUG", "Upload Handler test", "File size limits - user set: ".$pref['upload_maxfilesize']." Post_max_size: ".ini_get('post_max_size')." upload_max_size: ".ini_get('upload_max_size'), FALSE, FALSE); - } - $max_upload_size = file_size_decode(ini_get('post_max_size')); - $max_upload_size = file_size_decode(ini_get('upload_max_filesize'), $max_upload_size, 'lt'); - if ($max_up > 0) - { - $max_upload_size = file_size_decode($max_up, $max_upload_size, 'lt'); - } - else - { - if (varset($pref['upload_maxfilesize'], 0) > 0) - $max_upload_size = file_size_decode($pref['upload_maxfilesize'], $max_upload_size, 'lt'); - } - if (UH_DEBUG) - $admin_log-> - e_log_event(10, __FILE__."|".__FUNCTION__."@".__LINE__, "DEBUG", "Upload Handler test", "Final max upload size: {$max_upload_size}", FALSE, FALSE); - return $max_upload_size; - } - - - - /** - * Get an array of permitted filetypes according to a set hierarchy. - * If a specific file name given, that's used. Otherwise the default hierarchy is used - * - * @param string $file_mask - comma-separated list of allowed file types - * @param string $filename - optional override file name - defaults ignored - * - * @return array of filetypes - */ - function get_filetypes($file_mask = FALSE, $filename = '') - { - if ($filename != '') - { - - if (strtolower(substr($filename, -4) == '.xml')) - { - return get_XML_filetypes($filename, $file_mask); - } - return get_allowed_filetypes($filename, $file_mask); - } - - if (is_readable(e_SYSTEM.e_READ_FILETYPES)) - { - return get_XML_filetypes(e_READ_FILETYPES, $file_mask); - } - - if (ADMIN && is_readable(e_ADMIN.'admin_filetypes.php')) - { - return get_allowed_filetypes('admin_filetypes.php', $file_mask); - } - - if (is_readable(e_ADMIN.'filetypes.php')) - { - return get_allowed_filetypes('filetypes.php', $file_mask); - } - return array(); // Just an empty array - } - - - - /** - * Scans the array of allowed file types, updates allowed max size as appropriate. - * If the value is larger than the site-wide maximum, reduces it. - * - * @param array $allowed_filetypes - key is file type (extension), value is maximum size allowed - * @param int $max_upload_size - site-wide maximum file upload size - * - * @return int largest allowed file size across all file types - */ - function set_max_size(&$allowed_filetypes, $max_upload_size) - { - $new_max = 0; - foreach ($allowed_filetypes as $t=>$s) - { - if ($s < 0) - { // Unspecified max - use the global value - $allowed_filetypes[$t] = $max_upload_size; - } - elseif ($allowed_filetypes[$t] > $max_upload_size) - $allowed_filetypes[$t] = $max_upload_size; - if ($allowed_filetypes[$t] > $new_max) - $new_max = $allowed_filetypes[$t]; - } - return $new_max; - } - - - - /* - * Quick routine if all we want is the size of the largest file the current user can upload - * - * @return int largest allowed file size across all file types - */ - function get_user_max_upload() - { - $a_filetypes = get_filetypes(); - if (count($a_filetypes) == 0) - return 0; // Return if no upload allowed - $max_upload_size = calc_max_upload_size(-1); // Find overriding maximum upload size - $max_upload_size = set_max_size($a_filetypes, $max_upload_size); - return $max_upload_size; - } - -?> + + e_log_event(10, debug_backtrace(), "DEBUG", "Upload Handler test", "Process uploads to {$uploaddir}, fileinfo ".$fileinfo, FALSE, LOG_TO_ROLLING); + // $admin_log->e_log_event(10,__FILE__."|".__FUNCTION__."@".__LINE__,"DEBUG","Upload Handler test","Intermediate directory: {$ul_temp_dir} ",FALSE,LOG_TO_ROLLING); + + $overwrite = varset($options['overwrite'], FALSE); + + $uploaddir = realpath($uploaddir); // Mostly to get rid of the grot that might be passed in from legacy code. Also strips any trailing '/' + if (!is_dir($uploaddir)) + { + if (UH_DEBUG) + $admin_log-> + e_log_event(10, __FILE__."|".__FUNCTION__."@".__LINE__, "DEBUG", "Upload Handler test", "Invalid directory: ".$uploaddir, FALSE, FALSE); + return FALSE; // Need a valid directory + } + if (UH_DEBUG) + $admin_log-> + e_log_event(10, __FILE__."|".__FUNCTION__."@".__LINE__, "DEBUG", "Upload Handler test", "Destination directory: ".$uploaddir, FALSE, FALSE); + + $final_chmod = varset($options['final_chmod'], 0644); + + if (isset($options['file_array_name'])) + { + $files = $_FILES[$options['file_array_name']]; + } + else + { + $files = $_FILES['file_userfile']; + } + + $max_file_count = varset($options['max_file_count'], 0); + + if (!is_array($files)) + { + if (UH_DEBUG) + $admin_log-> + e_log_event(10, __FILE__."|".__FUNCTION__."@".__LINE__, "DEBUG", "Upload Handler test", "No files uploaded", FALSE, FALSE); + return FALSE; + } + + $uploaded = array( + ); + + $max_upload_size = calc_max_upload_size(varset($options['max_upload_size'], -1)); // Find overriding maximum upload size + $allowed_filetypes = get_filetypes(varset($options['file_mask'], ''), varset($options['filetypes'], '')); + $max_upload_size = set_max_size($allowed_filetypes, $max_upload_size); + + // That's the basics set up - we can start processing files now + + if (UH_DEBUG) + $admin_log-> + e_log_event(10, __FILE__."|".__FUNCTION__."@".__LINE__, "DEBUG", "Upload Handler test", "Start individual files: ".count($files['name'])." Max upload: ".$max_upload_size, FALSE, FALSE); + + $c = 0; + foreach ($files['name'] as $key=>$name) + { + $first_error = FALSE; // Clear error flag + if (($name != '') || $files['size'][$key]) // Need this check for things like file manager which allow multiple possible uploads + { + $name = preg_replace("/[^a-z0-9._-]/", '', str_replace(' ', '_', str_replace('%20', '_', strtolower($name)))); + $raw_name = $name; // Save 'proper' file name - useful for display + $file_ext = trim(strtolower(substr(strrchr($name, "."), 1))); // File extension - forced to lower case internally + + if (!trim($files['type'][$key])) + $files['type'][$key] = 'Unknowm mime-type'; + + if (UH_DEBUG) + $admin_log-> + e_log_event(10, __FILE__."|".__FUNCTION__."@".__LINE__, "DEBUG", "Upload Handler test", "Process file {$name}, size ".$files['size'][$key], FALSE, FALSE); + + if ($max_file_count && ($c > $max_file_count)) + { + $first_error = 249; // 'Too many files uploaded' error + } + else + { + $first_error = $files['error'][$key]; // Start with whatever error PHP gives us for the file + } + + if (!$first_error) + { // Check file size early on + if ($files['size'][$key] == 0) + { + $first_error = 4; // Standard error code for zero size file + } + elseif ($files['size'][$key] > $max_upload_size) + { + $first_error = 254; + } + elseif (isset($allowed_filetypes[$file_ext]) && ($allowed_filetypes[$file_ext] > 0) && ($files['size'][$key] > $allowed_filetypes[$file_ext])) + { // XML file set limits per extension + $first_error = 254; + } + } + + if (!$first_error) + { + $uploadfile = $files['tmp_name'][$key]; // Name in temporary directory + if (!$uploadfile) + $first_error = 253; + } + + if (!$first_error) + { + // Need to support multiple files with the same 'real' name in some cases + if (strpos($fileinfo, "attachment") === 0) + { // For attachments, add in a prefix plus time and date to give a unique file name + $addbit = explode('+', $fileinfo, 2); + $name = time()."_".USERID."_".trim($addbit[1]).$name; + } + elseif (strpos($fileinfo, "prefix") === 0) + { // For attachments, avatars, photos etc alternatively just add a prefix we've been passed + $addbit = explode('+', $fileinfo, 2); + $name = trim($addbit[1]).$name; + } + + $destination_file = $uploaddir."/".$name; + + if ($fileinfo == "unique" && file_exists($destination_file)) + { // Modify destination name to make it unique - but only if target file name exists + $name = time()."_".$name; + $destination_file = $uploaddir."/".$name; + } + + if (file_exists($destination_file) && !$overwrite) + $first_error = 250; // Invent our own error number - duplicate file + } + + if (!$first_error) + { + $tpos = FALSE; + if ($file_ext != '') // Require any uploaded file to have an extension + { + if ($ul_temp_dir) + { // Need to move file to our own temporary directory + $tempfilename = $uploadfile; + $uploadfile = $ul_temp_dir.basename($uploadfile); + if (UH_DEBUG) + $admin_log-> + e_log_event(10, __FILE__."|".__FUNCTION__."@".__LINE__, "DEBUG", "Upload Handler test", "Move {$tempfilename} to {$uploadfile} ", FALSE, LOG_TO_ROLLING); + @move_uploaded_file($tempfilename, $uploadfile); // This should work on all hosts + } + $tpos = (($file_status = vet_file($uploadfile, $name, $allowed_filetypes, varset($options['extra_file_types'], FALSE))) === TRUE); + } + if ($tpos === FALSE) + { + // File type upload not permitted - error message and abort + $first_error = 251; // Invent our own error number - file type not permitted + } + } + + if (!$first_error) + { // All tests passed - can store it somewhere + $uploaded[$c]['name'] = $name; + $uploaded[$c]['rawname'] = $raw_name; + $uploaded[$c]['type'] = $files['type'][$key]; + $uploaded[$c]['size'] = 0; + $uploaded[$c]['index'] = $key; // Store the actual index from the file_userfile array + + // Store as flat file + if ((!$ul_temp_dir && @move_uploaded_file($uploadfile, $destination_file)) || ($ul_temp_dir && @rename($uploadfile, $destination_file))) // This should work on all hosts + { + @chmod($destination_file, $final_chmod); + if (UH_DEBUG) + $admin_log-> + e_log_event(10, __FILE__."|".__FUNCTION__."@".__LINE__, "DEBUG", "Upload Handler test", "Final chmod() file {$destination_file} to {$final_chmod} ", FALSE, FALSE); + + $uploaded[$c]['size'] = $files['size'][$key]; + if (UH_DEBUG) + $admin_log-> + e_log_event(10, __FILE__."|".__FUNCTION__."@".__LINE__, "DEBUG", "Upload Handler test", "Saved file {$c} OK: ".$uploaded[$c]['name'], FALSE, FALSE); + } + else + { + $first_error = 252; // Error - "couldn't save destination" + } + } + + if (!$first_error) + { // This file succeeded + $uploaded[$c]['message'] = LANUPLOAD_3." '".$raw_name."'"; + $uploaded[$c]['error'] = 0; + } + else + { + $uploaded[$c]['error'] = $first_error; + $uploaded[$c]['size'] = 0; + switch ($first_error) + { + case 1: // Exceeds upload_max_filesize in php.ini + $error = LANUPLOAD_5; + break; + case 2: // Exceeds MAX_FILE_SIZE in form + $error = LANUPLOAD_6; + break; + case 3: // Partial upload + $error = LANUPLOAD_7; + break; + case 4: // No file uploaded + $error = LANUPLOAD_8; + break; + case 5: // Undocumented code (zero file size) + $error = LANUPLOAD_9; + break; + case 6: // Missing temporary folder + $error = LANUPLOAD_13; + break; + case 7: // File write failed + $error = LANUPLOAD_14; + break; + case 8: // Upload stopped by extension + $error = LANUPLOAD_15; + break; + case 249: // Too many files (our error code) + $error = LANUPLOAD_19; + break; + case 250: // Duplicate File (our error code) + $error = LANUPLOAD_10; + break; + case 251: // File type not allowed (our error code) + $error = LANUPLOAD_1." ".$files['type'][$key]." ".LANUPLOAD_2." ({$file_status})"; + break; + case 252: // File uploaded OK, but couldn't save it + $error = LANUPLOAD_4." [".str_replace("../", "", $uploaddir)."]"; + break; + case 253: // Bad name for uploaded file (our error code) + $error = LANUPLOAD_17; + break; + case 254: // file size exceeds allowable limits (our error code) + $error = LANUPLOAD_18; + break; + default: // Shouldn't happen - but at least try and make it obvious if it does! + $error = LANUPLOAD_16; + } + + $uploaded[$c]['message'] = LANUPLOAD_11." '".$name."'
".LANUPLOAD_12.": ".$error; + $uploaded[$c]['line'] = __LINE__; + $uploaded[$c]['file'] = __FILE__; + if (UH_DEBUG) + $admin_log-> + e_log_event(10, __FILE__."|".__FUNCTION__."@".__LINE__, "DEBUG", "Upload Handler test", "Main routine error {$first_error} file {$c}: ".$uploaded[$c]['message'], FALSE, FALSE); + // If we need to abort on first error, do so here - could check for specific error codes + } + if (is_file($uploadfile)) + @unlink($uploadfile); // Don't leave the file on the server if error (although should be auto-deleted) + $c++; + } + } + return $uploaded; +} + + + +/** + * Utility routine to handle the messages returned by process_uploaded_files(). + * @param array $upload_array is the list of uploaded files (as returned by process_uploaded_files()) + * @param boolean $errors_only - if TRUE, no message is shown for a successful upload. + * @param boolean $use_handler - if TRUE, message_handler is used to display the message. + * @return string - a list of all accumulated messages. (Non-destructive call, so can be called several times with different options). + */ + +function handle_upload_messages(&$upload_array, $errors_only = TRUE, $use_handler = FALSE) +{ + // Display error messages, accumulate FMESSAGE + // Write as a separate routine - returns all messages displayed. Option to only display failures. + $f_message = ''; + foreach ($upload_array as $k=>$r) + { + if (!$errors_only || $r['error']) + { + if ($use_handler) + { + require_once (e_HANDLER."message_handler.php"); + message_handler("MESSAGE", $r['message'], $r['line'], $r['file']); + } + $f_message[] = $r['message']; + } + } + return implode("
", $f_message); +} + +/* +//==================================================================== +// LEGACY FILE UPLOAD HANDLER +//==================================================================== + +/** + * This is the 'legacy' interface, which handles various special cases etc. + * It was the only option in E107 0.7.8 and earlier, and is still used in some places in core. + * It also attempts to return in the same way as the original, especially when any errors occur + * + * @param string $uploaddir - target directory for file. Defaults to e_FILE/public + * @param boolean|string $avatar - sets the 'type' or destination of the file: + * FALSE - its a 'general' file + * 'attachment' - indicates an attachment (related to forum post or PM) + * 'unique' - indicates that file name must be unique - new name given (prefixed with time()_ ) + * 'avatar' - indicates an avatar is being uploaded + * @param string $fileinfo - included within the name of the saved file with attachments - can be an identifier of some sort + * (Forum adds 'FT{$tid}_' - where $tid is the thread ID. + * @param boolean $overwrite - if true, an uploaded file can overwrite an existing file of the same name (not used in 0.7 core) + * + * Preference used: + * $pref['upload_storagetype'] - now ignored - used to be 1 for files, 2 for database storage + * + * @return boolean|array - For backward compatibility, returns FALSE if only one file uploaded and an error; + * otherwise returns an array with per-file error codes as appropriate. + * On exit, F_MESSAGE is defined with the success/failure message(s) that have been displayed - one file per line + */ + +function file_upload($uploaddir, $avatar = FALSE, $fileinfo = "", $overwrite = "") +{ + $admin_log = e107::getAdminLog(); + $options = array( + 'extra_file_types'=>TRUE + ); // As default, allow any filetype enabled in filetypes.php + + if (!$uploaddir) + { + $uploaddir = e_UPLOAD; + } + + if (strpos($avatar, '=') !== FALSE) + { + list($avatar, $param) = explode('=', $avatar, 2); + } + else + { + $param = USERID; + } + switch ($avatar) + { + case 'attachment': + $avatar = "attachment+".$fileinfo; + break; + case 'avatar': + $avatar = 'prefix+ap_'.$param.'_'; // Prefix unique to user + $options['overwrite'] = TRUE; // Allow update of avatar with same file name + break; + } + + if (UH_DEBUG) + $admin_log-> + e_log_event(10, __FILE__."|".__FUNCTION__."@".__LINE__, "DEBUG", "Upload Handler test", "Legacy call, directory ".$uploaddir, FALSE, FALSE); + + $ret = process_uploaded_files(getcwd()."/".$uploaddir, $avatar, $options); // Well, that's the way it was done before + + if ($ret === FALSE) + { + if (UH_DEBUG) + $admin_log-> + e_log_event(10, __FILE__."|".__FUNCTION__."@".__LINE__, "DEBUG", "Upload Handler test", "Legacy return FALSE", FALSE, FALSE); + return FALSE; + } + + if (UH_DEBUG) + $admin_log-> + e_log_event(10, __FILE__."|".__FUNCTION__."@".__LINE__, "DEBUG", "Upload Handler test", "Legacy return with ".count($ret)." files", FALSE, FALSE); + $messages = handle_upload_messages($ret, FALSE, TRUE); // Show all the error and acknowledgment messages + define(F_MESSAGE, $messages); + + if (count($ret) == 1) + { + if ($ret[0]['error'] != 0) + return FALSE; // Special case if errors + } + return $ret; +} + + + + +//==================================================================== +// VETTING AND UTILITY ROUTINES +//==================================================================== + + +/** + * Check uploaded file to try and identify dodgy content. + * @param string $filename is the full path+name to the uploaded file on the server + * @param string $target_name is the intended name of the file once transferred + * @param array $allowed_filetypes is an array of permitted file extensions, in lower case, no leading '.' + * (usually generated from filetypes.xml/filetypes.php) + * @param boolean|string $unknown - handling of file types unknown to us/define additional types + * if FALSE, rejects totally unknown file extensions (even if in $allowed_filetypes). + * if $unknown is TRUE, accepts totally unknown file extensions. + * otherwise $unknown is a comma-separated list of additional permitted file extensions + * @return boolean|int - TRUE if file acceptable, a numeric 'fail' code if unacceptable: + * 1 - file type not allowed + * 2 - can't read file contents + * 3 - illegal file contents (usually '$f) + { + $file_array[$k] = trim($f); + } + } + + if ($def_file && is_readable(e_ADMIN.$def_file)) + { + $a_filetypes = trim(file_get_contents(e_ADMIN.$def_file)); + $a_filetypes = explode(',', $a_filetypes); + } + foreach ($a_filetypes as $ftype) + { + $ftype = strtolower(trim(str_replace('.', '', $ftype))); + if ($ftype && (!$file_mask || in_array($ftype, $file_array))) + { + $ret[$ftype] = -1; + } + } + return $ret; + } + + + + + /** + * Parse a file size string (e.g. 16M) and compute the simple numeric value. + * Proxy to e_file::file_size_decode(). + * + * @see e_file::file_size_decode() + * @param string $source - input string which may include 'multiplier' characters such as 'M' or 'G'. Converted to 'decoded value' + * @param int $compare - a 'compare' value + * @param string $action - values (gt|lt) + * + * @return int file size value. + * If the decoded value evaluates to zero, returns the value of $compare + * If $action == 'gt', return the larger of the decoded value and $compare + * If $action == 'lt', return the smaller of the decoded value and $compare + */ + function file_size_decode($source, $compare = 0, $action = '') + { + return e107::getFile(true)->file_size_decode($source, $compare, $action); + } + + + + /** + * Get array of file types (file extensions) which are permitted - reads an XML-formatted definition file. + * (Similar to @See{get_allowed_filetypes()}, but expects an XML file) + * + * @param string $def_file - name of an XML-formatted file, which is sought in the E_ADMIN directory + * @param string $file_mask - comma-separated list of allowed file types - only those specified in both $file_mask and $def_file are returned + * + * @return array - where key is the file type (extension); value is max upload size + */ + function get_XML_filetypes($def_file = FALSE, $file_mask = '') + { + $ret = array( + ); + if ($def_file === FALSE) + return $ret; + + if ($file_mask) + { + $file_array = explode(',', $file_mask); + foreach ($file_array as $k=>$f) + { + $file_array[$k] = trim($f); + } + } + + if ($def_file && is_readable(e_SYSTEM.$def_file)) + { + $xml = e107::getXml(); + // class tag should be always array + $xml->setOptArrayTags('class'); + $temp_vars = $xml->loadXMLfile(e_SYSTEM.$def_file, 'filetypes', false); + if ($temp_vars === FALSE) + { + echo "Error reading XML file: {$def_file}
"; + return $ret; + } + foreach ($temp_vars['class'] as $v1) + { + $v = $v1['@attributes']; + if (check_class($v['name'])) + { + $current_perms[$v['name']] = array( + 'type'=>$v['type'], 'maxupload'=>$v['maxupload'] + ); + $a_filetypes = explode(',', $v['type']); + foreach ($a_filetypes as $ftype) + { + $ftype = strtolower(trim(str_replace('.', '', $ftype))); // File extension + + if (!$file_mask || in_array($ftype, $file_array)) + { // We can load this extension + if (isset($ret[$ftype])) + { + $ret[$ftype] = file_size_decode($v['maxupload'], $ret[$ftype], 'gt'); // Use largest value + } + else + { + $ret[$ftype] = file_size_decode($v['maxupload']); + } + } + } + } + } + } + + return $ret; + } + + + + /** + * Calculate 'global' maximum upload size - the maximum before extension-specific restrictions taken into account + * + * @param int $max_up - if > 0, its a global maximum permitted. If < 0, $pref['upload_maxfilesize'] is used (if set) + * + * @return int maximum allowed upload size for file + */ + function calc_max_upload_size($max_up = -1) + { + global $pref; + $admin_log = e107::getAdminLog(); + // Work out maximum allowable file size + if (UH_DEBUG) + { + $admin_log-> + e_log_event(10, __FILE__."|".__FUNCTION__."@".__LINE__, "DEBUG", "Upload Handler test", "File size limits - user set: ".$pref['upload_maxfilesize']." Post_max_size: ".ini_get('post_max_size')." upload_max_size: ".ini_get('upload_max_size'), FALSE, FALSE); + } + $max_upload_size = file_size_decode(ini_get('post_max_size')); + $max_upload_size = file_size_decode(ini_get('upload_max_filesize'), $max_upload_size, 'lt'); + if ($max_up > 0) + { + $max_upload_size = file_size_decode($max_up, $max_upload_size, 'lt'); + } + else + { + if (varset($pref['upload_maxfilesize'], 0) > 0) + $max_upload_size = file_size_decode($pref['upload_maxfilesize'], $max_upload_size, 'lt'); + } + if (UH_DEBUG) + $admin_log-> + e_log_event(10, __FILE__."|".__FUNCTION__."@".__LINE__, "DEBUG", "Upload Handler test", "Final max upload size: {$max_upload_size}", FALSE, FALSE); + return $max_upload_size; + } + + + + /** + * Get an array of permitted filetypes according to a set hierarchy. + * If a specific file name given, that's used. Otherwise the default hierarchy is used + * + * @param string $file_mask - comma-separated list of allowed file types + * @param string $filename - optional override file name - defaults ignored + * + * @return array of filetypes + */ + function get_filetypes($file_mask = FALSE, $filename = '') + { + if ($filename != '') + { + + if (strtolower(substr($filename, -4) == '.xml')) + { + return get_XML_filetypes($filename, $file_mask); + } + return get_allowed_filetypes($filename, $file_mask); + } + + if (is_readable(e_SYSTEM.e_READ_FILETYPES)) + { + return get_XML_filetypes(e_READ_FILETYPES, $file_mask); + } + + if (ADMIN && is_readable(e_ADMIN.'admin_filetypes.php')) + { + return get_allowed_filetypes('admin_filetypes.php', $file_mask); + } + + if (is_readable(e_ADMIN.'filetypes.php')) + { + return get_allowed_filetypes('filetypes.php', $file_mask); + } + return array(); // Just an empty array + } + + + + /** + * Scans the array of allowed file types, updates allowed max size as appropriate. + * If the value is larger than the site-wide maximum, reduces it. + * + * @param array $allowed_filetypes - key is file type (extension), value is maximum size allowed + * @param int $max_upload_size - site-wide maximum file upload size + * + * @return int largest allowed file size across all file types + */ + function set_max_size(&$allowed_filetypes, $max_upload_size) + { + $new_max = 0; + foreach ($allowed_filetypes as $t=>$s) + { + if ($s < 0) + { // Unspecified max - use the global value + $allowed_filetypes[$t] = $max_upload_size; + } + elseif ($allowed_filetypes[$t] > $max_upload_size) + $allowed_filetypes[$t] = $max_upload_size; + if ($allowed_filetypes[$t] > $new_max) + $new_max = $allowed_filetypes[$t]; + } + return $new_max; + } + + + + /* + * Quick routine if all we want is the size of the largest file the current user can upload + * + * @return int largest allowed file size across all file types + */ + function get_user_max_upload() + { + $a_filetypes = get_filetypes(); + if (count($a_filetypes) == 0) + return 0; // Return if no upload allowed + $max_upload_size = calc_max_upload_size(-1); // Find overriding maximum upload size + $max_upload_size = set_max_size($a_filetypes, $max_upload_size); + return $max_upload_size; + } + +?> diff --git a/e107_plugins/login_menu/login_menu_class.php b/e107_plugins/login_menu/login_menu_class.php index d70fc091c..ea976eeba 100644 --- a/e107_plugins/login_menu/login_menu_class.php +++ b/e107_plugins/login_menu/login_menu_class.php @@ -2,7 +2,7 @@ /* * e107 website system * - * Copyright (C) 2008-2009 e107 Inc (e107.org) + * Copyright (C) 2008-2013 e107 Inc (e107.org) * Released under the terms and conditions of the * GNU General Public License (http://www.gnu.org/licenses/gpl.txt) * @@ -171,7 +171,7 @@ class login_menu_class function get_forum_stats($get_stats=true) { - global $sql; + $sql = e107::getDb(); if(!e107::isInstalled('forum')) return array(); @@ -204,7 +204,7 @@ class login_menu_class function get_chatbox_menu_stats() { - global $sql; + $sql = e107::getDb(); if(!e107::isInstalled('chatbox_menu')) return array(); @@ -339,14 +339,16 @@ class login_menu_class /** - * @todo - $plugin_path is undefined - needs to be obtained from somewhere + * Get data for a plugin + * + * @param string $plugid - name (= base directory) of the required plugin */ function get_plugin_data($plugid) { if(($tmp = getcachedvars('loginbox_eplug_data_'.$plugid)) !== FALSE) return $tmp; $ret = array(); - if (is_readable(e_PLUGIN.vartrue($plugin_path).'/plugin.xml')) + if (is_readable(e_PLUGIN.$plugid.'/plugin.xml')) { require_once(e_HANDLER.'xml_class.php'); $xml = new xmlClass;