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.api.core;
020
021import org.apache.logging.log4j.LogManager;
022import org.apache.wiki.api.exceptions.ProviderException;
023import org.apache.wiki.event.WikiEventListener;
024import org.apache.wiki.util.TextUtil;
025
026import javax.servlet.ServletContext;
027import java.io.File;
028import java.io.FileNotFoundException;
029import java.io.IOException;
030import java.io.InputStream;
031import java.io.OutputStream;
032import java.net.MalformedURLException;
033import java.net.URL;
034import java.nio.charset.Charset;
035import java.nio.file.Files;
036import java.util.Collection;
037import java.util.Date;
038import java.util.List;
039import java.util.Properties;
040
041
042/**
043 *  Provides Wiki services to the JSP page.
044 *
045 *  <P>
046 *  This is the main interface through which everything should go.
047 *
048 *  <p>
049 *  There's basically only a single Engine for each web application, and you should always get it using either the
050 *  {@code Context#getEngine()} method or through {@code Wiki.engine().find(..)} DSL methods.
051 */
052public interface Engine {
053
054    /** The default inlining pattern.  Currently "*.png" */
055    String DEFAULT_INLINEPATTERN = "*.png";
056
057    /** The name used for the default template. The value is {@value}. */
058    String DEFAULT_TEMPLATE_NAME = "default";
059
060    /** Property for application name */
061    String PROP_APPNAME = "jspwiki.applicationName";
062
063    /** This property defines the inline image pattern.  It's current value is {@value} */
064    String PROP_INLINEIMAGEPTRN = "jspwiki.translatorReader.inlinePattern";
065
066    /** Property start for any interwiki reference. */
067    String PROP_INTERWIKIREF = "jspwiki.interWikiRef.";
068
069    /** The property name defining which packages will be searched for plugin classes. */
070    String PROP_SEARCHPATH = "jspwiki.plugin.searchPath";
071
072    /** If true, then the user name will be stored with the page data.*/
073    String PROP_STOREUSERNAME= "jspwiki.storeUserName";
074
075    /** Define the used encoding.  Currently supported are ISO-8859-1 and UTF-8 */
076    String PROP_ENCODING = "jspwiki.encoding";
077
078    /** Do not use encoding in WikiJSPFilter, default is false for most servers.
079     Double negative, cause for most servers you don't need the property */
080    String PROP_NO_FILTER_ENCODING = "jspwiki.nofilterencoding";
081
082    /** Property name for where the jspwiki work directory should be.
083     If not specified, reverts to ${java.tmpdir}. */
084    String PROP_WORKDIR = "jspwiki.workDir";
085
086    /** The name of the cookie that gets stored to the user browser. */
087    String PREFS_COOKIE_NAME = "JSPWikiUserProfile";
088
089    /** Property name for the "match english plurals" -hack. */
090    String PROP_MATCHPLURALS = "jspwiki.translatorReader.matchEnglishPlurals";
091
092    /** Property name for the template that is used. */
093    String PROP_TEMPLATEDIR = "jspwiki.templateDir";
094
095    /** Property name for the default front page. */
096    String PROP_FRONTPAGE = "jspwiki.frontPage";
097
098    /** Property name for setting the url generator instance */
099    String PROP_URLCONSTRUCTOR = "jspwiki.urlConstructor";
100
101    /** The name of the property containing the ACLManager implementing class. The value is {@value}. */
102    String PROP_ACL_MANAGER_IMPL = "jspwiki.aclManager";
103
104    /** If this property is set to false, we don't allow the creation of empty pages */
105    String PROP_ALLOW_CREATION_OF_EMPTY_PAGES = "jspwiki.allowCreationOfEmptyPages";
106
107    /**
108     * Adapt Engine to a concrete type.
109     *
110     * @param cls class denoting the type to adapt to.
111     * @param <E> type to adapt to.
112     * @return engine instance adapted to the requested type. Might throw an unchecked exception if the instance cannot be adapted to requested type!
113     */
114    @SuppressWarnings( "unchecked" )
115    default < E extends Engine > E adapt( final Class< E > cls ) {
116        return ( E )this;
117    }
118
119    /**
120     * Retrieves the object instantiated by the Engine matching the requested type.
121     *
122     * @param manager requested object instantiated by the Engine.
123     * @param <T> type of the requested object.
124     * @return requested object instantiated by the Engine, {@code null} if not available.
125     */
126    < T > T getManager( Class< T > manager );
127
128    /**
129     * Retrieves the objects instantiated by the Engine that can be assigned to the requested type.
130     *
131     * @param manager requested objectx instantiated by the Engine.
132     * @param <T> type of the requested object.
133     * @return collection of requested objects instantiated by the Engine, {@code empty} list if none available.
134     */
135    < T > List< T > getManagers( Class< T > manager );
136
137    /**
138     * check if the Engine has been configured.
139     *
140     * @return {@code true} if it has, {@code false} otherwise.
141     */
142    boolean isConfigured();
143
144    /**
145     *  Returns the set of properties that the Engine was initialized with.  Note that this method returns a direct reference, so it's
146     *  possible to manipulate the properties.  However, this is not advised unless you really know what you're doing.
147     *
148     *  @return The wiki properties
149     */
150    Properties getWikiProperties();
151
152    /**
153     *  Returns the JSPWiki working directory set with "jspwiki.workDir".
154     *
155     *  @since 2.1.100
156     *  @return The working directory.
157     */
158    String getWorkDir();
159
160    /**
161     *  Returns the current template directory.
162     *
163     *  @since 1.9.20
164     *  @return The template directory as initialized by the engine.
165     */
166    String getTemplateDir();
167
168    /**
169     * Returns plugins' search path.
170     *
171     * @return plugins' search path.
172     */
173    default String getPluginSearchPath() {
174        return TextUtil.getStringProperty( getWikiProperties(), PROP_SEARCHPATH, null );
175    }
176
177    /**
178     *  Returns the moment when this engine was started.
179     *
180     *  @since 2.0.15.
181     *  @return The start time of this wiki.
182     */
183    Date getStartTime();
184
185    /**
186     *  Returns the base URL, telling where this Wiki actually lives.
187     *
188     *  @since 1.6.1
189     *  @return The Base URL.
190     */
191    String getBaseURL();
192
193    /**
194     *  Returns the URL of the global RSS file.  May be null, if the RSS file generation is not operational.
195     *
196     *  @since 1.7.10
197     *  @return The global RSS url
198     */
199    String getGlobalRSSURL();
200
201    /**
202     *  Returns an URL to some other Wiki that we know.
203     *
204     *  @param  wikiName The name of the other wiki.
205     *  @return null, if no such reference was found.
206     */
207    String getInterWikiURL( String wikiName );
208
209    /**
210     *  Returns an URL if a WikiContext is not available.
211     *
212     *  @param context The WikiContext (VIEW, EDIT, etc...)
213     *  @param pageName Name of the page, as usual
214     *  @param params List of parameters. May be null, if no parameters.
215     *  @return An URL (absolute or relative).
216     */
217    String getURL( String context, String pageName, String params );
218
219    /**
220     *  Returns the default front page, if no page is used.
221     *
222     *  @return The front page name.
223     */
224    String getFrontPage();
225
226    /**
227     *  Returns the ServletContext that this particular Engine was initialized with. <strong>It may return {@code null}</strong>,
228     *  if the Engine is not running inside a servlet container!
229     *
230     *  @since 1.7.10
231     *  @return ServletContext of the Engine, or {@code null}.
232     */
233    ServletContext getServletContext();
234
235    /**
236     * Looks up and obtains a configuration file inside the WEB-INF folder of a wiki webapp.
237     *
238     * @param name the file to obtain, <em>e.g.</em>, <code>jspwiki.policy</code>
239     * @return the URL to the file
240     */
241    default URL findConfigFile( final String name ) {
242        LogManager.getLogger( Engine.class ).info( "looking for " + name + " inside WEB-INF " );
243        // Try creating an absolute path first
244        File defaultFile = null;
245        if( getRootPath() != null ) {
246            defaultFile = new File( getRootPath() + "/WEB-INF/" + name );
247        }
248        if ( defaultFile != null && defaultFile.exists() ) {
249            try {
250                return defaultFile.toURI().toURL();
251            } catch ( final MalformedURLException e ) {
252                // Shouldn't happen, but log it if it does
253                LogManager.getLogger( Engine.class ).warn( "Malformed URL: " + e.getMessage() );
254            }
255        }
256
257        // Ok, the absolute path didn't work; try other methods
258        URL path = null;
259
260        if( getServletContext() != null ) {
261            final File tmpFile;
262            try {
263                tmpFile = File.createTempFile( "temp." + name, "" );
264            } catch( final IOException e ) {
265                LogManager.getLogger( Engine.class ).error( "unable to create a temp file to load onto the policy", e );
266                return null;
267            }
268            tmpFile.deleteOnExit();
269            LogManager.getLogger( Engine.class ).info( "looking for /" + name + " on classpath" );
270            //  create a tmp file of the policy loaded as an InputStream and return the URL to it
271            try( final InputStream is = Engine.class.getResourceAsStream( "/" + name );
272                final OutputStream os = Files.newOutputStream( tmpFile.toPath() ) ) {
273                if( is == null ) {
274                    throw new FileNotFoundException( name + " not found" );
275                }
276                final URL url = getServletContext().getResource( "/WEB-INF/" + name );
277                if( url != null ) {
278                    return url;
279                }
280
281                final byte[] buff = new byte[1024];
282                int bytes;
283                while( ( bytes = is.read( buff ) ) != -1 ) {
284                    os.write( buff, 0, bytes );
285                }
286
287                path = tmpFile.toURI().toURL();
288            } catch( final MalformedURLException e ) {
289                // This should never happen unless I screw up
290                LogManager.getLogger( Engine.class ).fatal( "Your code is b0rked.  You are a bad person.", e );
291            } catch( final IOException e ) {
292                LogManager.getLogger( Engine.class ).error( "failed to load security policy from file " + name + ",stacktrace follows", e );
293            }
294        }
295        return path;
296    }
297
298    /**
299     *  Returns a collection of all supported InterWiki links.
300     *
301     *  @return A Collection of Strings.
302     */
303    Collection< String > getAllInterWikiLinks();
304
305    /**
306     *  Returns a collection of all image types that get inlined.
307     *
308     *  @return A Collection of Strings with a regexp pattern.
309     */
310    Collection< String > getAllInlinedImagePatterns();
311
312    /**
313     *  <p>If the page is a special page, then returns a direct URL to that page. Otherwise returns <code>null</code>.
314     *  This method delegates requests to {@link org.apache.wiki.ui.CommandResolver#getSpecialPageReference(String)}.</p>
315     *  <p>Special pages are defined in jspwiki.properties using the jspwiki.specialPage setting. They're typically used to give Wiki page
316     *  names to e.g. custom JSP pages.</p>
317     *
318     *  @param original The page to check
319     *  @return A reference to the page, or null, if there's no special page.
320     */
321    String getSpecialPageReference( String original );
322
323    /**
324     *  Returns the name of the application.
325     *
326     *  @return A string describing the name of this application.
327     */
328    String getApplicationName();
329
330    /**
331     *  Returns the root path.  The root path is where the Engine is located in the file system.
332     *
333     *  @since 2.2
334     *  @return A path to where the Wiki is installed in the local filesystem.
335     */
336    String getRootPath();
337
338    /**
339     *  Returns the correct page name, or null, if no such page can be found.  Aliases are considered. This method simply delegates to
340     *  {@link org.apache.wiki.ui.CommandResolver#getFinalPageName(String)}.
341     *
342     *  @since 2.0
343     *  @param page Page name.
344     *  @return The rewritten page name, or null, if the page does not exist.
345     *  @throws ProviderException If something goes wrong in the backend.
346     */
347    String getFinalPageName( String page ) throws ProviderException;
348
349    /**
350     *  Turns a WikiName into something that can be called through using an URL.
351     *
352     *  @since 1.4.1
353     *  @param pagename A name. Can be actually any string.
354     *  @return A properly encoded name.
355     *  @see #decodeName(String)
356     */
357    String encodeName( String pagename );
358
359    /**
360     *  Decodes a URL-encoded request back to regular life.  This properly heeds the encoding as defined in the settings file.
361     *
362     *  @param pagerequest The URL-encoded string to decode
363     *  @return A decoded string.
364     *  @see #encodeName(String)
365     */
366    String decodeName( String pagerequest );
367
368    /**
369     *  Returns the IANA name of the character set encoding we're supposed to be using right now.
370     *
371     *  @since 1.5.3
372     *  @return The content encoding (either UTF-8 or ISO-8859-1).
373     */
374    Charset getContentEncoding();
375
376    /**
377     * Registers a WikiEventListener with this instance.
378     *
379     * @param listener the event listener
380     */
381    void addWikiEventListener( WikiEventListener listener );
382
383    /**
384     * Un-registers a WikiEventListener with this instance.
385     *
386     * @param listener the event listener
387     */
388    void removeWikiEventListener( WikiEventListener listener );
389
390    /**
391     * Adds an attribute to the engine for the duration of this engine.  The value is not persisted.
392     *
393     * @since 2.4.91
394     * @param key the attribute name
395     * @param value the value
396     */
397    void setAttribute( String key, Object value );
398
399    /**
400     *  Gets an attribute from the engine.
401     *
402     *  @param key the attribute name
403     *  @return the value
404     */
405    < T > T getAttribute( String key );
406
407    /**
408     *  Removes an attribute.
409     *
410     *  @param key The key of the attribute to remove.
411     *  @return The previous attribute, if it existed.
412     */
413    < T > T removeAttribute( String key );
414
415    /**
416     * Signals that the Engine will be shut down by the servlet container.
417     */
418    void shutdown();
419
420}