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.url;
020
021import java.io.UnsupportedEncodingException;
022import java.util.Properties;
023
024import javax.servlet.http.HttpServletRequest;
025
026import org.apache.commons.lang.StringUtils;
027import org.apache.wiki.WikiContext;
028import org.apache.wiki.WikiEngine;
029import org.apache.wiki.ui.Command;
030import org.apache.wiki.ui.CommandResolver;
031import org.apache.wiki.util.TextUtil;
032
033/**
034 *  Implements the default URL constructor using links directly to the
035 *  JSP pages.  This is what JSPWiki by default is using.  For example,
036 *  WikiContext.VIEW points at "Wiki.jsp", etc.
037 *
038 *  @since 2.2
039 */
040public class DefaultURLConstructor
041    implements URLConstructor
042{
043    protected WikiEngine m_engine;
044
045    /**
046     *  Contains the absolute path of the JSPWiki Web application without the
047     *  actual servlet (which is the m_urlPrefix).
048     */
049    protected String m_pathPrefix = "";
050
051    /**
052     *
053     * {@inheritDoc}
054     */
055    public void initialize( WikiEngine engine,
056                            Properties properties )
057    {
058        m_engine = engine;
059
060        m_pathPrefix = engine.getBaseURL() + "/";
061    }
062
063    /**
064     *  Does replacement of some particular variables.  The variables are:
065     *
066     *  <ul>
067     *  <li> "%u" - inserts either the base URL (when absolute is required), or the base path
068     *       (which is an absolute path without the host name).
069     *  <li> "%U" - always inserts the base URL
070     *  <li> "%p" - always inserts the base path
071     *  <li> "%n" - inserts the page name
072     *  </ul>
073     *
074     * @param baseptrn  The pattern to use
075     * @param name The page name
076     * @param absolute If true, %u is always the entire base URL, otherwise it depends on
077     *                 the setting in jspwiki.properties.
078     * @return A replacement.
079     */
080    protected final String doReplacement( String baseptrn, String name, boolean absolute )
081    {
082        String baseurl = m_pathPrefix;
083
084        if( absolute ) baseurl = m_engine.getBaseURL() + "/";
085
086        baseptrn = TextUtil.replaceString( baseptrn, "%u", baseurl );
087        baseptrn = TextUtil.replaceString( baseptrn, "%U", m_engine.getBaseURL() );
088        baseptrn = TextUtil.replaceString( baseptrn, "%n", encodeURI(name) );
089        baseptrn = TextUtil.replaceString( baseptrn, "%p", m_pathPrefix );
090
091        return baseptrn;
092    }
093
094    /**
095     *  URLEncoder returns pluses, when we want to have the percent
096     *  encoding.  See http://issues.apache.org/bugzilla/show_bug.cgi?id=39278
097     *  for more info.
098     *
099     *  We also convert any %2F's back to slashes to make nicer-looking URLs.
100     */
101    private String encodeURI( String uri )
102    {
103        uri = m_engine.encodeName(uri);
104
105        uri = StringUtils.replace( uri, "+", "%20" );
106        uri = StringUtils.replace( uri, "%2F", "/" );
107
108        return uri;
109    }
110
111    /**
112     * Returns the URL pattern for a supplied wiki request context.
113     * @param context the wiki context
114     * @param name the wiki page
115     * @return A pattern for replacement.
116     * @throws IllegalArgumentException if the context cannot be found
117     */
118    public static String getURLPattern( String context, String name )
119        throws IllegalArgumentException
120    {
121        if( context.equals(WikiContext.VIEW) && name == null)
122        {
123            // FIXME
124            return "%uWiki.jsp";
125        }
126
127        // Find the action matching our pattern (could throw exception)
128        Command command = CommandResolver.findCommand( context );
129
130        return command.getURLPattern();
131    }
132
133    /**
134     *  Constructs the actual URL based on the context.
135     */
136    private String makeURL( String context,
137                            String name,
138                            boolean absolute )
139    {
140        return doReplacement( getURLPattern(context,name), name, absolute );
141    }
142
143    /**
144     *  Constructs the URL with a bunch of parameters.
145     *  @param parameters If null or empty, no parameters are added.
146     *
147     *  {@inheritDoc}
148     */
149    public String makeURL( String context,
150                           String name,
151                           boolean absolute,
152                           String parameters )
153    {
154        if( parameters != null && parameters.length() > 0 )
155        {
156            if( context.equals(WikiContext.ATTACH) )
157            {
158                parameters = "?"+parameters;
159            }
160            else if( context.equals(WikiContext.NONE) )
161            {
162                parameters = (name.indexOf('?') != -1 ) ? "&amp;" : "?" + parameters;
163            }
164            else
165            {
166                parameters = "&amp;"+parameters;
167            }
168        }
169        else
170        {
171            parameters = "";
172        }
173        return makeURL( context, name, absolute )+parameters;
174    }
175
176    /**
177     *  Should parse the "page" parameter from the actual
178     *  request.
179     *
180     *  {@inheritDoc}
181     */
182    public String parsePage( String context,
183                             HttpServletRequest request,
184                             String encoding )
185        throws UnsupportedEncodingException
186    {
187        String pagereq = request.getParameter( "page" );
188
189        if( context.equals(WikiContext.ATTACH) )
190        {
191            pagereq = parsePageFromURL( request, encoding );
192        }
193
194        return pagereq;
195    }
196
197    /**
198     *  There's a bug in Tomcat until 5.5.16 at least: The "+" sign is not
199     *  properly decoded by the servlet container, and therefore request.getPathInfo()
200     *  will return faulty results for paths which contains + signs to signify spaces.
201     *  <p>
202     *  This method provides a workaround by simply parsing the getRequestURI(), which
203     *  is returned from the servlet container undedecoded.
204     *  <p>
205     *  Please see <a href="http://issues.apache.org/bugzilla/show_bug.cgi?id=39278">Tomcat Bug 39278</a>
206     *  for more information.
207     *
208     *  @param request A HTTP servlet request
209     *  @param encoding The used encoding
210     *  @return a String, decoded by JSPWiki, specifying extra path information that comes
211     *          after the servlet path but before the query string in the request URL;
212     *          or null if the URL does not have any extra path information
213     *  @throws UnsupportedEncodingException
214     */
215    /*
216    private static String getPathInfo( HttpServletRequest request, String encoding )
217        throws UnsupportedEncodingException
218    {
219        String c = request.getContextPath(); // Undecoded
220        String s = request.getServletPath(); // Decoded
221        String u = request.getRequestURI();  // Undecoded
222
223        c = URLDecoder.decode( c, encoding );
224        u = URLDecoder.decode( u, encoding );
225
226        String pi = u.substring( s.length()+c.length() );
227
228        if( pi.length() == 0 ) pi = null;
229
230        return pi;
231    }
232    */
233    /**
234     *  Takes the name of the page from the request URI.
235     *  The initial slash is also removed.  If there is no page,
236     *  returns null.
237     *
238     *  @param request The request to parse
239     *  @param encoding The encoding to use
240     *
241     *  @return a parsed page name, or null, if it cannot be found
242     *
243     *  @throws UnsupportedEncodingException If the encoding is not recognized.
244     */
245    public static String parsePageFromURL( HttpServletRequest request,
246                                           String encoding )
247        throws UnsupportedEncodingException
248    {
249        String name = request.getPathInfo();
250
251        if( name == null || name.length() <= 1 )
252        {
253            return null;
254        }
255        else if( name.charAt(0) == '/' )
256        {
257            name = name.substring(1);
258        }
259
260        //
261        //  This is required, because by default all URLs are handled
262        //  as Latin1, even if they are really UTF-8.
263        //
264
265        // name = TextUtil.urlDecode( name, encoding );
266
267        return name;
268    }
269
270
271    /**
272     *  This method is not needed for the DefaultURLConstructor.
273     *
274     * @param request The HTTP Request that was used to end up in this page.
275     * @return "Wiki.jsp", "PageInfo.jsp", etc.  Just return the name,
276     *         JSPWiki will figure out the page.
277     */
278    public String getForwardPage( HttpServletRequest request )
279    {
280        return "Wiki.jsp";
281    }
282}