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 */
019 package org.apache.wiki;
020
021 import java.lang.reflect.Method;
022 import java.security.Principal;
023 import java.util.Date;
024 import java.util.Iterator;
025 import java.util.List;
026 import java.util.Properties;
027 import java.util.ResourceBundle;
028
029 import javax.servlet.http.HttpServletRequest;
030 import javax.servlet.http.HttpSession;
031
032 import org.apache.log4j.Logger;
033 import org.apache.wiki.api.engine.FilterManager;
034 import org.apache.wiki.api.exceptions.NoSuchVariableException;
035 import org.apache.wiki.api.filters.PageFilter;
036 import org.apache.wiki.i18n.InternationalizationManager;
037 import org.apache.wiki.modules.InternalModule;
038 import 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 */
046 public 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 StringBuffer result = new StringBuffer();
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 StringBuffer res = new StringBuffer();
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 StringBuffer res = new StringBuffer();
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 StringBuffer sb = new StringBuffer();
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 }