/** * Try and get a good .htaccess file built. * @param resource FTP connection to server */ function test_htaccess($conn) { $clauses = array(); $clauses[] = <<<END # Disable inaccurate security scanning (ocPortal has it's own) <IfModule mod_security.c> SecFilterEngine Off SecFilterScanPOST Off </IfModule> END; $php_value_ok = substr(ocp_srv('SERVER_SOFTWARE'), 0, 10) != 'LightSpeed'; if ($php_value_ok) { $clauses[] = <<<END # ocPortal needs uploads; many hosts leave these low php_value post_max_size "16M" php_value upload_max_filesize "16M" END; } if ($php_value_ok) { $clauses[] = <<<END # Turn insecure things off php_flag allow_url_fopen off END; } if ($php_value_ok) { $clauses[] = <<<END php_flag register_globals off END; } if ($php_value_ok) { $clauses[] = <<<END php_value max_input_vars "2000" php_value mbstring.func_overload "0" # Suhosin can cause problems on configuration and Catalogue forms, which use a lot of fields php_value suhosin.post.max_vars "2000" php_value suhosin.request.max_vars "2000" php_value suhosin.cookie.max_vars "400" php_value suhosin.cookie.max_name_length "150" php_value suhosin.post.max_value_length "100000000" php_value suhosin.request.max_value_length "100000000" php_value suhosin.post.max_totalname_length "10000" php_value suhosin.request.max_totalname_length "10000" php_flag suhosin.cookie.encrypt off php_flag suhosin.sql.union off END; } if ($php_value_ok) { $clauses[] = <<<END # Put some limits up. ocPortal is stable enough not to cause problems- it'll only use higher limits when it really needs them php_value memory_limit "128M" END; } if ($php_value_ok) { $clauses[] = <<<END php_value max_input_time "60" END; } /*// NB: This'll only work in PHP6+ Bad idea, will miss temp directory $file_base=$GLOBALS['FILE_BASE']; $clauses[]=<<<END # Sandbox ocPortal to it's own directory php_value open_basedir "{$file_base}" END; */ $clauses[] = <<<END Options +FollowSymLinks END; $clauses[] = <<<END RewriteEngine on # Redirect away from modules called directly by URL. Helpful as it allows you to "run" a module file in a debugger and still see it running. RewriteRule ^([^=]*)webdav.php/([^=]*)pages/(modules|modules\\_custom)/([^/]*)\\.php\$ - [L] RewriteRule ^([^=]*)pages/(modules|modules\\_custom)/([^/]*)\\.php\$ \$1index.php\\?page=\$3 [L,QSA,R] # These have a specially reduced form (no need to make it too explicit that these are CEDI) # We shouldn't shorten them too much, or the actual zone or base url might conflict RewriteRule ^([^=]*)pg/s/([^\\&\\?]*)/index\\.php\$ \$1index.php\\?page=cedi&id=\$2 [L,QSA] # These have a specially reduce form (wide is implied) RewriteRule ^([^=]*)pg/galleries/image/([^\\&\\?]*)/index\\.php(.*)\$ \$1index.php\\?page=galleries&type=image&id=\$2&wide=1\$3 [L,QSA] RewriteRule ^([^=]*)pg/galleries/video/([^\\&\\?]*)/index\\.php(.*)\$ \$1index.php\\?page=galleries&type=video&id=\$2&wide=1\$3 [L,QSA] RewriteRule ^([^=]*)pg/iotds/view/([^\\&\\?]*)/index\\.php(.*)\$ \$1index.php\\?page=iotds&type=view&id=\$2&wide=1\$3 [L,QSA] # These are standard patterns RewriteRule ^([^=]*)pg/([^/\\&\\?]*)/([^/\\&\\?]*)/([^\\&\\?]*)/index\\.php(.*)\$ \$1index.php\\?page=\$2&type=\$3&id=\$4\$5 [L,QSA] RewriteRule ^([^=]*)pg/([^/\\&\\?]*)/([^/\\&\\?]*)/index\\.php(.*)\$ \$1index.php\\?page=\$2&type=\$3\$4 [L,QSA] RewriteRule ^([^=]*)pg/([^/\\&\\?]*)/index\\.php(.*)\$ \$1index.php\\?page=\$2\$3 [L,QSA] # This one is weird... apache strips out // and turns to /, thus requiring an extra pattern... RewriteRule ^([^=]*)pg/index\\.php(.*)\$ \$1index.php\\?page=\$3 [L,QSA] # Now the same, but without any additional parameters (and thus no index.php) RewriteRule ^([^=]*)pg/s/([^\\&\\?]*)\$ \$1index.php\\?page=cedi&id=\$2 [L,QSA] RewriteRule ^([^=]*)pg/galleries/image/([^\\&\\?]*)\$ \$1index.php\\?page=galleries&type=image&id=\$2&wide=1\$3 [L,QSA] RewriteRule ^([^=]*)pg/galleries/video/([^\\&\\?]*)\$ \$1index.php\\?page=galleries&type=video&id=\$2&wide=1\$3 [L,QSA] RewriteRule ^([^=]*)pg/iotds/view/([^\\&\\?]*)\$ \$1index.php\\?page=iotds&type=view&id=\$2&wide=1 [L,QSA] RewriteRule ^([^=]*)pg/([^/\\&\\?]*)/([^/\\&\\?]*)/([^\\&\\?]*)/\$ \$1index.php\\?page=\$2&type=\$3&id=\$4 [L,QSA] RewriteRule ^([^=]*)pg/([^/\\&\\?]*)/([^/\\&\\?]*)/([^\\&\\?]*)\$ \$1index.php\\?page=\$2&type=\$3&id=\$4 [L,QSA] RewriteRule ^([^=]*)pg/([^/\\&\\?]*)/([^/\\&\\?]*)\$ \$1index.php\\?page=\$2&type=\$3 [L,QSA] RewriteRule ^([^=]*)pg/([^/\\&\\?]*)\$ \$1index.php\\?page=\$2 [L,QSA] # And these for those nasty situations where index.php was missing and we couldn't do anything about it (usually due to keep_session creeping into a semi-cached URL) RewriteRule ^([^=]*)pg/s/([^\\&\\?\\.]*)&(.*)\$ \$1index.php\\?\$3&page=cedi&id=\$2 [L,QSA] RewriteRule ^([^=]*)pg/galleries/image/([^/\\&\\?\\.]*)&(.*)\$ \$1index.php\\?\$5&page=galleries&type=image&id=\$2&wide=1&\$3 [L,QSA] RewriteRule ^([^=]*)pg/galleries/video/([^/\\&\\?\\.]*)&(.*)\$ \$1index.php\\?\$5&page=galleries&type=video&id=\$2&wide=1&\$3 [L,QSA] RewriteRule ^([^=]*)pg/iotds/view/([^/\\&\\?\\.]*)&(.*)\$ \$1index.php\\?\$3&page=iotds&type=view&id=\$2&wide=1 [L,QSA] RewriteRule ^([^=]*)pg/([^/\\&\\?\\.]*)/([^/\\&\\?\\.]*)/([^/\\&\\?\\.]*)&(.*)\$ \$1index.php\\?\$5&page=\$2&type=\$3&id=\$4 [L,QSA] RewriteRule ^([^=]*)pg/([^/\\&\\?\\.]*)/([^/\\&\\?\\.]*)&(.*)\$ \$1index.php\\?\$4&page=\$2&type=\$3 [L,QSA] RewriteRule ^([^=]*)pg/([^/\\&\\?\\.]*)&(.*)\$ \$1index.php\\?\$3&page=\$2 [L,QSA] # These have a specially reduced form (no need to make it too explicit that these are CEDI) # We shouldn't shorten them too much, or the actual zone or base url might conflict RewriteRule ^(site|forum|adminzone|cms|collaboration)/s/([^\\&\\?]*)\\.htm\$ \$1/index.php\\?page=cedi&id=\$2 [L,QSA] RewriteRule ^s/([^\\&\\?]*)\\.htm\$ index\\.php\\?page=cedi&id=\$1 [L,QSA] # These have a specially reduce form (wide is implied) RewriteRule ^(site|forum|adminzone|cms|collaboration)/galleries/image/([^\\&\\?]*)\\.htm\$ \$1/index.php\\?page=galleries&type=image&id=\$2&wide=1 [L,QSA] RewriteRule ^(site|forum|adminzone|cms|collaboration)/galleries/video/([^\\&\\?]*)\\.htm\$ \$1/index.php\\?page=galleries&type=video&id=\$2&wide=1 [L,QSA] RewriteRule ^(site|forum|adminzone|cms|collaboration)/iotds/view/([^\\&\\?]*)\\.htm\$ \$1/index.php\\?page=iotds&type=view&id=\$2&wide=1 [L,QSA] # These are standard patterns RewriteRule ^(site|forum|adminzone|cms|collaboration)/([^/\\&\\?]+)/([^/\\&\\?]*)/([^\\&\\?]*)\\.htm\$ \$1/index.php\\?page=\$2&type=\$3&id=\$4 [L,QSA] RewriteRule ^(site|forum|adminzone|cms|collaboration)/([^/\\&\\?]+)/([^/\\&\\?]*)\\.htm\$ \$1/index.php\\?page=\$2&type=\$3 [L,QSA] RewriteRule ^(site|forum|adminzone|cms|collaboration)/([^/\\&\\?]+)\\.htm\$ \$1/index.php\\?page=\$2 [L,QSA] RewriteRule ^([^/\\&\\?]+)/([^/\\&\\?]*)/([^\\&\\?]*)\\.htm\$ index.php\\?page=\$1&type=\$2&id=\$3 [L,QSA] RewriteRule ^([^/\\&\\?]+)/([^/\\&\\?]*)\\.htm\$ index.php\\?page=\$1&type=\$2 [L,QSA] RewriteRule ^([^/\\&\\?]+)\\.htm\$ index.php\\?page=\$1 [L,QSA] END; $clauses[] = <<<END order allow,deny # IP bans go here (leave this comment here! If this file is writeable, ocPortal will write in IP bans below, in sync with it's own DB-based banning - this makes DOS/hack attack prevention stronger) # deny from xxx.xx.x.x (leave this comment here!) allow from all END; $base = dirname(ocp_srv('PHP_SELF')); $clauses[] = <<<END <FilesMatch !"\\.(jpg|jpeg|gif|png|ico)\$"> ErrorDocument 404 {$base}/index.php?page=404 </FilesMatch> END; if (is_writable_wrap(get_file_base() . '/exports/mods') && (!file_exists(get_file_base() . '/.htaccess') || trim(file_get_contents(get_file_base() . '/.htaccess')) == '')) { global $HTTP_MESSAGE; $domain = ocp_srv('HTTP_HOST'); if (substr($domain, 0, 4) == 'www.') { $domain = substr($domain, 4); } $colon_pos = strpos($domain, ':'); if ($colon_pos !== false) { $domain = substr($domain, 0, $colon_pos); } $pos = strpos(ocp_srv('PHP_SELF'), 'install.php'); if ($pos === false) { $pos = strlen(ocp_srv('PHP_SELF')); } else { $pos--; } $port = ocp_srv('SERVER_PORT'); if ($port == '' || $port == '80' || $port == '443') { $port = ''; } else { $port = ':' . $port; } $base_url = post_param('base_url', 'http://' . $domain . $port . substr(ocp_srv('PHP_SELF'), 0, $pos)); if (substr($base_url, -1) == '/') { $base_url = substr($base_url, 0, strlen($base_url) - 1); } foreach ($clauses as $i => $clause) { $myfile = fopen(get_file_base() . '/exports/mods/index.php', 'wt'); fwrite($myfile, "<" . "?php\n\t\t\t@header('Expires: Mon, 20 Dec 1998 01:00:00 GMT');\n\t\t\t@header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');\n\t\t\t@header('Pragma: no-cache'); // for proxies, and also IE\n\t\t\t"); fclose($myfile); $myfile = fopen(get_file_base() . '/exports/mods/.htaccess', 'wt'); fwrite($myfile, $clause); fclose($myfile); $HTTP_MESSAGE = ''; http_download_file($base_url . '/exports/mods/index.php', NULL, false); if ($HTTP_MESSAGE != '200') { $clauses[$i] = NULL; } unlink(get_file_base() . '/exports/mods/.htaccess'); } $out = ''; foreach ($clauses as $i => $clause) { if (!is_null($clause)) { $out .= $clause . chr(10) . chr(10); } } if (is_suexec_like()) { @unlink(get_file_base() . '/.htaccess'); $tmp = fopen(get_file_base() . '/.htaccess', 'wb'); fwrite($tmp, $out); fclose($tmp); } else { @ftp_delete($conn, '.htaccess'); $tmp = fopen(get_file_base() . '/ocp_inst_tmp/tmp', 'wb'); fwrite($tmp, $out); fclose($tmp); @ftp_put($conn, '.htaccess', get_file_base() . '/ocp_inst_tmp/tmp', FTP_TEXT); @ftp_site($conn, 'CHMOD 644 .htaccess'); } } }
/** * Translate truth about needing world write access to a file to absolute permissions. * * @param boolean Whether world file access is required. * @param ID_TEXT The file type (blank: don't care). * @return integer The absolute permission. */ function _translate_file_access($world_access, $file_type = '') { $mask = 0; if ($file_type == 'php') { $php_perms = fileperms(get_file_base() . '/index.php'); if (($php_perms & 0100) == 0100) { $mask = $mask | 0100; } if (($php_perms & 010) == 010) { $mask = $mask | 010; } if (($php_perms & 01) == 01) { $mask = $mask | 01; } } if (is_suexec_like()) { return 0644 | $mask; } if (_ftp_info() === false) { return 0666 | $mask; } // We want the FTP user to be able to delete.. otherwise it gets awkward for them return ($world_access ? 0666 : 0644) | $mask; }
/** * Ensure that the specified file/folder is writeable for the FTP user (so that it can be deleted by the system), and should be called whenever a file is uploaded/created, or a folder is made. We call this function assuming we are giving world permissions * * @param PATH The full pathname to the file/directory * @param integer The permissions to make (not the permissions are reduced if the function finds that the file is owned by the web user [doesn't need world permissions then]) */ function fix_permissions($path, $perms = 0666) { // If the file user is different to the FTP user, we need to make it world writeable if (!is_suexec_like() || ocp_srv('REQUEST_METHOD') == '') { @chmod($path, $perms); } else { if ($perms == 0666) { @chmod($path, 0644); } elseif ($perms == 0777) { @chmod($path, 0755); } else { @chmod($path, $perms); } } global $_CREATED_FILES; // From ocProducts PHP version, for development testing if (isset($_CREATED_FILES)) { foreach ($_CREATED_FILES as $i => $x) { if ($x == $path) { unset($_CREATED_FILES[$i]); } } } }
/** * Output a login page. * * @param ?string Error message (NULL: none) */ function up_do_login($message = NULL) { $type = get_param('type', 'misc'); global $SITE_INFO; $ftp_username = get_value('ftp_username'); $ftp_folder = get_value('ftp_directory'); $ftp_domain = get_value('ftp_domain'); if (is_null($ftp_domain)) { $ftp_domain = array_key_exists('ftp_domain', $SITE_INFO) ? $SITE_INFO['ftp_domain'] : 'localhost'; } if (is_null($ftp_username)) { if (!array_key_exists('ftp_username', $SITE_INFO)) { if (function_exists('posix_getpwuid') && strpos(@ini_get('disable_functions'), 'posix_getpwuid') === false) { $u_info = posix_getpwuid(fileowner(get_file_base() . '/index.php')); $ftp_username = $u_info['name']; } else { $ftp_username = ''; } if (is_null($ftp_username)) { $ftp_username = ''; } } else { $ftp_username = $SITE_INFO['ftp_username']; } } if (is_null($ftp_folder)) { if (!array_key_exists('ftp_folder', $SITE_INFO)) { $dr = array_key_exists('DOCUMENT_ROOT', $_SERVER) ? $_SERVER['DOCUMENT_ROOT'] : (array_key_exists('DOCUMENT_ROOT', $_ENV) ? $_ENV['DOCUMENT_ROOT'] : ''); if (strpos($dr, '/') !== false) { $dr_parts = explode('/', $dr); } else { $dr_parts = explode('\\', $dr); } $webdir_stub = $dr_parts[count($dr_parts) - 1]; $pos = strpos($_SERVER['PHP_SELF'], 'upgrader.php'); if ($pos === false) { $pos = strlen($_SERVER['PHP_SELF']); } else { $pos--; } $ftp_folder = '/' . $webdir_stub . substr($_SERVER['PHP_SELF'], 0, $pos); } else { $ftp_folder = $SITE_INFO['ftp_folder']; } } require_lang('installer'); $l_password = do_lang('MASTER_PASSWORD'); $l_ftp_info = do_lang('FU_FTP_INFO'); $l_ftp_domain = do_lang('FTP_DOMAIN'); $l_ftp_directory = do_lang('FTP_DIRECTORY'); $l_ftp_username = do_lang('FTP_USERNAME'); $l_ftp_password = do_lang('FTP_PASSWORD'); $l_login = do_lang('_LOGIN'); $l_login_info = do_lang('FU_LOGIN_INFO'); $l_login_info_pass_forget = do_lang('FU_LOGIN_INFO_PASS_FORGET'); $l_login_forgot_password_q = do_lang('FU_LOGIN_FORGOT_PASSWORD_Q'); if (!is_null($message)) { echo '<p><strong>' . $message . '</strong></p>'; } $news_id = get_param_integer('news_id', NULL); $url = "upgrader.php?type=" . escape_html($type); if (get_param_integer('keep_safe_mode', 0) == 1) { $url .= '&keep_safe_mode=1'; } if (get_param_integer('keep_show_loading', 0) == 1) { $url .= '&keep_show_loading=1'; } echo "\n\t<p>{$l_login_info}</p>\n\t<form title=\"{$l_login}\" action=\"" . escape_html($url) . "\" method=\"post\">\n\t" . (is_null($news_id) ? '' : '<input type="hidden" name="news_id" value="' . strval($news_id) . '" />') . "\n\t<p>\n\t\t{$l_password}: <input type=\"password\" name=\"given_password\" value=\"" . escape_html(post_param('password', '')) . "\" />\n\t</p>\n\t"; require_code('files'); if (is_suexec_like() || !function_exists('ftp_ssl_connect') && !function_exists('ftp_connect')) { } else { echo "\n\t\t<hr />\n\t\t{$l_ftp_info}\n\t\t<table>\n\t\t\t<tr><th>{$l_ftp_domain}:</th><td><input size=\"50\" type=\"text\" name=\"ftp_domain\" value=\"" . escape_html($ftp_domain) . "\" /></td></tr>\n\t\t\t<tr><th>{$l_ftp_directory}:</th><td><input size=\"50\" type=\"text\" name=\"ftp_folder\" value=\"" . escape_html($ftp_folder) . "\" /></td></tr>\n\t\t\t<tr><th>{$l_ftp_username}:</th><td><input size=\"50\" type=\"text\" name=\"ftp_username\" value=\"" . escape_html($ftp_username) . "\" /></td></tr>\n\t\t\t<tr><th>{$l_ftp_password}:</th><td><input size=\"50\" type=\"password\" name=\"ftp_password\" /></td></tr>\n\t\t</table>\n\t\t<hr />\n\t\t"; } echo "\n\t<p>\n\t\t<input type=\"submit\" value=\"{$l_login}\" />\n\t</p>\n\t</form>\n\t"; echo "\n\t<hr />\n\t<div style=\"font-size: 0.8em\">\n\t<h2>{$l_login_forgot_password_q}</h2>\n\t<p>{$l_login_info_pass_forget}</p>\n\t</div>\n\t"; }