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