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.variables; 020 021import org.apache.logging.log4j.LogManager; 022import org.apache.logging.log4j.Logger; 023import org.apache.wiki.api.Release; 024import org.apache.wiki.api.core.Context; 025import org.apache.wiki.api.core.Page; 026import org.apache.wiki.api.core.Session; 027import org.apache.wiki.api.exceptions.NoSuchVariableException; 028import org.apache.wiki.api.filters.PageFilter; 029import org.apache.wiki.api.providers.WikiProvider; 030import org.apache.wiki.attachment.AttachmentManager; 031import org.apache.wiki.filters.FilterManager; 032import org.apache.wiki.i18n.InternationalizationManager; 033import org.apache.wiki.modules.InternalModule; 034import org.apache.wiki.pages.PageManager; 035import org.apache.wiki.preferences.Preferences; 036 037import javax.servlet.http.HttpServletRequest; 038import javax.servlet.http.HttpSession; 039import java.lang.reflect.Method; 040import java.security.Principal; 041import java.util.Date; 042import java.util.List; 043import java.util.Properties; 044import java.util.ResourceBundle; 045 046 047/** 048 * Manages variables. Variables are case-insensitive. A list of all available variables is on a Wiki page called "WikiVariables". 049 * 050 * @since 1.9.20. 051 */ 052public class DefaultVariableManager implements VariableManager { 053 054 private static final Logger log = LogManager.getLogger( DefaultVariableManager.class ); 055 056 /** 057 * Contains a list of those properties that shall never be shown. Put names here in lower case. 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 DefaultVariableManager( final Properties props ) { 068 } 069 070 /** 071 * {@inheritDoc} 072 */ 073 @Override 074 public String parseAndGetValue( final Context context, final String link ) throws IllegalArgumentException, NoSuchVariableException { 075 if( !link.startsWith( "{$" ) ) { 076 throw new IllegalArgumentException( "Link does not start with {$" ); 077 } 078 if( !link.endsWith( "}" ) ) { 079 throw new IllegalArgumentException( "Link does not end with }" ); 080 } 081 final String varName = link.substring( 2, link.length() - 1 ); 082 083 return getValue( context, varName.trim() ); 084 } 085 086 /** 087 * {@inheritDoc} 088 */ 089 @Override 090 // FIXME: somewhat slow. 091 public String expandVariables( final Context context, final String source ) { 092 final StringBuilder result = new StringBuilder(); 093 for( int i = 0; i < source.length(); i++ ) { 094 if( source.charAt(i) == '{' ) { 095 if( i < source.length()-2 && source.charAt(i+1) == '$' ) { 096 final int end = source.indexOf( '}', i ); 097 098 if( end != -1 ) { 099 final String varname = source.substring( i+2, end ); 100 String value; 101 102 try { 103 value = getValue( context, varname ); 104 } catch( final NoSuchVariableException | IllegalArgumentException e ) { 105 value = e.getMessage(); 106 } 107 108 result.append( value ); 109 i = end; 110 } 111 } else { 112 result.append( '{' ); 113 } 114 } else { 115 result.append( source.charAt(i) ); 116 } 117 } 118 119 return result.toString(); 120 } 121 122 /** 123 * {@inheritDoc} 124 */ 125 @Override 126 public String getValue( final Context context, final String varName, final String defValue ) { 127 try { 128 return getValue( context, varName ); 129 } catch( final NoSuchVariableException e ) { 130 return defValue; 131 } 132 } 133 134 /** 135 * {@inheritDoc} 136 */ 137 @Override 138 public String getVariable( final Context context, final String name ) { 139 return getValue( context, name, null ); 140 } 141 142 /** 143 * {@inheritDoc} 144 */ 145 @Override 146 public String getValue( final Context context, final String varName ) throws IllegalArgumentException, NoSuchVariableException { 147 if( varName == null ) { 148 throw new IllegalArgumentException( "Null variable name." ); 149 } 150 if( varName.isEmpty() ) { 151 throw new IllegalArgumentException( "Zero length variable name." ); 152 } 153 // Faster than doing equalsIgnoreCase() 154 final String name = varName.toLowerCase(); 155 156 for( final String value : THE_BIG_NO_NO_LIST ) { 157 if( name.equals( value ) ) { 158 return ""; // FIXME: Should this be something different? 159 } 160 } 161 162 try { 163 // 164 // Using reflection to get system variables adding a new system variable 165 // now only involves creating a new method in the SystemVariables class 166 // with a name starting with get and the first character of the name of 167 // the variable capitalized. Example: 168 // public String getMysysvar(){ 169 // return "Hello World"; 170 // } 171 // 172 final SystemVariables sysvars = new SystemVariables( context ); 173 final String methodName = "get" + Character.toUpperCase( name.charAt( 0 ) ) + name.substring( 1 ); 174 final Method method = sysvars.getClass().getMethod( methodName ); 175 return ( String )method.invoke( sysvars ); 176 } catch( final NoSuchMethodException e1 ) { 177 // 178 // It is not a system var. Time to handle the other cases. 179 // 180 // Check if such a context variable exists, returning its string representation. 181 // 182 if( ( context.getVariable( varName ) ) != null ) { 183 return context.getVariable( varName ).toString(); 184 } 185 186 // 187 // Well, I guess it wasn't a final straw. We also allow variables from the session and the request (in this order). 188 // 189 final HttpServletRequest req = context.getHttpRequest(); 190 if( req != null && req.getSession() != null ) { 191 final HttpSession session = req.getSession(); 192 193 try { 194 String s = ( String )session.getAttribute( varName ); 195 196 if( s != null ) { 197 return s; 198 } 199 200 s = context.getHttpParameter( varName ); 201 if( s != null ) { 202 return s; 203 } 204 } catch( final ClassCastException e ) { 205 log.debug( "Not a String: " + varName ); 206 } 207 } 208 209 // 210 // And the final straw: see if the current page has named metadata. 211 // 212 final Page pg = context.getPage(); 213 if( pg != null ) { 214 final Object metadata = pg.getAttribute( varName ); 215 if( metadata != null ) { 216 return metadata.toString(); 217 } 218 } 219 220 // 221 // And the final straw part 2: see if the "real" current page has named metadata. This allows 222 // a parent page to control a inserted page through defining variables 223 // 224 final Page rpg = context.getRealPage(); 225 if( rpg != null ) { 226 final Object metadata = rpg.getAttribute( varName ); 227 if( metadata != null ) { 228 return metadata.toString(); 229 } 230 } 231 232 // 233 // Next-to-final straw: attempt to fetch using property name. We don't allow fetching any other 234 // properties than those starting with "jspwiki.". I know my own code, but I can't vouch for bugs 235 // in other people's code... :-) 236 // 237 if( varName.startsWith("jspwiki.") ) { 238 final Properties props = context.getEngine().getWikiProperties(); 239 final String s = props.getProperty( varName ); 240 if( s != null ) { 241 return s; 242 } 243 } 244 245 // 246 // Final defaults for some known quantities. 247 // 248 if( varName.equals( VAR_ERROR ) || varName.equals( VAR_MSG ) ) { 249 return ""; 250 } 251 252 throw new NoSuchVariableException( "No variable " + varName + " defined." ); 253 } catch( final Exception e ) { 254 log.info("Interesting exception: cannot fetch variable value", e ); 255 } 256 return ""; 257 } 258 259 /** 260 * This class provides the implementation for the different system variables. 261 * It is called via Reflection - any access to a variable called $xxx is mapped 262 * to getXxx() on this class. 263 * <p> 264 * This is a lot neater than using a huge if-else if branching structure 265 * that we used to have before. 266 * <p> 267 * Note that since we are case insensitive for variables, and VariableManager 268 * calls var.toLowerCase(), the getters for the variables do not have 269 * capitalization anywhere. This may look a bit odd, but then again, this 270 * is not meant to be a public class. 271 * 272 * @since 2.7.0 273 */ 274 @SuppressWarnings( "unused" ) 275 private static class SystemVariables { 276 277 private final Context m_context; 278 279 public SystemVariables( final Context context ) 280 { 281 m_context=context; 282 } 283 284 public String getPagename() 285 { 286 return m_context.getPage().getName(); 287 } 288 289 public String getApplicationname() 290 { 291 return m_context.getEngine().getApplicationName(); 292 } 293 294 public String getJspwikiversion() 295 { 296 return Release.getVersionString(); 297 } 298 299 public String getEncoding() { 300 return m_context.getEngine().getContentEncoding().displayName(); 301 } 302 303 public String getTotalpages() { 304 return Integer.toString( m_context.getEngine().getManager( PageManager.class ).getTotalPageCount() ); 305 } 306 307 public String getPageprovider() { 308 return m_context.getEngine().getManager( PageManager.class ).getCurrentProvider(); 309 } 310 311 public String getPageproviderdescription() { 312 return m_context.getEngine().getManager( PageManager.class ).getProviderDescription(); 313 } 314 315 public String getAttachmentprovider() { 316 final WikiProvider p = m_context.getEngine().getManager( AttachmentManager.class ).getCurrentProvider(); 317 return (p != null) ? p.getClass().getName() : "-"; 318 } 319 320 public String getAttachmentproviderdescription() { 321 final WikiProvider p = m_context.getEngine().getManager( AttachmentManager.class ).getCurrentProvider(); 322 return (p != null) ? p.getProviderInfo() : "-"; 323 } 324 325 public String getInterwikilinks() { 326 final StringBuilder res = new StringBuilder(); 327 328 for( final String link : m_context.getEngine().getAllInterWikiLinks() ) { 329 if( res.length() > 0 ) { 330 res.append( ", " ); 331 } 332 res.append( link ); 333 res.append( " --> " ); 334 res.append( m_context.getEngine().getInterWikiURL( link ) ); 335 } 336 return res.toString(); 337 } 338 339 public String getInlinedimages() { 340 final StringBuilder res = new StringBuilder(); 341 for( final String ptrn : m_context.getEngine().getAllInlinedImagePatterns() ) { 342 if( res.length() > 0 ) { 343 res.append( ", " ); 344 } 345 346 res.append( ptrn ); 347 } 348 349 return res.toString(); 350 } 351 352 public String getPluginpath() { 353 final String s = m_context.getEngine().getPluginSearchPath(); 354 355 return ( s == null ) ? "-" : s; 356 } 357 358 public String getBaseurl() 359 { 360 return m_context.getEngine().getBaseURL(); 361 } 362 363 public String getUptime() { 364 final Date now = new Date(); 365 long secondsRunning = ( now.getTime() - m_context.getEngine().getStartTime().getTime() ) / 1_000L; 366 367 final long seconds = secondsRunning % 60; 368 final long minutes = (secondsRunning /= 60) % 60; 369 final long hours = (secondsRunning /= 60) % 24; 370 final long days = secondsRunning /= 24; 371 372 return days + "d, " + hours + "h " + minutes + "m " + seconds + "s"; 373 } 374 375 public String getLoginstatus() { 376 final Session session = m_context.getWikiSession(); 377 return Preferences.getBundle( m_context, InternationalizationManager.CORE_BUNDLE ).getString( "varmgr." + session.getStatus() ); 378 } 379 380 public String getUsername() { 381 final Principal wup = m_context.getCurrentUser(); 382 final ResourceBundle rb = Preferences.getBundle( m_context, InternationalizationManager.CORE_BUNDLE ); 383 return wup != null ? wup.getName() : rb.getString( "varmgr.not.logged.in" ); 384 } 385 386 public String getRequestcontext() 387 { 388 return m_context.getRequestContext(); 389 } 390 391 public String getPagefilters() { 392 final FilterManager fm = m_context.getEngine().getManager( FilterManager.class ); 393 final List< PageFilter > filters = fm.getFilterList(); 394 final StringBuilder sb = new StringBuilder(); 395 for( final PageFilter pf : filters ) { 396 final String f = pf.getClass().getName(); 397 if( pf instanceof InternalModule ) { 398 continue; 399 } 400 401 if( sb.length() > 0 ) { 402 sb.append( ", " ); 403 } 404 sb.append( f ); 405 } 406 return sb.toString(); 407 } 408 } 409 410}