    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;
    020
    021import org.apache.logging.log4j.LogManager;
    022import org.apache.logging.log4j.Logger;
    023import org.apache.wiki.api.core.Command;
    024import org.apache.wiki.api.core.Context;
    025import org.apache.wiki.api.core.ContextEnum;
    026import org.apache.wiki.api.core.Engine;
    027import org.apache.wiki.api.core.Page;
    028import org.apache.wiki.api.core.Session;
    029import org.apache.wiki.api.spi.Wiki;
    030import org.apache.wiki.auth.AuthorizationManager;
    031import org.apache.wiki.auth.NoSuchPrincipalException;
    032import org.apache.wiki.auth.UserManager;
    033import org.apache.wiki.auth.WikiPrincipal;
    034import org.apache.wiki.auth.permissions.AllPermission;
    035import org.apache.wiki.auth.user.UserDatabase;
    036import org.apache.wiki.pages.PageManager;
    037import org.apache.wiki.ui.CommandResolver;
    038import org.apache.wiki.ui.Installer;
    039import org.apache.wiki.ui.PageCommand;
    040import org.apache.wiki.ui.WikiCommand;
    041import org.apache.wiki.util.TextUtil;
    042
    043import jakarta.servlet.http.HttpServletRequest;
    044import jakarta.servlet.http.HttpSession;
    045import jakarta.servlet.jsp.PageContext;
    046import java.security.Permission;
    047import java.security.Principal;
    048import java.util.HashMap;
    049import java.util.PropertyPermission;
    050
    051/**
    052 *  <p>Provides state information throughout the processing of a page.  A WikiContext is born when the JSP pages that are the main entry
    053 *  points, are invoked.  The JSPWiki engine creates the new WikiContext, which basically holds information about the page, the
    054 *  handling engine, and in which context (view, edit, etc) the call was done.</p>
    055 *  <p>A WikiContext also provides request-specific variables, which can be used to communicate between plugins on the same page, or
    056 *  between different instances of the same plugin.  A WikiContext variable is valid until the processing of the page has ended.  For
    057 *  an example, please see the Counter plugin.</p>
    058 *  <p>When a WikiContext is created, it automatically associates a {@link WikiSession} object with the user's HttpSession. The
    059 *  WikiSession contains information about the user's authentication status, and is consulted by {@link #getCurrentUser()} object.</p>
    060 *  <p>Do not cache the page object that you get from the WikiContext; always use getPage()!</p>
    061 *
    062 *  @see org.apache.wiki.plugin.Counter
    063 */
    064public class WikiContext implements Context, Command {
    065
    066    private Command  m_command;
    067    private WikiPage m_page;
    068    private WikiPage m_realPage;
    069    private Engine   m_engine;
    070    private String   m_template = "default";
    071
    072    private HashMap< String, Object > m_variableMap = new HashMap<>();
    073
    074    /** Stores the HttpServletRequest.  May be null, if the request did not come from a servlet. */
    075    protected HttpServletRequest m_request;
    076
    077    private Session m_session;
    078
    079    /** User is doing administrative things. */
    080    public static final String ADMIN = ContextEnum.WIKI_ADMIN.getRequestContext();
    081
    082    /** User is downloading an attachment. */
    083    public static final String ATTACH = ContextEnum.PAGE_ATTACH.getRequestContext();
    084
    085    /** User is commenting something. */
    086    public static final String COMMENT = ContextEnum.PAGE_COMMENT.getRequestContext();
    087
    088    /** User has an internal conflict, and does quite not know what to do. Please provide some counseling. */
    089    public static final String CONFLICT = ContextEnum.PAGE_CONFLICT.getRequestContext();
    090
    091    /** User wishes to create a new group */
    092    public static final String CREATE_GROUP = ContextEnum.WIKI_CREATE_GROUP.getRequestContext();
    093
    094    /** User is deleting a page or an attachment. */
    095    public static final String DELETE = ContextEnum.PAGE_DELETE.getRequestContext();
    096
    097    /** User is deleting an existing group. */
    098    public static final String DELETE_GROUP = ContextEnum.GROUP_DELETE.getRequestContext();
    099
    100    /** User is viewing a DIFF between the two versions of the page. */
    101    public static final String DIFF = ContextEnum.PAGE_DIFF.getRequestContext();
    102
    103    /** The EDIT context - the user is editing the page. */
    104    public static final String EDIT = ContextEnum.PAGE_EDIT.getRequestContext();
    105
    106    /** User is editing an existing group. */
    107    public static final String EDIT_GROUP = ContextEnum.GROUP_EDIT.getRequestContext();
    108
    109    /** An error has been encountered and the user needs to be informed. */
    110    public static final String ERROR = ContextEnum.WIKI_ERROR.getRequestContext();
    111
    112    /** User is searching for content. */
    113    public static final String FIND = ContextEnum.WIKI_FIND.getRequestContext();
    114
    115    /** User is viewing page history. */
    116    public static final String INFO = ContextEnum.PAGE_INFO.getRequestContext();
    117
    118    /** User is administering JSPWiki (Install, SecurityConfig). */
    119    public static final String INSTALL = ContextEnum.WIKI_INSTALL.getRequestContext();
    120
    121    /** User is preparing for a login/authentication. */
    122    public static final String LOGIN = ContextEnum.WIKI_LOGIN.getRequestContext();
    123
    124    /** User is preparing to log out. */
    125    public static final String LOGOUT = ContextEnum.WIKI_LOGOUT.getRequestContext();
    126
    127    /** JSPWiki wants to display a message. */
    128    public static final String MESSAGE = ContextEnum.WIKI_MESSAGE.getRequestContext();
    129
    130    /** This is not a JSPWiki context, use it to access static files. */
    131    public static final String NONE = ContextEnum.PAGE_NONE.getRequestContext();
    132
    133    /** Same as NONE; this is just a clarification. */
    134    public static final String OTHER = ContextEnum.PAGE_NONE.getRequestContext();
    135
    136    /** User is editing preferences */
    137    public static final String PREFS = ContextEnum.WIKI_PREFS.getRequestContext();
    138
    139    /** User is previewing the changes he just made. */
    140    public static final String PREVIEW = ContextEnum.PAGE_PREVIEW.getRequestContext();
    141
    142    /** User is renaming a page. */
    143    public static final String RENAME = ContextEnum.PAGE_RENAME.getRequestContext();
    144
    145    /** RSS feed is being generated. */
    146    public static final String RSS = ContextEnum.PAGE_RSS.getRequestContext();
    147
    148    /** User is uploading something. */
    149    public static final String UPLOAD = ContextEnum.PAGE_UPLOAD.getRequestContext();
    150
    151    /** The VIEW context - the user just wants to view the page contents. */
    152    public static final String VIEW = ContextEnum.PAGE_VIEW.getRequestContext();
    153
    154    /** User is viewing an existing group */
    155    public static final String VIEW_GROUP = ContextEnum.GROUP_VIEW.getRequestContext();
    156
    157    /** User wants to view or administer workflows. */
    158    public static final String WORKFLOW = ContextEnum.WIKI_WORKFLOW.getRequestContext();
    159
    160    private static final Logger LOG = LogManager.getLogger( WikiContext.class );
    161
    162    private static final Permission DUMMY_PERMISSION = new PropertyPermission( "os.name", "read" );
    163
    164    /**
    165     *  Note: it is preferred to use the DSL API for creating a wiki context.
    166     *  Example: Wiki.contex().create( .. )
    167     *
    168     *  Create a new WikiContext for the given WikiPage. Delegates to {@link #WikiContext(Engine, HttpServletRequest, Page)}.
    169     *
    170     *  @param engine The Engine that is handling the request.
    171     *  @param page The WikiPage. If you want to create a WikiContext for an older version of a page, you must use this constructor.
    172     */
    173    public WikiContext( final Engine engine, final Page page ) {
    174        this( engine, null, findCommand( engine, null, page ) );
    175    }
    176
    177    /**
    178     * <p>
    179     * Creates a new WikiContext for the given Engine, Command and HttpServletRequest.
    180     * </p>
    181     * Note: it is preferred to use the DSL API for creating a wiki context.
    182     * Example: Wiki.contex().create( .. )
    183     * <p>
    184     * This constructor will also look up the HttpSession associated with the request, and determine if a Session object is present.
    185     * If not, a new one is created.
    186     * </p>
    187     * @param engine The Engine that is handling the request
    188     * @param request The HttpServletRequest that should be associated with this context. This parameter may be <code>null</code>.
    189     * @param command the command
    190     * @throws IllegalArgumentException if <code>engine</code> or <code>command</code> are <code>null</code>
    191     */
    192    public WikiContext( final Engine engine, final HttpServletRequest request, final Command command ) throws IllegalArgumentException {
    193        if ( engine == null || command == null ) {
    194            throw new IllegalArgumentException( "Parameter engine and command must not be null." );
    195        }
    196
    197        m_engine = engine;
    198        m_request = request;
    199        m_session = Wiki.session().find( engine, request );
    200        m_command = command;
    201
    202        // If PageCommand, get the WikiPage
    203        if( command instanceof PageCommand ) {
    204            m_page = ( WikiPage )command.getTarget();
    205        }
    206
    207        // If page not supplied, default to front page to avoid NPEs
    208        if( m_page == null ) {
    209            m_page = ( WikiPage )m_engine.getManager( PageManager.class ).getPage( m_engine.getFrontPage() );
    210
    211            // Front page does not exist?
    212            if( m_page == null ) {
    213                m_page = ( WikiPage )Wiki.contents().page( m_engine, m_engine.getFrontPage() );
    214            }
    215        }
    216
    217        m_realPage = m_page;
    218
    219        // Special case: retarget any empty 'view' PageCommands to the front page
    220        if ( PageCommand.VIEW.equals( command ) && command.getTarget() == null ) {
    221            m_command = command.targetedCommand( m_page );
    222        }
    223
    224        // Debugging...
    225        final HttpSession session = ( request == null ) ? null : request.getSession( false );
    226        final String sid = session == null ? "(null)" : session.getId();
    227        LOG.debug( "Creating WikiContext for session ID={}; target={}", sid, getName() );
    228
    229        // Figure out what template to use
    230        setDefaultTemplate( request );
    231    }
    232
    233    /**
    234     * Creates a new WikiContext for the given Engine, WikiPage and HttpServletRequest. This method simply looks up the appropriate
    235     * Command using {@link #findCommand(Engine, HttpServletRequest, Page)} and delegates to
    236     * {@link #WikiContext(Engine, HttpServletRequest, Command)}.
    237     *
    238     * Note: it is preferred to use the DSL API for creating a wiki context.
    239     *  Example: Wiki.contex().create( .. )
    240     *
    241     * @param engine The Engine that is handling the request
    242     * @param request The HttpServletRequest that should be associated with this context. This parameter may be <code>null</code>.
    243     * @param page The WikiPage. If you want to create a WikiContext for an older version of a page, you must supply this parameter
    244     */
    245    public WikiContext( final Engine engine, final HttpServletRequest request, final Page page ) {
    246        this( engine, request, findCommand( engine, request, page ) );
    247    }
    248
    249    /**
    250     *  Creates a new WikiContext from a supplied HTTP request, using a default wiki context.
    251     *
    252     *  Note: it is preferred to use the DSL API for creating a wiki context.
    253     *  Example: Wiki.contex().create( .. )
    254     *
    255     *  @param engine The Engine that is handling the request
    256     *  @param request the HTTP request
    257     *  @param requestContext the default context to use
    258     *  @see org.apache.wiki.ui.CommandResolver
    259     *  @see org.apache.wiki.api.core.Command
    260     *  @since 2.1.15.
    261     */
    262    public WikiContext( final Engine engine, final HttpServletRequest request, final String requestContext ) {
    263        this( engine, request, engine.getManager( CommandResolver.class ).findCommand( request, requestContext ) );
    264        if( !engine.isConfigured() ) {
    265            throw new InternalWikiException( "Engine has not been properly started.  It is likely that the configuration is faulty.  Please check all logs for the possible reason." );
    266        }
    267    }
    268
    269    /**
    270     * {@inheritDoc}
    271     * @see org.apache.wiki.api.core.Command#getContentTemplate()
    272     */
    273    @Override
    274    public String getContentTemplate()
    275    {
    276        return m_command.getContentTemplate();
    277    }
    278
    279    /**
    280     * {@inheritDoc}
    281     * @see org.apache.wiki.api.core.Command#getJSP()
    282     */
    283    @Override
    284    public String getJSP()
    285    {
    286        return m_command.getContentTemplate();
    287    }
    288
    289    /**
    290     *  Sets a reference to the real page whose content is currently being rendered.
    291     *  <p>
    292     *  Sometimes you may want to render the page using some other page's context. In those cases, it is highly recommended that you set
    293     *  the setRealPage() to point at the real page you are rendering.  Please see InsertPageTag for an example.
    294     *  <p>
    295     *  Also, if your plugin e.g. does some variable setting, be aware that if it is embedded in the LeftMenu or some other page added
    296     *  with InsertPageTag, you should consider what you want to do - do you wish to really reference the "master" page or the included
    297     *  page.
    298     *
    299     *  @param page  The real page which is being rendered.
    300     *  @return The previous real page
    301     *  @since 2.3.14
    302     *  @see org.apache.wiki.tags.InsertPageTag
    303     */
    304    @Override
    305    public WikiPage setRealPage( final Page page ) {
    306        final WikiPage old = m_realPage;
    307        m_realPage = ( WikiPage )page;
    308        updateCommand( m_command.getRequestContext() );
    309        return old;
    310    }
    311
    312    /**
    313     *  Gets a reference to the real page whose content is currently being rendered. If your plugin e.g. does some variable setting, be
    314     *  aware that if it is embedded in the LeftMenu or some other page added with InsertPageTag, you should consider what you want to
    315     *  do - do you wish to really reference the "master" page or the included page.
    316     *  <p>
    317     *  For example, in the default template, there is a page called "LeftMenu". Whenever you access a page, e.g. "Main", the master
    318     *  page will be Main, and that's what the getPage() will return - regardless of whether your plugin resides on the LeftMenu or on
    319     *  the Main page.  However, getRealPage() will return "LeftMenu".
    320     *
    321     *  @return A reference to the real page.
    322     *  @see org.apache.wiki.tags.InsertPageTag
    323     *  @see org.apache.wiki.parser.JSPWikiMarkupParser
    324     */
    325    @Override
    326    public WikiPage getRealPage()
    327    {
    328        return m_realPage;
    329    }
    330
    331    /**
    332     *  Figure out to which page we are really going to.  Considers special page names from the jspwiki.properties, and possible aliases.
    333     *  This method forwards requests to {@link org.apache.wiki.ui.CommandResolver#getSpecialPageReference(String)}.
    334     *  @return A complete URL to the new page to redirect to
    335     *  @since 2.2
    336     */
    337    @Override
    338    public String getRedirectURL() {
    339        final String pagename = m_page.getName();
    340        String redirURL = m_engine.getManager( CommandResolver.class ).getSpecialPageReference( pagename );
    341        if( redirURL == null ) {
    342            final String alias = m_page.getAttribute( WikiPage.ALIAS );
    343            if( alias != null ) {
    344                redirURL = getViewURL( alias );
    345            } else {
    346                redirURL = m_page.getAttribute( WikiPage.REDIRECT );
    347            }
    348        }
    349
    350        return redirURL;
    351    }
    352
    353    /**
    354     *  Returns the handling engine.
    355     *
    356     *  @return The engine owning this context.
    357     */
    358    @Override
    359    public WikiEngine getEngine() {
    360        return ( WikiEngine )m_engine;
    361    }
    362
    363    /**
    364     *  Returns the page that is being handled.
    365     *
    366     *  @return the page which was fetched.
    367     */
    368    @Override
    369    public WikiPage getPage()
    370    {
    371        return m_page;
    372    }
    373
    374    /**
    375     *  Sets the page that is being handled.
    376     *
    377     *  @param page The wikipage
    378     *  @since 2.1.37.
    379     */
    380    @Override
    381    public void setPage( final Page page ) {
    382        m_page = (WikiPage)page;
    383        updateCommand( m_command.getRequestContext() );
    384    }
    385
    386    /**
    387     *  Returns the request context.
    388     *
    389     *  @return The name of the request context (e.g. VIEW).
    390     */
    391    @Override
    392    public String getRequestContext()
    393    {
    394        return m_command.getRequestContext();
    395    }
    396
    397    /**
    398     *  Sets the request context.  See above for the different request contexts (VIEW, EDIT, etc.)
    399     *
    400     *  @param arg The request context (one of the predefined contexts.)
    401     */
    402    @Override
    403    public void setRequestContext( final String arg )
    404    {
    405        updateCommand( arg );
    406    }
    407
    408    /**
    409     * {@inheritDoc}
    410     * @see org.apache.wiki.api.core.Command#getTarget()
    411     */
    412    @Override
    413    public Object getTarget()
    414    {
    415        return m_command.getTarget();
    416    }
    417
    418    /**
    419     * {@inheritDoc}
    420     * @see org.apache.wiki.api.core.Command#getURLPattern()
    421     */
    422    @Override
    423    public String getURLPattern()
    424    {
    425        return m_command.getURLPattern();
    426    }
    427
    428    /**
    429     *  Gets a previously set variable.
    430     *
    431     *  @param key The variable name.
    432     *  @return The variable contents.
    433     */
    434    @Override
    435    @SuppressWarnings( "unchecked" )
    436    public < T > T getVariable( final String key ) {
    437        return ( T )m_variableMap.get( key );
    438    }
    439
    440    /**
    441     *  Sets a variable.  The variable is valid while the WikiContext is valid, i.e. while page processing continues.  The variable data
    442     *  is discarded once the page processing is finished.
    443     *
    444     *  @param key The variable name.
    445     *  @param data The variable value.
    446     */
    447    @Override
    448    public void setVariable( final String key, final Object data ) {
    449        m_variableMap.put( key, data );
    450        updateCommand( m_command.getRequestContext() );
    451    }
    452
    453    /**
    454     * This is just a simple helper method which will first check the context if there is already an override in place, and if there is not,
    455     * it will then check the given properties.
    456     *
    457     * @param key What key are we searching for?
    458     * @param defValue Default value for the boolean
    459     * @return {@code true} or {@code false}.
    460     */
    461    @Override
    462    public boolean getBooleanWikiProperty( final String key, final boolean defValue ) {
    463        final String bool = getVariable( key );
    464        if( bool != null ) {
    465            return TextUtil.isPositive( bool );
    466        }
    467
    468        return TextUtil.getBooleanProperty( getEngine().getWikiProperties(), key, defValue );
    469    }
    470
    471    /**
    472     *  This method will safely return any HTTP parameters that might have been defined.  You should use this method instead
    473     *  of peeking directly into the result of getHttpRequest(), since this method is smart enough to do all the right things,
    474     *  figure out UTF-8 encoded parameters, etc.
    475     *
    476     *  @since 2.0.13.
    477     *  @param paramName Parameter name to look for.
    478     *  @return HTTP parameter, or null, if no such parameter existed.
    479     */
    480    @Override
    481    public String getHttpParameter( final String paramName ) {
    482        String result = null;
    483        if( m_request != null ) {
    484            result = m_request.getParameter( paramName );
    485        }
    486
    487        return result;
    488    }
    489
    490    /**
    491     *  If the request did originate from an HTTP request, then the HTTP request can be fetched here.  However, if the request
    492     *  did NOT originate from an HTTP request, then this method will return null, and YOU SHOULD CHECK FOR IT!
    493     *
    494     *  @return Null, if no HTTP request was done.
    495     *  @since 2.0.13.
    496     */
    497    @Override
    498    public HttpServletRequest getHttpRequest()
    499    {
    500        return m_request;
    501    }
    502
    503    /**
    504     *  Sets the template to be used for this request.
    505     *
    506     *  @param dir The template name
    507     *  @since 2.1.15.
    508     */
    509    @Override
    510    public void setTemplate( final String dir )
    511    {
    512        m_template = dir;
    513    }
    514
    515    /**
    516     * Returns the target of this wiki context: a page, group name or JSP. If the associated Command is a PageCommand, this method
    517     * returns the page's name. Otherwise, this method delegates to the associated Command's {@link org.apache.wiki.api.core.Command#getName()}
    518     * method. Calling classes can rely on the results of this method for looking up canonically-correct page or group names. Because it
    519     * does not automatically assume that the wiki context is a PageCommand, calling this method is inherently safer than calling
    520     * {@code getPage().getName()}.
    521     *
    522     * @return the name of the target of this wiki context
    523     * @see org.apache.wiki.ui.PageCommand#getName()
    524     * @see org.apache.wiki.ui.GroupCommand#getName()
    525     */
    526    @Override
    527    public String getName() {
    528        if ( m_command instanceof PageCommand ) {
    529            return m_page != null ? m_page.getName() : "<no page>";
    530        }
    531        return m_command.getName();
    532    }
    533
    534    /**
    535     *  Gets the template that is to be used throughout this request.
    536     *
    537     *  @since 2.1.15.
    538     *  @return template name
    539     */
    540    @Override
    541    public String getTemplate()
    542    {
    543        return m_template;
    544    }
    545
    546    /**
    547     *  Convenience method that gets the current user. Delegates the lookup to the WikiSession associated with this WikiContect.
    548     *  May return null, in case the current user has not yet been determined; or this is an internal system. If the WikiSession has not
    549     *  been set, <em>always</em> returns null.
    550     *
    551     *  @return The current user; or maybe null in case of internal calls.
    552     */
    553    @Override
    554    public Principal getCurrentUser() {
    555        if (m_session == null) {
    556            // This shouldn't happen, really...
    557            return WikiPrincipal.GUEST;
    558        }
    559        return m_session.getLoginPrincipal();
    560    }
    561
    562    /**
    563     *  A shortcut to generate a VIEW url.
    564     *
    565     *  @param page The page to which to link.
    566     *  @return A URL to the page.  This honours the current absolute/relative setting.
    567     */
    568    @Override
    569    public String getViewURL( final String page ) {
    570        return getURL( ContextEnum.PAGE_VIEW.getRequestContext(), page, null );
    571    }
    572
    573    /**
    574     *  Creates a URL for the given request context.
    575     *
    576     *  @param context e.g. WikiContext.EDIT
    577     *  @param page The page to which to link
    578     *  @return A URL to the page, honours the absolute/relative setting in jspwiki.properties
    579     */
    580    @Override
    581    public String getURL( final String context, final String page ) {
    582        return getURL( context, page, null );
    583    }
    584
    585    /**
    586     *  Returns a URL from a page. It this WikiContext instance was constructed with an actual HttpServletRequest, we will attempt to
    587     *  construct the URL using HttpUtil, which preserves the HTTPS portion if it was used.
    588     *
    589     *  @param context The request context (e.g. WikiContext.UPLOAD)
    590     *  @param page The page to which to link
    591     *  @param params A list of parameters, separated with "&amp;"
    592     *
    593     *  @return A URL to the given context and page.
    594     */
    595    @Override
    596    public String getURL( final String context, final String page, final String params ) {
    597        // FIXME: is rather slow
    598        return m_engine.getURL( context, page, params );
    599    }
    600
    601    /**
    602     * Returns the Command associated with this WikiContext.
    603     *
    604     * @return the command
    605     */
    606    @Override
    607    public Command getCommand() {
    608        return m_command;
    609    }
    610
    611    /**
    612     *  Returns a shallow clone of the WikiContext.
    613     *
    614     *  @since 2.1.37.
    615     *  @return A shallow clone of the WikiContext
    616     */
    617    @Override
    618    public WikiContext clone() {
    619        try {
    620            // super.clone() must always be called to make sure that inherited objects
    621            // get the right type
    622            final WikiContext copy = (WikiContext)super.clone();
    623
    624            copy.m_engine = m_engine;
    625            copy.m_command = m_command;
    626
    627            copy.m_template    = m_template;
    628            copy.m_variableMap = m_variableMap;
    629            copy.m_request     = m_request;
    630            copy.m_session     = m_session;
    631            copy.m_page        = m_page;
    632            copy.m_realPage    = m_realPage;
    633            return copy;
    634        } catch( final CloneNotSupportedException e ){
    635            // Never happens
    636            LOG.debug(e.getMessage(), e);
    637        }
    638
    639        return null;
    640    }
    641
    642    /**
    643     *  Creates a deep clone of the WikiContext.  This is useful when you want to be sure that you don't accidentally mess with page
    644     *  attributes, etc.
    645     *
    646     *  @since  2.8.0
    647     *  @return A deep clone of the WikiContext.
    648     */
    649    @Override
    650    @SuppressWarnings("unchecked")
    651    public WikiContext deepClone() {
    652        try {
    653            // super.clone() must always be called to make sure that inherited objects
    654            // get the right type
    655            final WikiContext copy = (WikiContext)super.clone();
    656
    657            //  No need to deep clone these
    658            copy.m_engine  = m_engine;
    659            copy.m_command = m_command; // Static structure
    660
    661            copy.m_template    = m_template;
    662            copy.m_variableMap = (HashMap<String,Object>)m_variableMap.clone();
    663            copy.m_request     = m_request;
    664            copy.m_session     = m_session;
    665            copy.m_page        = m_page.clone();
    666            copy.m_realPage    = m_realPage.clone();
    667            return copy;
    668        }
    669        catch( final CloneNotSupportedException e ){
    670            LOG.debug(e.getMessage(), e);
    671            // Never happens
    672        }
    673
    674        return null;
    675    }
    676
    677    /**
    678     *  Returns the Session associated with the context. This method is guaranteed to always return a valid Session.
    679     *  If this context was constructed without an associated HttpServletRequest, it will return
    680     *  {@link org.apache.wiki.WikiSession#guestSession(Engine)}.
    681     *
    682     *  @return The Session associated with this context.
    683     */
    684    @Override
    685    public WikiSession getWikiSession() {
    686        return ( WikiSession )m_session;
    687    }
    688
    689    /**
    690     * This method can be used to find the WikiContext programmatically from a JSP PageContext. We check the request context.
    691     * The wiki context, if it exists, is looked up using the key {@link #ATTR_CONTEXT}.
    692     *
    693     * @since 2.4
    694     * @param pageContext the JSP page context
    695     * @return Current WikiContext, or null, of no context exists.
    696     * @deprecated use {@link Context#findContext( PageContext )} instead.
    697     * @see Context#findContext( PageContext )
    698     */
    699    @Deprecated
    700    public static WikiContext findContext( final PageContext pageContext ) {
    701        final HttpServletRequest request = ( HttpServletRequest )pageContext.getRequest();
    702        return ( WikiContext )request.getAttribute( ATTR_CONTEXT );
    703    }
    704
    705    /**
    706     * Returns the permission required to successfully execute this context. For example, a wiki context of VIEW for a certain page
    707     * means that the PagePermission "view" is required for the page. In some cases, no particular permission is required, in which case
    708     * a dummy permission will be returned ({@link java.util.PropertyPermission}<code> "os.name", "read"</code>). This method is guaranteed
    709     * to always return a valid, non-null permission.
    710     *
    711     * @return the permission
    712     * @since 2.4
    713     */
    714    @Override
    715    public Permission requiredPermission() {
    716        // This is a filthy rotten hack -- absolutely putrid
    717        if ( WikiCommand.INSTALL.equals( m_command ) ) {
    718            // See if admin users exists
    719            try {
    720                final UserManager userMgr = m_engine.getManager( UserManager.class );
    721                final UserDatabase userDb = userMgr.getUserDatabase();
    722                userDb.findByLoginName( Installer.ADMIN_ID );
    723            } catch ( final NoSuchPrincipalException e ) {
    724                return DUMMY_PERMISSION;
    725            }
    726            return new AllPermission( m_engine.getApplicationName() );
    727        }
    728
    729        // TODO: we should really break the contract so that this
    730        // method returns null, but until then we will use this hack
    731        if( m_command.requiredPermission() == null ) {
    732            return DUMMY_PERMISSION;
    733        }
    734
    735        return m_command.requiredPermission();
    736    }
    737
    738    /**
    739     * Associates a target with the current Command and returns the new targeted Command. If the Command associated with this
    740     * WikiContext is already "targeted", it is returned instead.
    741     *
    742     * @see org.apache.wiki.api.core.Command#targetedCommand(java.lang.Object)
    743     *
    744     * {@inheritDoc}
    745     */
    746    @Override
    747    public Command targetedCommand( final Object target ) {
    748        if ( m_command.getTarget() == null ) {
    749            return m_command.targetedCommand( target );
    750        }
    751        return m_command;
    752    }
    753
    754    /**
    755     *  Returns true, if the current user has administrative permissions (i.e. the omnipotent AllPermission).
    756     *
    757     *  @since 2.4.46
    758     *  @return true, if the user has all permissions.
    759     */
    760    @Override
    761    public boolean hasAdminPermissions() {
    762        return m_engine.getManager( AuthorizationManager.class ).checkPermission( getWikiSession(), new AllPermission( m_engine.getApplicationName() ) );
    763    }
    764
    765    /**
    766     * Figures out which template a new WikiContext should be using.
    767     * @param request the HTTP request
    768     */
    769    protected void setDefaultTemplate( final HttpServletRequest request ) {
    770        final String defaultTemplate = m_engine.getTemplateDir();
    771
    772        //  Figure out which template we should be using for this page.
    773        String template = null;
    774        if ( request != null ) {
    775            final String skin = request.getParameter( "skin" );
    776            if( skin != null )
    777            {
    778                template = skin.replaceAll("\\p{Punct}", "");
    779            }
    780
    781        }
    782
    783        // If request doesn't supply the value, extract from wiki page
    784        if( template == null ) {
    785            final WikiPage page = getPage();
    786            if ( page != null ) {
    787                template = page.getAttribute( Engine.PROP_TEMPLATEDIR );
    788            }
    789
    790        }
    791
    792        // If something over-wrote the default, set the new value.
    793        if ( template != null ) {
    794            setTemplate( template );
    795        } else {
    796            setTemplate( defaultTemplate );
    797        }
    798    }
    799
    800    /**
    801     * Looks up and returns a PageCommand based on a supplied WikiPage and HTTP request. First, the appropriate Command is obtained by
    802     * examining the HTTP request; the default is {@link ContextEnum#PAGE_VIEW}. If the Command is a PageCommand (and it should be, in most
    803     * cases), a targeted Command is created using the (non-<code>null</code>) WikiPage as target.
    804     *
    805     * @param engine the wiki engine
    806     * @param request the HTTP request
    807     * @param page the wiki page
    808     * @return the correct command
    809     */
    810    protected static Command findCommand( final Engine engine, final HttpServletRequest request, final Page page ) {
    811        final String defaultContext = ContextEnum.PAGE_VIEW.getRequestContext();
    812        Command command = engine.getManager( CommandResolver.class ).findCommand( request, defaultContext );
    813        if ( command instanceof PageCommand && page != null ) {
    814            command = command.targetedCommand( page );
    815        }
    816        return command;
    817    }
    818
    819    /**
    820     * Protected method that updates the internally cached Command. Will always be called when the page name, request context, or variable
    821     * changes.
    822     *
    823     * @param requestContext the desired request context
    824     * @since 2.4
    825     */
    826    protected void updateCommand( final String requestContext ) {
    827        if ( requestContext == null ) {
    828            m_command = PageCommand.NONE;
    829        } else {
    830            final CommandResolver resolver = m_engine.getManager( CommandResolver.class );
    831            m_command = resolver.findCommand( m_request, requestContext );
    832        }
    833
    834        if ( m_command instanceof PageCommand && m_page != null ) {
    835            m_command = m_command.targetedCommand( m_page );
    836        }
    837    }
    838
    839}