View Javadoc
1   /*
2    * Copyright (c) 2002-2022, City of Paris
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without
6    * modification, are permitted provided that the following conditions
7    * are met:
8    *
9    *  1. Redistributions of source code must retain the above copyright notice
10   *     and the following disclaimer.
11   *
12   *  2. Redistributions in binary form must reproduce the above copyright notice
13   *     and the following disclaimer in the documentation and/or other materials
14   *     provided with the distribution.
15   *
16   *  3. Neither the name of 'Mairie de Paris' nor 'Lutece' nor the names of its
17   *     contributors may be used to endorse or promote products derived from
18   *     this software without specific prior written permission.
19   *
20   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
24   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30   * POSSIBILITY OF SUCH DAMAGE.
31   *
32   * License 1.0
33   */
34  package fr.paris.lutece.portal.service.util;
35  
36  import java.io.File;
37  import java.io.FileInputStream;
38  import java.io.IOException;
39  import java.text.MessageFormat;
40  import java.util.Enumeration;
41  import java.util.StringTokenizer;
42  
43  import javax.servlet.ServletContext;
44  import javax.servlet.http.HttpServletRequest;
45  import javax.servlet.http.HttpSession;
46  
47  import org.apache.commons.lang3.StringUtils;
48  
49  import fr.paris.lutece.portal.service.datastore.DatastoreService;
50  import fr.paris.lutece.portal.service.message.SiteMessageService;
51  import fr.paris.lutece.portal.service.security.SecurityTokenService;
52  import fr.paris.lutece.portal.web.LocalVariables;
53  import fr.paris.lutece.portal.web.constants.Parameters;
54  import fr.paris.lutece.util.ReferenceList;
55  import fr.paris.lutece.util.stream.StreamUtil;
56  import fr.paris.lutece.util.string.StringUtil;
57  import fr.paris.lutece.util.url.UrlItem;
58  
59  /**
60   * this class provides services for locate repository or url
61   */
62  public final class AppPathService
63  {
64      public static final String SESSION_BASE_URL = "base_url";
65      private static final String MSG_LOG_PROPERTY_NOT_FOUND = "Property {0} not found in the properties file ";
66      private static final int PORT_NUMBER_HTTP = 80;
67      private static final String PROPERTY_BASE_URL = "lutece.base.url";
68      private static final String PROPERTY_PORTAL_URL = "lutece.portal.path";
69      private static final String PROPERTY_SITE_MESSAGE_URL = "lutece.siteMessage.path";
70      private static final String PROPERTY_ADMIN_URL = "lutece.admin.path";
71      private static final String PROPERTY_ADMIN_MENU_URL = "lutece.admin.menu.url";
72      private static final String PROPERTY_PORTAL_REDIRECT_URL = "lutece.portal.redirect.url";
73      private static final String PROPERTY_VIRTUAL_HOST_KEYS = "virtualHostKeys";
74      private static final String PROPERTY_VIRTUAL_HOST_KEY_PARAMETER = "virtualHostKey.parameterName";
75      private static final String PROPERTY_VIRTUAL_HOST = "virtualHost.";
76      private static final String PROPERTY_PREFIX_URL = "url.";
77      private static final String PROPERTY_PROD_BASE_URL = "lutece.prod.url";
78      private static final String PROPERTY_INSTANCE = "lutece.webapp.instance";
79      private static final String INSTANCE_DEFAULT = "default";
80      private static final String SUFFIX_BASE_URL = ".baseUrl";
81      private static final String SUFFIX_DESCRIPTION = ".description";
82      private static final String SLASH = "/";
83      private static final String DOUBLE_POINTS = ":";
84  
85      // Datastore keys
86      private static final String KEY_ADMIN_HOME_URL = "portal.site.site_property.admin_home_url";
87      private static final String KEY_PORTAL_HOME_URL = "portal.site.site_property.home_url";
88      private static String _strWebAppPath;
89  
90      /**
91       * Creates a new AppPathService object.
92       */
93      private AppPathService( )
94      {
95      }
96  
97      /**
98       * Initialize The path service
99       *
100      * @param context
101      *            The servlet context
102      */
103     public static void init( ServletContext context )
104     {
105         String strRealPath = context.getRealPath( "/" );
106         _strWebAppPath = normalizeWebappPath( strRealPath );
107     }
108 
109     /**
110      * Initialize The webapppath
111      *
112      * @param strWebAppPath
113      *            The Webapp path
114      */
115     public static void init( String strWebAppPath )
116     {
117         _strWebAppPath = normalizeWebappPath( strWebAppPath );
118     }
119 
120     /**
121      * Returns the absolute path of a repository from a relative definition in properties file
122      *
123      *
124      * @return the repository absolute path
125      * @param strKey
126      *            the repository key definied in properties file
127      */
128     public static String getPath( String strKey )
129     {
130         // Adds relative path found from strKey
131         String strDirectory = AppPropertiesService.getProperty( strKey );
132 
133         if ( strDirectory == null )
134         {
135             Object [ ] propertyMissing = {
136                     strKey
137             };
138             String strMsg = MessageFormat.format( MSG_LOG_PROPERTY_NOT_FOUND, propertyMissing );
139             throw new AppException( strMsg );
140         }
141 
142         return getWebAppPath( ) + strDirectory;
143     }
144 
145     /**
146      * Returns the webapp path from the properties file
147      *
148      *
149      * @return the webapp path
150      */
151     public static String getWebAppPath( )
152     {
153         return _strWebAppPath;
154     }
155 
156     /**
157      * Returns the absolute path of file from its relative definition in properties file.
158      *
159      * @param strKey
160      *            the repository key defined in properties file
161      * @param strFilename
162      *            The name of file
163      * @return the absolute path of file
164      */
165     public static String getPath( String strKey, String strFilename )
166     {
167         return getPath( strKey ) + SLASH + strFilename;
168     }
169 
170     /**
171      * Gets a file as stream
172      *
173      * @param strPath
174      *            the path
175      * @param strFilename
176      *            The name of file
177      * @return a FileInput Stream object
178      */
179     public static FileInputStream getResourceAsStream( String strPath, String strFilename )
180     {
181         String strFilePath = getWebAppPath( ) + strPath + strFilename;
182 
183         FileInputStream fis = null;
184         try
185         {
186             File file = new File( strFilePath );
187             fis = new FileInputStream( file );
188 
189             return fis;
190         }
191         catch( IOException e )
192         {
193             StreamUtil.safeClose( fis );
194             throw new AppException( "Unable to get file : " + strFilePath );
195         }
196     }
197 
198     /**
199      * Returns the absolute path of a repository from a relative path
200      *
201      *
202      * @return the repository absolute path
203      * @param strDirectory
204      *            the relative path
205      */
206     public static String getAbsolutePathFromRelativePath( String strDirectory )
207     {
208         return _strWebAppPath + strDirectory;
209     }
210 
211     /**
212      * Return the url of the webapp, built from the request
213      *
214      * @param request
215      *            The HttpServletRequest
216      * @return strBase the webapp url
217      */
218     public static String getBaseUrl( HttpServletRequest request )
219     {
220         if ( request == null )
221         {
222             return getBaseUrl( );
223         }
224 
225         String strBase;
226 
227         // Search for a Virtual Host Base Url defined in the request
228         strBase = getVirtualHostBaseUrl( request );
229 
230         // If not found, get the base url from session
231         if ( ( strBase == null ) || strBase.equals( StringUtils.EMPTY ) )
232         {
233             HttpSession session = request.getSession( false );
234 
235             if ( session != null )
236             {
237                 Object oBase = session.getAttribute( SESSION_BASE_URL );
238 
239                 if ( oBase != null )
240                 {
241                     strBase = (String) oBase;
242                 }
243             }
244         }
245 
246         // If not found, get the base url from the config.properties
247         if ( ( strBase == null ) || ( strBase.equals( StringUtils.EMPTY ) ) )
248         {
249             strBase = AppPropertiesService.getProperty( PROPERTY_BASE_URL );
250         }
251 
252         if ( ( strBase == null ) || ( strBase.equals( StringUtils.EMPTY ) ) )
253         {
254             // Dynamic base URL if not defined in the properties
255             strBase = request.getScheme( ) + DOUBLE_POINTS + SLASH + SLASH + request.getServerName( );
256 
257             int nPort = request.getServerPort( );
258 
259             if ( nPort != PORT_NUMBER_HTTP )
260             {
261                 strBase += ( DOUBLE_POINTS + nPort );
262             }
263 
264             strBase += request.getContextPath( );
265         }
266 
267         if ( !strBase.endsWith( SLASH ) )
268         {
269             strBase += SLASH;
270         }
271 
272         return strBase;
273     }
274 
275     /**
276      * Return the url of the webapp. The method should only be used out of request context (by daemons for example). If there is a request context, use
277      * {@link AppPathService#getBaseUrl(HttpServletRequest)} instead.
278      *
279      * @return The webapp url, or null if the 'lutece.base.url' property has not been set.
280      * @deprecated Use {@link AppPathService#getBaseUrl(HttpServletRequest)} instead
281      */
282     @Deprecated
283     public static String getBaseUrl( )
284     {
285         HttpServletRequest request = LocalVariables.getRequest( );
286 
287         if ( request != null )
288         {
289             return getBaseUrl( request );
290         }
291 
292         // FIXME : lutece.base.url is only set when using WSSO
293         String strBaseUrl = AppPropertiesService.getProperty( PROPERTY_BASE_URL );
294 
295         if ( strBaseUrl == null )
296         {
297             strBaseUrl = StringUtils.EMPTY;
298         }
299         else
300         {
301             if ( !strBaseUrl.endsWith( SLASH ) )
302             {
303                 strBaseUrl += SLASH;
304             }
305         }
306 
307         return strBaseUrl;
308     }
309 
310     /**
311      * Return the webapp prod url (or the base url if no prod url has been definied).
312      *
313      * @param request
314      *            The HTTP request
315      * @return The prod url
316      */
317     public static String getProdUrl( HttpServletRequest request )
318     {
319         String strBaseUrl = AppPropertiesService.getProperty( PROPERTY_PROD_BASE_URL );
320 
321         if ( StringUtils.isBlank( strBaseUrl ) )
322         {
323             strBaseUrl = getBaseUrl( request );
324         }
325 
326         if ( !strBaseUrl.endsWith( SLASH ) )
327         {
328             strBaseUrl += SLASH;
329         }
330 
331         return strBaseUrl;
332     }
333 
334     /**
335      * Return the webapp prod url. If no prod URL has been defined, then the base URL is returned
336      *
337      * @param strBaseUrl
338      *            The base URL
339      * @return The prod url
340      */
341     public static String getProdUrl( String strBaseUrl )
342     {
343         String strProdUrl = AppPropertiesService.getProperty( PROPERTY_PROD_BASE_URL );
344 
345         if ( StringUtils.isBlank( strProdUrl ) )
346         {
347             strProdUrl = strBaseUrl;
348         }
349 
350         if ( ( strProdUrl != null ) && !strProdUrl.endsWith( SLASH ) )
351         {
352             strProdUrl += SLASH;
353         }
354 
355         return strProdUrl;
356     }
357 
358     /**
359      * Return the url of the webapp, built from the request
360      *
361      * @param request
362      *            The HttpServletRequest
363      * @return strBase the webapp url
364      */
365     public static String getSiteMessageUrl( HttpServletRequest request )
366     {
367         // Set the site message url
368         return SiteMessageService.setSiteMessageUrl( getBaseUrl( request ) + getSiteMessageUrl( ) );
369     }
370 
371     /**
372      * Returns the portal page relative url (jsp/site/Portal.jsp) defined in lutece.properties
373      *
374      * @return the Portal Url
375      */
376     public static String getPortalUrl( )
377     {
378         return AppPropertiesService.getProperty( PROPERTY_PORTAL_URL );
379     }
380 
381     /**
382      * Returns the forward URL for webapp's root path. Default is (jsp/site/Portal.jsp) defined in lutece.properties
383      *
384      * @return the Portal Root forward Url
385      */
386     public static String getRootForwardUrl( )
387     {
388         return DatastoreService.getDataValue( KEY_PORTAL_HOME_URL, AppPropertiesService.getProperty( PROPERTY_PORTAL_REDIRECT_URL ) );
389     }
390 
391     /**
392      * Returns the Site Message relative url (jsp/site/SiteMessage.jsp) defined in lutece.properties
393      *
394      * @return the SiteMessage Url
395      */
396     public static String getSiteMessageUrl( )
397     {
398         return AppPropertiesService.getProperty( PROPERTY_SITE_MESSAGE_URL );
399     }
400 
401     /**
402      * Returns the admin portal page relative url (jsp/admin/site/AdminSite.jsp) defined in lutece.properties
403      *
404      * @return the Portal Url
405      */
406     public static String getAdminPortalUrl( )
407     {
408         return AppPropertiesService.getProperty( PROPERTY_ADMIN_URL );
409     }
410 
411     /**
412      * Returns the admin menu page relative url (jsp/admin/site/AdminMenu.jsp) defined in lutece.properties
413      *
414      * @return the Admin Menu Url
415      */
416     public static String getAdminMenuUrl( )
417     {
418         return DatastoreService.getDataValue( KEY_ADMIN_HOME_URL, AppPropertiesService.getProperty( PROPERTY_ADMIN_MENU_URL ) );
419     }
420 
421     /**
422      * Normalizes the Webapp Path
423      *
424      * @param strPath
425      *            The path to normalize
426      * @return The normalized path
427      */
428     private static String normalizeWebappPath( String strPath )
429     {
430         String strNormalized = strPath;
431 
432         // For windows, remove the leading \
433         if ( ( strNormalized.length( ) > 3 ) && ( strNormalized.indexOf( ':' ) == 2 ) )
434         {
435             strNormalized = strNormalized.substring( 1 );
436         }
437 
438         // convert Windows path separator if present
439         strNormalized = StringUtil.substitute( strNormalized, "/", "\\" );
440 
441         // remove the ending separator if present
442         if ( strNormalized.endsWith( "/" ) )
443         {
444             strNormalized = strNormalized.substring( 0, strNormalized.length( ) - 1 );
445         }
446 
447         return strNormalized;
448     }
449 
450     // //////////////////////////////////////////////////////////////////////////
451     // Multiple virtual hosts configuration management
452     /**
453      * Gets available virtual hosts defined in the config.properties
454      *
455      * @return A reference list containing the key and the description of each virtual host configuration. The list is empty if there is no configuration
456      *         defined.
457      */
458     public static ReferenceList getAvailableVirtualHosts( )
459     {
460         ReferenceList list = null;
461 
462         // Get keys list form config.properties
463         String strKeysList = AppPropertiesService.getProperty( PROPERTY_VIRTUAL_HOST_KEYS );
464 
465         if ( strKeysList != null )
466         {
467             list = new ReferenceList( );
468 
469             // Extracts each key (separated by a comma)
470             StringTokenizer strTokens = new StringTokenizer( strKeysList, "," );
471 
472             while ( strTokens.hasMoreTokens( ) )
473             {
474                 String strHostKey = strTokens.nextToken( );
475                 String strHostKeyDescription = AppPropertiesService.getProperty( PROPERTY_VIRTUAL_HOST + strHostKey + SUFFIX_DESCRIPTION );
476                 list.addItem( strHostKey, strHostKeyDescription );
477             }
478         }
479 
480         return list;
481     }
482 
483     /**
484      * Gets a Virtual Host Key if the request contains a virtual host key
485      *
486      * @param request
487      *            The HTTP request
488      * @return A Virtual Host Key if present, otherwise null.
489      */
490     public static String getVirtualHostKey( HttpServletRequest request )
491     {
492         String strVirtalHostKey = null;
493 
494         // Get from config.properties the parameter name for virtual host keys
495         String strVirtualHostKeyParameter = AppPropertiesService.getProperty( PROPERTY_VIRTUAL_HOST_KEY_PARAMETER );
496 
497         if ( ( request != null ) && ( strVirtualHostKeyParameter != null ) && ( !strVirtualHostKeyParameter.equals( "" ) ) )
498         {
499             // Search for this parameter into the request
500             strVirtalHostKey = request.getParameter( strVirtualHostKeyParameter );
501         }
502 
503         return strVirtalHostKey;
504     }
505 
506     /**
507      * Gets a Base Url for a virtual host if the request contains a virtual host key
508      *
509      * @param request
510      *            The HTTP request
511      * @return A virtual host base url if present, otherwise null.
512      */
513     private static String getVirtualHostBaseUrl( HttpServletRequest request )
514     {
515         String strBaseUrl = null;
516         String strVirtalHostKey = getVirtualHostKey( request );
517 
518         if ( ( strVirtalHostKey != null ) && ( !strVirtalHostKey.equals( "" ) ) )
519         {
520             // If found gets the Base url for this virtual host by its key
521             strBaseUrl = AppPropertiesService.getProperty( PROPERTY_VIRTUAL_HOST + strVirtalHostKey + SUFFIX_BASE_URL );
522         }
523 
524         return strBaseUrl;
525     }
526 
527     /**
528      * Build the url item to use for a url that includes the redirection parameter for reconnection.
529      *
530      * @param strRootUrl
531      *            the root part of the url, to build an absolute url
532      * @param strUrlPropertySuffixKey
533      *            The property suffix to retrieve the url
534      * @return an absolute url, completed with the redirectUrl parameter (contains the relative part of the url), as an UrlItem
535      */
536     public static UrlItem buildRedirectUrlItem( String strRootUrl, String strUrlPropertySuffixKey )
537     {
538         String strUrl = AppPropertiesService.getProperty( PROPERTY_PREFIX_URL + strUrlPropertySuffixKey );
539         UrlItem/url/UrlItem.html#UrlItem">UrlItem url = new UrlItem( strRootUrl + strUrl );
540         url.addParameter( Parameters.REDIRECT_URL, strUrlPropertySuffixKey );
541 
542         return url;
543     }
544 
545     /**
546      * Retrieve the url to redirect to after login. It is given by the redirectUrl parameter if found. The request parameters are copied (except the login and
547      * acces code and token). This is to be used by the doLogin method of AdminLoginJspBean.
548      *
549      * @param request
550      *            the http request
551      * @param strDefaultRedirectUrl
552      *            the default url to go to after login
553      * @return an UrlItem corresponding to the url to redirect to after login.
554      */
555     public static UrlItem resolveRedirectUrl( HttpServletRequest request, String strDefaultRedirectUrl )
556     {
557         String strUrl = strDefaultRedirectUrl;
558 
559         String strUrlKey = request.getParameter( Parameters.REDIRECT_URL );
560         String strRedirectUrl = null;
561 
562         if ( strUrlKey != null )
563         {
564             strRedirectUrl = AppPropertiesService.getProperty( PROPERTY_PREFIX_URL + strUrlKey );
565         }
566 
567         if ( strRedirectUrl != null )
568         {
569             strUrl = strRedirectUrl;
570         }
571 
572         Enumeration enumParams = request.getParameterNames( );
573         UrlItem/url/UrlItem.html#UrlItem">UrlItem url = new UrlItem( getBaseUrl( request ) + strUrl );
574 
575         String strParamName;
576 
577         while ( enumParams.hasMoreElements( ) )
578         {
579             strParamName = (String) enumParams.nextElement( );
580 
581             if ( !strParamName.equals( Parameters.REDIRECT_URL ) && !strParamName.equals( Parameters.ACCESS_CODE )
582                     && !strParamName.equals( Parameters.PASSWORD ) && !strParamName.equals( SecurityTokenService.PARAMETER_TOKEN ) )
583             {
584                 url.addParameter( strParamName, request.getParameter( strParamName ) );
585             }
586         }
587 
588         return url;
589     }
590 
591     /**
592      * Returns the absolute url corresponding to the given one, if the later was found to be relative. An url starting with "http://" is absolute. A relative
593      * url should be given relatively to the webapp root.
594      *
595      * @param request
596      *            the http request (provides the base path if needed)
597      * @param strUrl
598      *            the url to transform
599      * @return the corresonding absolute url
600      *
601      *
602      */
603     public static String getAbsoluteUrl( HttpServletRequest request, String strUrl )
604     {
605         if ( ( strUrl != null ) && !strUrl.startsWith( "http://" ) && !strUrl.startsWith( "https://" ) )
606         {
607             return AppPathService.getBaseUrl( request ) + strUrl;
608         }
609 
610         return strUrl;
611     }
612 
613     /**
614      * Gets the webapp instance defined in the config.properties file with the key lutece.webapp.instance
615      *
616      * @return The instance name
617      * @since 4.1
618      */
619     public static String getWebappInstance( )
620     {
621         String strInstance = AppPropertiesService.getProperty( PROPERTY_INSTANCE );
622 
623         if ( ( strInstance != null ) && ( !strInstance.equals( StringUtils.EMPTY ) ) )
624         {
625             return strInstance;
626         }
627 
628         return INSTANCE_DEFAULT;
629     }
630 
631     /**
632      * Returns whether the current instance is the default webapp instance
633      *
634      * @return true if default, otherwise false
635      */
636     public static boolean isDefaultWebappInstance( )
637     {
638         return INSTANCE_DEFAULT.equals( getWebappInstance( ) );
639     }
640 }