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