001/* 002 Licensed to the Apache Software Foundation (ASF) under one 003 or more contributor license agreements. See the NOTICE file 004 distributed with this work for additional information 005 regarding copyright ownership. The ASF licenses this file 006 to you under the Apache License, Version 2.0 (the 007 "License"); you may not use this file except in compliance 008 with the License. You may obtain a copy of the License at 009 010 http://www.apache.org/licenses/LICENSE-2.0 011 012 Unless required by applicable law or agreed to in writing, 013 software distributed under the License is distributed on an 014 "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 KIND, either express or implied. See the License for the 016 specific language governing permissions and limitations 017 under the License. 018 */ 019package org.apache.wiki; 020 021import java.lang.reflect.Method; 022import java.security.Principal; 023import java.util.Date; 024import java.util.Iterator; 025import java.util.List; 026import java.util.Properties; 027import java.util.ResourceBundle; 028 029import javax.servlet.http.HttpServletRequest; 030import javax.servlet.http.HttpSession; 031 032import org.apache.log4j.Logger; 033import org.apache.wiki.api.engine.FilterManager; 034import org.apache.wiki.api.exceptions.NoSuchVariableException; 035import org.apache.wiki.api.filters.PageFilter; 036import org.apache.wiki.i18n.InternationalizationManager; 037import org.apache.wiki.modules.InternalModule; 038import org.apache.wiki.preferences.Preferences; 039 040/** 041 * Manages variables. Variables are case-insensitive. A list of all 042 * available variables is on a Wiki page called "WikiVariables". 043 * 044 * @since 1.9.20. 045 */ 046public class VariableManager 047{ 048 private static Logger log = Logger.getLogger( VariableManager.class ); 049 050 // FIXME: These are probably obsolete. 051 public static final String VAR_ERROR = "error"; 052 public static final String VAR_MSG = "msg"; 053 054 /** 055 * Contains a list of those properties that shall never be shown. 056 * Put names here in lower case. 057 */ 058 059 static final String[] THE_BIG_NO_NO_LIST = { 060 "jspwiki.auth.masterpassword" 061 }; 062 063 /** 064 * Creates a VariableManager object using the property list given. 065 * @param props The properties. 066 */ 067 public VariableManager( Properties props ) 068 { 069 } 070 071 /** 072 * Returns true if the link is really command to insert 073 * a variable. 074 * <P> 075 * Currently we just check if the link starts with "{$". 076 * 077 * @param link The link text 078 * @return true, if this represents a variable link. 079 */ 080 public static boolean isVariableLink( String link ) 081 { 082 return link.startsWith("{$"); 083 } 084 085 /** 086 * Parses the link and finds a value. This is essentially used 087 * once {@link #isVariableLink(String)} has found that the link text 088 * actually contains a variable. For example, you could pass in 089 * "{$username}" and get back "JanneJalkanen". 090 * 091 * @param context The WikiContext 092 * @param link The link text containing the variable name. 093 * @return The variable value. 094 * @throws IllegalArgumentException If the format is not valid (does not 095 * start with "{$", is zero length, etc.) 096 * @throws NoSuchVariableException If a variable is not known. 097 */ 098 public String parseAndGetValue( WikiContext context, 099 String link ) 100 throws IllegalArgumentException, 101 NoSuchVariableException 102 { 103 if( !link.startsWith("{$") ) 104 throw new IllegalArgumentException( "Link does not start with {$" ); 105 106 if( !link.endsWith("}") ) 107 throw new IllegalArgumentException( "Link does not end with }" ); 108 109 String varName = link.substring(2,link.length()-1); 110 111 return getValue( context, varName.trim() ); 112 } 113 114 /** 115 * This method does in-place expansion of any variables. However, 116 * the expansion is not done twice, that is, a variable containing text $variable 117 * will not be expanded. 118 * <P> 119 * The variables should be in the same format ({$variablename} as in the web 120 * pages. 121 * 122 * @param context The WikiContext of the current page. 123 * @param source The source string. 124 * @return The source string with variables expanded. 125 */ 126 // FIXME: somewhat slow. 127 public String expandVariables( WikiContext context, 128 String source ) 129 { 130 StringBuilder result = new StringBuilder(); 131 132 for( int i = 0; i < source.length(); i++ ) 133 { 134 if( source.charAt(i) == '{' ) 135 { 136 if( i < source.length()-2 && source.charAt(i+1) == '$' ) 137 { 138 int end = source.indexOf( '}', i ); 139 140 if( end != -1 ) 141 { 142 String varname = source.substring( i+2, end ); 143 String value; 144 145 try 146 { 147 value = getValue( context, varname ); 148 } 149 catch( NoSuchVariableException e ) 150 { 151 value = e.getMessage(); 152 } 153 catch( IllegalArgumentException e ) 154 { 155 value = e.getMessage(); 156 } 157 158 result.append( value ); 159 i = end; 160 continue; 161 } 162 } 163 else 164 { 165 result.append( '{' ); 166 } 167 } 168 else 169 { 170 result.append( source.charAt(i) ); 171 } 172 } 173 174 return result.toString(); 175 } 176 177 /** 178 * Returns the value of a named variable. See {@link #getValue(WikiContext, String)}. 179 * The only difference is that this method does not throw an exception, but it 180 * returns the given default value instead. 181 * 182 * @param context WikiContext 183 * @param varName The name of the variable 184 * @param defValue A default value. 185 * @return The variable value, or if not found, the default value. 186 */ 187 public String getValue( WikiContext context, String varName, String defValue ) 188 { 189 try 190 { 191 return getValue( context, varName ); 192 } 193 catch( NoSuchVariableException e ) 194 { 195 return defValue; 196 } 197 } 198 199 /** 200 * Returns a value of the named variable. The resolving order is 201 * <ol> 202 * <li>Known "constant" name, such as "pagename", etc. This is so 203 * that pages could not override certain constants. 204 * <li>WikiContext local variable. This allows a programmer to 205 * set a parameter which cannot be overridden by user. 206 * <li>HTTP Session 207 * <li>HTTP Request parameters 208 * <li>WikiPage variable. As set by the user with the SET directive. 209 * <li>jspwiki.properties 210 * </ol> 211 * 212 * Use this method only whenever you really need to have a parameter that 213 * can be overridden by anyone using the wiki. 214 * 215 * @param context The WikiContext 216 * @param varName Name of the variable. 217 * 218 * @return The variable value. 219 * 220 * @throws IllegalArgumentException If the name is somehow broken. 221 * @throws NoSuchVariableException If a variable is not known. 222 */ 223 public String getValue( WikiContext context, 224 String varName ) 225 throws IllegalArgumentException, 226 NoSuchVariableException 227 { 228 if( varName == null ) 229 throw new IllegalArgumentException( "Null variable name." ); 230 231 if( varName.length() == 0 ) 232 throw new IllegalArgumentException( "Zero length variable name." ); 233 234 // Faster than doing equalsIgnoreCase() 235 String name = varName.toLowerCase(); 236 237 for( int i = 0; i < THE_BIG_NO_NO_LIST.length; i++ ) 238 { 239 if( name.equals(THE_BIG_NO_NO_LIST[i]) ) 240 return ""; // FIXME: Should this be something different? 241 } 242 243 try 244 { 245 // 246 // Using reflection to get system variables adding a new system variable 247 // now only invloves creating a new method in the SystemVariables class 248 // with a name starting with get and the first character of the name of 249 // the variable capitalized. Example: 250 // public String getMysysvar(){ 251 // return "Hello World"; 252 // } 253 // 254 SystemVariables sysvars = new SystemVariables(context); 255 String methodName = "get"+Character.toUpperCase(name.charAt(0))+name.substring(1); 256 Method method = sysvars.getClass().getMethod(methodName); 257 return (String)method.invoke(sysvars); 258 } 259 catch( NoSuchMethodException e1 ) 260 { 261 // 262 // It is not a system var. Time to handle the other cases. 263 // 264 // Check if such a context variable exists, 265 // returning its string representation. 266 // 267 if( (context.getVariable( varName )) != null ) 268 { 269 return context.getVariable( varName ).toString(); 270 } 271 272 // 273 // Well, I guess it wasn't a final straw. We also allow 274 // variables from the session and the request (in this order). 275 // 276 277 HttpServletRequest req = context.getHttpRequest(); 278 if( req != null && req.getSession() != null ) 279 { 280 HttpSession session = req.getSession(); 281 282 try 283 { 284 String s; 285 286 if( (s = (String)session.getAttribute( varName )) != null ) 287 return s; 288 289 if( (s = context.getHttpParameter( varName )) != null ) 290 return s; 291 } 292 catch( ClassCastException e ) {} 293 } 294 295 // 296 // And the final straw: see if the current page has named metadata. 297 // 298 299 WikiPage pg = context.getPage(); 300 if( pg != null ) 301 { 302 Object metadata = pg.getAttribute( varName ); 303 if( metadata != null ) 304 return metadata.toString(); 305 } 306 307 // 308 // And the final straw part 2: see if the "real" current page has 309 // named metadata. This allows a parent page to control a inserted 310 // page through defining variables 311 // 312 WikiPage rpg = context.getRealPage(); 313 if( rpg != null ) 314 { 315 Object metadata = rpg.getAttribute( varName ); 316 if( metadata != null ) 317 return metadata.toString(); 318 } 319 320 // 321 // Next-to-final straw: attempt to fetch using property name 322 // We don't allow fetching any other properties than those starting 323 // with "jspwiki.". I know my own code, but I can't vouch for bugs 324 // in other people's code... :-) 325 // 326 327 if( varName.startsWith("jspwiki.") ) 328 { 329 Properties props = context.getEngine().getWikiProperties(); 330 331 String s = props.getProperty( varName ); 332 if( s != null ) 333 { 334 return s; 335 } 336 } 337 338 // 339 // Final defaults for some known quantities. 340 // 341 342 if( varName.equals( VAR_ERROR ) || varName.equals( VAR_MSG ) ) 343 return ""; 344 345 throw new NoSuchVariableException( "No variable "+varName+" defined." ); 346 } 347 catch( Exception e ) 348 { 349 log.info("Interesting exception: cannot fetch variable value",e); 350 } 351 return ""; 352 } 353 354 /** 355 * This class provides the implementation for the different system variables. 356 * It is called via Reflection - any access to a variable called $xxx is mapped 357 * to getXxx() on this class. 358 * <p> 359 * This is a lot neater than using a huge if-else if branching structure 360 * that we used to have before. 361 * <p> 362 * Note that since we are case insensitive for variables, and VariableManager 363 * calls var.toLowerCase(), the getters for the variables do not have 364 * capitalization anywhere. This may look a bit odd, but then again, this 365 * is not meant to be a public class. 366 * 367 * @since 2.7.0 368 * 369 */ 370 private static class SystemVariables 371 { 372 private WikiContext m_context; 373 374 public SystemVariables(WikiContext context) 375 { 376 m_context=context; 377 } 378 379 public String getPagename() 380 { 381 return m_context.getPage().getName(); 382 } 383 384 public String getApplicationname() 385 { 386 return m_context.getEngine().getApplicationName(); 387 } 388 389 public String getJspwikiversion() 390 { 391 return Release.getVersionString(); 392 } 393 394 public String getEncoding() 395 { 396 return m_context.getEngine().getContentEncoding(); 397 } 398 399 public String getTotalpages() 400 { 401 return Integer.toString(m_context.getEngine().getPageCount()); 402 } 403 404 public String getPageprovider() 405 { 406 return m_context.getEngine().getCurrentProvider(); 407 } 408 409 public String getPageproviderdescription() 410 { 411 return m_context.getEngine().getCurrentProviderInfo(); 412 } 413 414 public String getAttachmentprovider() 415 { 416 WikiProvider p = m_context.getEngine().getAttachmentManager().getCurrentProvider(); 417 return (p != null) ? p.getClass().getName() : "-"; 418 } 419 420 public String getAttachmentproviderdescription() 421 { 422 WikiProvider p = m_context.getEngine().getAttachmentManager().getCurrentProvider(); 423 424 return (p != null) ? p.getProviderInfo() : "-"; 425 } 426 427 public String getInterwikilinks() 428 { 429 StringBuilder res = new StringBuilder(); 430 431 for( Iterator< String > i = m_context.getEngine().getAllInterWikiLinks().iterator(); i.hasNext(); ) 432 { 433 if( res.length() > 0 ) res.append(", "); 434 String link = i.next(); 435 res.append( link ); 436 res.append( " --> " ); 437 res.append( m_context.getEngine().getInterWikiURL(link) ); 438 } 439 return res.toString(); 440 } 441 442 public String getInlinedimages() 443 { 444 StringBuilder res = new StringBuilder(); 445 446 for( Iterator< String > i = m_context.getEngine().getAllInlinedImagePatterns().iterator(); i.hasNext(); ) 447 { 448 if( res.length() > 0 ) res.append(", "); 449 450 String ptrn = i.next(); 451 res.append(ptrn); 452 } 453 454 return res.toString(); 455 } 456 457 public String getPluginpath() 458 { 459 String s = m_context.getEngine().getPluginManager().getPluginSearchPath(); 460 461 return (s == null) ? "-" : s; 462 } 463 464 public String getBaseurl() 465 { 466 return m_context.getEngine().getBaseURL(); 467 } 468 469 public String getUptime() 470 { 471 Date now = new Date(); 472 long secondsRunning = (now.getTime() - m_context.getEngine().getStartTime().getTime()) / 1000L; 473 474 long seconds = secondsRunning % 60; 475 long minutes = (secondsRunning /= 60) % 60; 476 long hours = (secondsRunning /= 60) % 24; 477 long days = secondsRunning /= 24; 478 479 return days + "d, " + hours + "h " + minutes + "m " + seconds + "s"; 480 } 481 482 public String getLoginstatus() 483 { 484 WikiSession session = m_context.getWikiSession(); 485 return Preferences.getBundle( m_context, InternationalizationManager.CORE_BUNDLE ).getString( "varmgr." + session.getStatus()); 486 } 487 488 public String getUsername() 489 { 490 Principal wup = m_context.getCurrentUser(); 491 492 ResourceBundle rb = Preferences.getBundle( m_context, InternationalizationManager.CORE_BUNDLE ); 493 return wup != null ? wup.getName() : rb.getString( "varmgr.not.logged.in" ); 494 } 495 496 public String getRequestcontext() 497 { 498 return m_context.getRequestContext(); 499 } 500 501 public String getPagefilters() 502 { 503 FilterManager fm = m_context.getEngine().getFilterManager(); 504 List<PageFilter> filters = fm.getFilterList(); 505 StringBuilder sb = new StringBuilder(); 506 507 for (PageFilter pf : filters ) 508 { 509 String f = pf.getClass().getName(); 510 511 if( pf instanceof InternalModule ) 512 continue; 513 514 if( sb.length() > 0 ) 515 sb.append(", "); 516 sb.append(f); 517 } 518 519 return sb.toString(); 520 } 521 } 522 523}