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