001/** 002 * 003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved. 004 * 005 * This library is free software; you can redistribute it and/or 006 * modify it under the terms of the GNU Lesser General Public 007 * License as published by the Free Software Foundation; either 008 * version 2.1 of the License, or (at your option) any later version. 009 * 010 * This library is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013 * Lesser General Public License for more details. 014 * 015 * You should have received a copy of the GNU Lesser General Public 016 * License along with this library. If not, see <http://www.gnu.org/licenses/>. 017 * 018 **/ 019package lucee.runtime.config; 020 021import java.io.IOException; 022import java.io.InputStream; 023import java.util.ArrayList; 024import java.util.Iterator; 025import java.util.List; 026import java.util.Map; 027import java.util.Map.Entry; 028 029import javax.servlet.ServletConfig; 030import javax.servlet.ServletContext; 031 032import lucee.commons.digest.MD5; 033import lucee.commons.io.IOUtil; 034import lucee.commons.io.SystemUtil; 035import lucee.commons.io.res.Resource; 036import lucee.commons.io.res.util.ResourceUtil; 037import lucee.commons.lang.StringUtil; 038import lucee.runtime.Mapping; 039import lucee.runtime.PageContext; 040import lucee.runtime.cache.tag.CacheHandlerFactoryCollection; 041import lucee.runtime.exp.SecurityException; 042import lucee.runtime.listener.ApplicationListener; 043import lucee.runtime.listener.ClassicAppListener; 044import lucee.runtime.listener.MixedAppListener; 045import lucee.runtime.listener.ModernAppListener; 046import lucee.runtime.listener.NoneAppListener; 047import lucee.runtime.net.http.ReqRspUtil; 048import lucee.runtime.security.SecurityManager; 049import lucee.runtime.type.Collection.Key; 050import lucee.runtime.type.Struct; 051import lucee.runtime.type.util.ArrayUtil; 052 053 054/** 055 * 056 */ 057public final class ConfigWebUtil { 058 059 /** 060 * touch a file object by the string definition 061 * @param config 062 * @param directory 063 * @param path 064 * @param type 065 * @return matching file 066 */ 067 public static Resource getFile(Config config, Resource directory,String path, short type) { 068 path=replacePlaceholder(path,config); 069 if(!StringUtil.isEmpty(path,true)) { 070 Resource file=getFile(directory.getRealResource(path),type); 071 if(file!=null) return file; 072 073 file=getFile(config.getResource(path),type); 074 075 if(file!=null) return file; 076 } 077 return null; 078 } 079 080 /** 081 * generate a file object by the string definition 082 * @param rootDir 083 * @param strDir 084 * @param defaultDir 085 * @param configDir 086 * @param type 087 * @param config 088 * @return file 089 */ 090 static Resource getFile(Resource rootDir,String strDir, String defaultDir,Resource configDir, short type, ConfigImpl config) { 091 strDir=replacePlaceholder(strDir,config); 092 if(!StringUtil.isEmpty(strDir,true)) { 093 Resource res; 094 if(strDir.indexOf("://")!=-1){ // TODO better impl. 095 res=getFile(config.getResource(strDir),type); 096 if(res!=null) return res; 097 } 098 res=getFile(rootDir.getRealResource(strDir),type); 099 if(res!=null) return res; 100 101 res=getFile(config.getResource(strDir),type); 102 if(res!=null) return res; 103 } 104 if(defaultDir==null) return null; 105 Resource file=getFile(configDir.getRealResource(defaultDir),type); 106 return file; 107 } 108 109 public static String replacePlaceholder(String str, Config config) { 110 if(StringUtil.isEmpty(str)) return str; 111 112 if(StringUtil.startsWith(str,'{')){ 113 114 // Config Server 115 if(str.startsWith("{lucee-config") || str.startsWith("{railo-config")) { 116 if(str.startsWith("}",13)) str=checkResult(str,config.getConfigDir().getReal(str.substring(14))); 117 else if(str.startsWith("-dir}",13)) str=checkResult(str,config.getConfigDir().getReal(str.substring(18))); 118 else if(str.startsWith("-directory}",13)) str=checkResult(str,config.getConfigDir().getReal(str.substring(24))); 119 } 120 121 122 else if(config!=null && (str.startsWith("{lucee-server") || str.startsWith("{railo-server"))) { 123 Resource dir=config instanceof ConfigWeb?((ConfigWeb)config).getConfigServerDir():config.getConfigDir(); 124 //if(config instanceof ConfigServer && cs==null) cs=(ConfigServer) cw; 125 if(dir!=null) { 126 if(str.startsWith("}",13)) str=checkResult(str,dir.getReal(str.substring(14))); 127 else if(str.startsWith("-dir}",13)) str=checkResult(str,dir.getReal(str.substring(18))); 128 else if(str.startsWith("-directory}",13)) str=checkResult(str,dir.getReal(str.substring(24))); 129 } 130 } 131 // Config Web 132 else if(str.startsWith("{lucee-web") || str.startsWith("{railo-web")) { 133 if(str.startsWith("}",10)) str=checkResult(str,config.getConfigDir().getReal(str.substring(11))); 134 else if(str.startsWith("-dir}",10)) str=checkResult(str,config.getConfigDir().getReal(str.substring(15))); 135 else if(str.startsWith("-directory}",10)) str=checkResult(str,config.getConfigDir().getReal(str.substring(21))); 136 } 137 // Web Root 138 else if(str.startsWith("{web-root")) { 139 if(config instanceof ConfigWeb) { 140 if(str.startsWith("}",9)) str=checkResult(str,config.getRootDirectory().getReal(str.substring(10))); 141 else if(str.startsWith("-dir}",9)) str=checkResult(str,config.getRootDirectory().getReal(str.substring(14))); 142 else if(str.startsWith("-directory}",9)) str=checkResult(str,config.getRootDirectory().getReal(str.substring(20))); 143 } 144 } 145 // Temp 146 else if(str.startsWith("{temp")) { 147 if(str.startsWith("}",5)) str=checkResult(str,config.getTempDirectory().getRealResource(str.substring(6)).toString()); 148 else if(str.startsWith("-dir}",5)) str=checkResult(str,config.getTempDirectory().getRealResource(str.substring(10)).toString()); 149 else if(str.startsWith("-directory}",5)) str=checkResult(str,config.getTempDirectory().getRealResource(str.substring(16)).toString()); 150 } 151 else if(config instanceof ServletConfig){ 152 Map<String,String> labels=null; 153 // web 154 if(config instanceof ConfigWebImpl){ 155 labels=((ConfigWebImpl)config).getAllLabels(); 156 } 157 // server 158 else if(config instanceof ConfigServerImpl){ 159 labels=((ConfigServerImpl)config).getLabels(); 160 } 161 if(labels!=null)str=SystemUtil.parsePlaceHolder(str,((ServletConfig)config).getServletContext(),labels); 162 } 163 else str=SystemUtil.parsePlaceHolder(str); 164 165 if(StringUtil.startsWith(str,'{')){ 166 Struct constants = ((ConfigImpl)config).getConstants(); 167 Iterator<Entry<Key, Object>> it = constants.entryIterator(); 168 Entry<Key, Object> e; 169 while(it.hasNext()) { 170 e = it.next(); 171 if(StringUtil.startsWithIgnoreCase(str,"{"+e.getKey().getString()+"}")) { 172 String value=(String) e.getValue(); 173 str=checkResult(str,config.getResource( value) 174 .getReal(str.substring(e.getKey().getString().length()+2))); 175 break; 176 177 } 178 } 179 } 180 } 181 return str; 182 } 183 184 185 186 private static String checkResult(String src, String res) { 187 boolean srcEndWithSep=StringUtil.endsWith(src, ResourceUtil.FILE_SEPERATOR) || StringUtil.endsWith(src, '/') || StringUtil.endsWith(src, '\\'); 188 boolean resEndWithSep=StringUtil.endsWith(res, ResourceUtil.FILE_SEPERATOR) || StringUtil.endsWith(res, '/') || StringUtil.endsWith(res, '\\'); 189 if(srcEndWithSep && !resEndWithSep) return res+ResourceUtil.FILE_SEPERATOR; 190 if(!srcEndWithSep && resEndWithSep) return res.substring(0,res.length()-1); 191 192 return res; 193 } 194 195 /** 196 * get only a existing file, dont create it 197 * @param sc 198 * @param strDir 199 * @param defaultDir 200 * @param configDir 201 * @param type 202 * @param config 203 * @return existing file 204 */ 205 public static Resource getExistingResource(ServletContext sc,String strDir, String defaultDir,Resource configDir, short type, Config config) { 206 //ARP 207 208 strDir=replacePlaceholder(strDir,config); 209 if(strDir!=null && strDir.trim().length()>0) { 210 Resource res=sc==null?null:_getExistingFile(config.getResource(ResourceUtil.merge(ReqRspUtil.getRootPath(sc),strDir)),type); 211 if(res!=null) return res; 212 213 res=_getExistingFile(config.getResource(strDir),type); 214 if(res!=null) return res; 215 } 216 if(defaultDir==null) return null; 217 return _getExistingFile(configDir.getRealResource(defaultDir),type); 218 219 } 220 221 private static Resource _getExistingFile(Resource file, short type) { 222 223 boolean asDir=type==ResourceUtil.TYPE_DIR; 224 // File 225 if(file.exists() && ((file.isDirectory() && asDir)||(file.isFile() && !asDir))) { 226 return ResourceUtil.getCanonicalResourceEL(file); 227 } 228 return null; 229 } 230 231 /** 232 * 233 * @param file 234 * @param type (FileUtil.TYPE_X) 235 * @return created file 236 */ 237 public static Resource getFile(Resource file, short type) { 238 return ResourceUtil.createResource(file,ResourceUtil.LEVEL_GRAND_PARENT_FILE,type); 239 } 240 241 /** 242 * checks if file is a directory or not, if directory doesn't exist, it will be created 243 * @param directory 244 * @return is directory or not 245 */ 246 public static boolean isDirectory(Resource directory) { 247 if(directory.exists()) return directory.isDirectory(); 248 return directory.mkdirs(); 249 } 250 251 /** 252 * checks if file is a file or not, if file doesn't exist, it will be created 253 * @param file 254 * @return is file or not 255 */ 256 public static boolean isFile(Resource file) { 257 if(file.exists()) return file.isFile(); 258 Resource parent=file.getParentResource(); 259 return parent.mkdirs() && file.createNewFile(); 260 } 261 262 /** 263 * has access checks if config object has access to given type 264 * @param config 265 * @param type 266 * @return has access 267 */ 268 public static boolean hasAccess(Config config, int type) { 269 270 boolean has=true; 271 if(config instanceof ConfigWeb) { 272 has=((ConfigWeb)config).getSecurityManager().getAccess(type)!=SecurityManager.VALUE_NO; 273 } 274 return has; 275 } 276 277 public static String translateOldPath(String path) { 278 if(path==null) return path; 279 if(path.startsWith("/WEB-INF/lucee/")) { 280 path="{web-root}"+path; 281 } 282 return path; 283 } 284 285 public static Object getIdMapping(Mapping m) { 286 StringBuilder id=new StringBuilder(m.getVirtualLowerCase()); 287 if(m.hasPhysical())id.append(m.getStrPhysical()); 288 if(m.hasArchive())id.append(m.getStrPhysical()); 289 return m.toString().toLowerCase(); 290 } 291 292 public static void checkGeneralReadAccess(ConfigImpl config, String password) throws SecurityException { 293 SecurityManager sm = config.getSecurityManager(); 294 short access = sm.getAccess(SecurityManager.TYPE_ACCESS_READ); 295 if(config instanceof ConfigServer)access=SecurityManager.ACCESS_PROTECTED; 296 if(access==SecurityManager.ACCESS_PROTECTED) { 297 checkPassword(config,"read",password); 298 } 299 else if(access==SecurityManager.ACCESS_CLOSE) { 300 throw new SecurityException("can't access, read access is disabled"); 301 } 302 } 303 304 public static void checkGeneralWriteAccess(ConfigImpl config, String password) throws SecurityException { 305 SecurityManager sm = config.getSecurityManager(); 306 short access = sm.getAccess(SecurityManager.TYPE_ACCESS_WRITE); 307 308 if(config instanceof ConfigServer)access=SecurityManager.ACCESS_PROTECTED; 309 if(access==SecurityManager.ACCESS_PROTECTED) { 310 checkPassword(config,"write",password); 311 } 312 else if(access==SecurityManager.ACCESS_CLOSE) { 313 throw new SecurityException("can't access, write access is disabled"); 314 } 315 } 316 317 public static Password checkPassword(ConfigImpl config, String type,String password) throws SecurityException { 318 if(!config.hasPassword()) 319 throw new SecurityException("can't access password protected information from the configuration, no password is defined"); // TODO make the message more clear for someone using the admin indirectly in source code by using ACF specific interfaces 320 Password pw = config.isPasswordEqual(password,true); 321 if(pw==null){ 322 if(StringUtil.isEmpty(password)){ 323 if(type==null) 324 throw new SecurityException("Access is protected", 325 "to access the configuration without a password, you need to change the access to [open] in the Server Administrator"); 326 throw new SecurityException(type +" access is protected", 327 "to access the configuration without a password, you need to change the "+type+" access to [open] in the Server Administrator"); 328 } 329 throw new SecurityException("No access, password is invalid"); 330 } 331 return pw; 332 } 333 334 public static String createMD5FromResource(Resource resource) throws IOException { 335 InputStream is=null; 336 try{ 337 is=resource.getInputStream(); 338 byte[] barr = IOUtil.toBytes(is); 339 return MD5.getDigestAsString(barr); 340 } 341 finally{ 342 IOUtil.closeEL(is); 343 } 344 } 345 346 public static int toListenerMode(String strListenerMode, int defaultValue) { 347 if(StringUtil.isEmpty(strListenerMode,true)) return defaultValue; 348 strListenerMode=strListenerMode.trim(); 349 350 if("current".equalsIgnoreCase(strListenerMode) || "curr".equalsIgnoreCase(strListenerMode)) 351 return ApplicationListener.MODE_CURRENT; 352 else if("currenttoroot".equalsIgnoreCase(strListenerMode) || "current2root".equalsIgnoreCase(strListenerMode) || "curr2root".equalsIgnoreCase(strListenerMode)) 353 return ApplicationListener.MODE_CURRENT2ROOT; 354 else if("currentorroot".equalsIgnoreCase(strListenerMode) || "currorroot".equalsIgnoreCase(strListenerMode)) 355 return ApplicationListener.MODE_CURRENT_OR_ROOT; 356 else if("root".equalsIgnoreCase(strListenerMode)) 357 return ApplicationListener.MODE_ROOT; 358 359 return defaultValue; 360 } 361 362 public static ApplicationListener loadListener(String type, ApplicationListener defaultValue) { 363 if(StringUtil.isEmpty(type,true)) return defaultValue; 364 type=type.trim(); 365 366 // none 367 if("none".equalsIgnoreCase(type)) 368 return new NoneAppListener(); 369 // classic 370 if("classic".equalsIgnoreCase(type)) 371 return new ClassicAppListener(); 372 // modern 373 if("modern".equalsIgnoreCase(type)) 374 return new ModernAppListener(); 375 // mixed 376 if("mixed".equalsIgnoreCase(type)) 377 return new MixedAppListener(); 378 379 return defaultValue; 380 } 381 382 public static short inspectTemplate(String str, short defaultValue) { 383 if(str==null) return defaultValue; 384 str = str.trim().toLowerCase(); 385 if (str.equals("always")) return ConfigImpl.INSPECT_ALWAYS; 386 else if (str.equals("never"))return ConfigImpl.INSPECT_NEVER; 387 else if (str.equals("once"))return ConfigImpl.INSPECT_ONCE; 388 return defaultValue; 389 } 390 391 392 public static String inspectTemplate(short s,String defaultValue) { 393 switch(s){ 394 case ConfigImpl.INSPECT_ALWAYS: return "always"; 395 case ConfigImpl.INSPECT_NEVER: return "never"; 396 case ConfigImpl.INSPECT_ONCE: return "once"; 397 default: return defaultValue; 398 } 399 } 400 401 public static short toScopeCascading(String type, short defaultValue) { 402 if(StringUtil.isEmpty(type)) return defaultValue; 403 if(type.equalsIgnoreCase("strict")) return Config.SCOPE_STRICT; 404 else if(type.equalsIgnoreCase("small")) return Config.SCOPE_SMALL; 405 else if(type.equalsIgnoreCase("standard"))return Config.SCOPE_STANDARD; 406 else if(type.equalsIgnoreCase("standart"))return Config.SCOPE_STANDARD; 407 return defaultValue; 408 } 409 410 public static String toScopeCascading(short type, String defaultValue) { 411 switch(type){ 412 case Config.SCOPE_STRICT: return "strict"; 413 case Config.SCOPE_SMALL: return "small"; 414 case Config.SCOPE_STANDARD: return "standard"; 415 default: return defaultValue; 416 } 417 } 418 419 420 public static Mapping[] getAllMappings(PageContext pc) { 421 List<Mapping> list=new ArrayList<Mapping>(); 422 getAllMappings(list,pc.getConfig().getMappings()); 423 getAllMappings(list,pc.getConfig().getCustomTagMappings()); 424 getAllMappings(list,pc.getConfig().getComponentMappings()); 425 getAllMappings(list,pc.getApplicationContext().getMappings()); 426 return list.toArray(new Mapping[list.size()]); 427 } 428 429 public static Mapping[] getAllMappings(Config c) { 430 431 List<Mapping> list=new ArrayList<Mapping>(); 432 getAllMappings(list,c.getMappings()); 433 getAllMappings(list,c.getCustomTagMappings()); 434 getAllMappings(list,c.getComponentMappings()); 435 return list.toArray(new Mapping[list.size()]); 436 } 437 438 private static void getAllMappings(List<Mapping> list, Mapping[] mappings) { 439 if(!ArrayUtil.isEmpty(mappings))for(int i=0;i<mappings.length;i++) { 440 list.add(mappings[i]); 441 } 442 } 443 444 public static CacheHandlerFactoryCollection getCacheHandlerFactories(ConfigWeb config) { 445 return ((ConfigWebImpl)config).getCacheHandlerFactories(); 446 } 447 448 public static String fixComponentPath(String path) { 449 if(path==null) return path; 450 451 if(path.startsWith("railo-server-context.")) 452 return "lucee-server"+path.substring(20); 453 if(path.startsWith("railo-context.")) 454 return "lucee-server"+path.substring(13); 455 456 return path; 457 } 458}