001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH (http://www.alkacon.com)
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * For further information about Alkacon Software GmbH, please see the
018 * company website: http://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: http://www.opencms.org
022 *
023 * You should have received a copy of the GNU Lesser General Public
024 * License along with this library; if not, write to the Free Software
025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
026 */
027
028package org.opencms.file;
029
030import org.opencms.main.CmsIllegalArgumentException;
031import org.opencms.main.OpenCms;
032import org.opencms.security.CmsOrganizationalUnit;
033import org.opencms.site.CmsSiteMatcher;
034import org.opencms.util.CmsResourceTranslator;
035import org.opencms.util.CmsUUID;
036import org.opencms.workplace.CmsWorkplace;
037
038import java.util.Hashtable;
039import java.util.Locale;
040import java.util.Map;
041
042/**
043 * Stores the information about the current users OpenCms context,
044 * for example the requested URI, the current project, the selected site and more.<p>
045 *
046 * @since 6.0.0
047 */
048public final class CmsRequestContext {
049
050    /** Request context attribute for indicating that an editor is currently open. */
051    public static final String ATTRIBUTE_EDITOR = CmsRequestContext.class.getName() + ".ATTRIBUTE_EDITOR";
052
053    /** Request context attribute for indicating we want full links generated for HTML fields. */
054    public static final String ATTRIBUTE_FULLLINKS = CmsRequestContext.class.getName() + ".ATTRIBUTE_FULLLINKS";
055
056    /** Request context attribute for indicating the model file for a create resource operation. */
057    public static final String ATTRIBUTE_MODEL = CmsRequestContext.class.getName() + ".ATTRIBUTE_MODEL";
058
059    /** Request context attribute for indicating content locale for a create resource operation. */
060    public static final String ATTRIBUTE_NEW_RESOURCE_LOCALE = CmsRequestContext.class.getName()
061        + ".NEW_RESOURCE_LOCALE";
062
063    /** A map for storing (optional) request context attributes. */
064    private Map<String, Object> m_attributeMap;
065
066    /** The current project. */
067    private CmsProject m_currentProject;
068
069    /** The detail content resource (possibly null). */
070    private CmsResource m_detailResource;
071
072    /** Directory name translator. */
073    private CmsResourceTranslator m_directoryTranslator;
074
075    /** Current encoding. */
076    private String m_encoding;
077
078    /** File name translator. */
079    private CmsResourceTranslator m_fileTranslator;
080
081    /** The secure request flag. */
082    private boolean m_isSecureRequest;
083
084    /** The locale used by this request context. */
085    private Locale m_locale;
086
087    /** The fully qualified name of the organizational unit for this request. */
088    private String m_ouFqn;
089
090    /** The remote ip address. */
091    private String m_remoteAddr;
092
093    /** The current request time. */
094    private long m_requestTime;
095
096    /** The name of the root, e.g. /site_a/vfs. */
097    private String m_siteRoot;
098
099    /** Flag to indicate that this context should not update the user session. */
100    private boolean m_updateSession;
101
102    /** The URI for getUri() in case it is "overwritten".  */
103    private String m_uri;
104
105    /** The current user. */
106    private CmsUser m_user;
107
108    /** the matcher for the current request, that is the host part of the URI from the original http request. */
109    private CmsSiteMatcher m_requestMatcher;
110
111    /**
112     * Constructs a new request context.<p>
113     *
114     * @param user the current user
115     * @param project the current project
116     * @param requestedUri the requested OpenCms VFS URI
117     * @param requestMatcher the matcher for the current request, that is the host part of the URI from the original http request
118     * @param siteRoot the users current site root
119     * @param isSecureRequest true if this is a secure request
120     * @param locale the users current locale
121     * @param encoding the encoding to use for this request
122     * @param remoteAddr the remote IP address of the user
123     * @param requestTime the time of the request (used for resource publication / expiration date)
124     * @param directoryTranslator the directory translator
125     * @param fileTranslator the file translator
126     * @param ouFqn the fully qualified name of the organizational unit
127     */
128    public CmsRequestContext(
129        CmsUser user,
130        CmsProject project,
131        String requestedUri,
132        CmsSiteMatcher requestMatcher,
133        String siteRoot,
134        boolean isSecureRequest,
135        Locale locale,
136        String encoding,
137        String remoteAddr,
138        long requestTime,
139        CmsResourceTranslator directoryTranslator,
140        CmsResourceTranslator fileTranslator,
141        String ouFqn) {
142
143        m_updateSession = true;
144        m_user = user;
145        m_currentProject = project;
146        m_uri = requestedUri;
147        m_requestMatcher = requestMatcher;
148        m_isSecureRequest = isSecureRequest;
149        setSiteRoot(siteRoot);
150        m_locale = locale;
151        m_encoding = encoding;
152        m_remoteAddr = remoteAddr;
153        m_requestTime = requestTime;
154        m_directoryTranslator = directoryTranslator;
155        m_fileTranslator = fileTranslator;
156        setOuFqn(ouFqn);
157    }
158
159    /**
160     * Returns the adjusted site root for a resource using the provided site root as a base.<p>
161     *
162     * Usually, this would be the site root for the current site.
163     * However, if a resource from the <code>/system/</code> folder is requested,
164     * this will be the empty String.<p>
165     *
166     * @param siteRoot the site root of the current site
167     * @param resourcename the resource name to get the adjusted site root for
168     *
169     * @return the adjusted site root for the resource
170     */
171    public static String getAdjustedSiteRoot(String siteRoot, String resourcename) {
172
173        if (resourcename.startsWith(CmsWorkplace.VFS_PATH_SYSTEM)
174            || OpenCms.getSiteManager().startsWithShared(resourcename)) {
175            return "";
176        } else {
177            return siteRoot;
178        }
179    }
180
181    /**
182     * Adds the current site root of this context to the given resource name,
183     * and also translates the resource name with the configured the directory translator.<p>
184     *
185     * @param resourcename the resource name
186     * @return the translated resource name including site root
187     * @see #addSiteRoot(String, String)
188     */
189    public String addSiteRoot(String resourcename) {
190
191        return addSiteRoot(m_siteRoot, resourcename);
192    }
193
194    /**
195     * Adds the given site root of this context to the given resource name,
196     * taking into account special folders like "/system" where no site root must be added,
197     * and also translates the resource name with the configured the directory translator.<p>
198     *
199     * @param siteRoot the site root to add
200     * @param resourcename the resource name
201     * @return the translated resource name including site root
202     */
203    public String addSiteRoot(String siteRoot, String resourcename) {
204
205        if ((resourcename == null) || (siteRoot == null)) {
206            return null;
207        }
208        siteRoot = getAdjustedSiteRoot(siteRoot, resourcename);
209        StringBuffer result = new StringBuffer(128);
210        result.append(siteRoot);
211        if (((siteRoot.length() == 0) || (siteRoot.charAt(siteRoot.length() - 1) != '/'))
212            && ((resourcename.length() == 0) || (resourcename.charAt(0) != '/'))) {
213            // add slash between site root and resource if required
214            result.append('/');
215        }
216        result.append(resourcename);
217        return m_directoryTranslator.translateResource(result.toString());
218    }
219
220    /**
221     * Returns the current project of the current user.
222     *
223     * @return the current project of the current user
224     *
225     * @deprecated use {@link #getCurrentProject()} instead
226     */
227    @Deprecated
228    public CmsProject currentProject() {
229
230        return getCurrentProject();
231    }
232
233    /**
234     * Returns the current user object.<p>
235     *
236     * @return the current user object
237     *
238     * @deprecated use {@link #getCurrentUser()} instead
239     */
240    @Deprecated
241    public CmsUser currentUser() {
242
243        return getCurrentUser();
244    }
245
246    /**
247     * Returns the adjusted site root for a resoure this context current site root.<p>
248     *
249     * @param resourcename the resource name to get the adjusted site root for
250     * @return the adjusted site root for the resoure
251     * @see #getAdjustedSiteRoot(String, String)
252     */
253    public String getAdjustedSiteRoot(String resourcename) {
254
255        return getAdjustedSiteRoot(m_siteRoot, resourcename);
256    }
257
258    /**
259     * Gets the value of an attribute from the OpenCms request context attribute list.<p>
260     *
261     * @param attributeName the attribute name
262     * @return Object the attribute value, or <code>null</code> if the attribute was not found
263     */
264    public Object getAttribute(String attributeName) {
265
266        if (m_attributeMap == null) {
267            return null;
268        }
269        return m_attributeMap.get(attributeName);
270    }
271
272    /**
273     * Returns the current project of the current user.
274     *
275     * @return the current project of the current user
276     */
277    public CmsProject getCurrentProject() {
278
279        return m_currentProject;
280    }
281
282    /**
283     * Returns the current user object.<p>
284     *
285     * @return the current user object
286     */
287    public CmsUser getCurrentUser() {
288
289        return m_user;
290    }
291
292    /**
293     * Gets the detail content structure id (or null if no detail content has been loaded).<p>
294     *
295     * @return the detail content id
296     */
297    public CmsUUID getDetailContentId() {
298
299        if (m_detailResource == null) {
300            return null;
301        }
302        return m_detailResource.getStructureId();
303    }
304
305    /**
306     * Gets the detail content resource (or null if no detail content has been loaded).<p>
307     *
308     * @return the detail content resource
309     */
310    public CmsResource getDetailResource() {
311
312        return m_detailResource;
313    }
314
315    /**
316     * Returns the directory name translator this context was initialized with.<p>
317     *
318     * The directory translator is used to translate old VFS path information
319     * to a new location. Example: <code>/bodys/index.html --> /system/bodies/</code>.<p>
320     *
321     * @return the directory name translator this context was initialized with
322     */
323    public CmsResourceTranslator getDirectoryTranslator() {
324
325        return m_directoryTranslator;
326    }
327
328    /**
329     * Returns the current content encoding to be used in HTTP response.<p>
330     *
331     * @return the encoding
332     */
333    public String getEncoding() {
334
335        return m_encoding;
336    }
337
338    /**
339     * Returns the file name translator this context was initialized with.<p>
340     *
341     * The file name translator is used to translate filenames from uploaded files
342     * to valid OpenCms filenames. Example: <code>W&uuml;ste W&ouml;rter.doc --> Wueste_Woerter.doc</code>.<p>
343     *
344     * @return the file name translator this context was initialized with
345     */
346    public CmsResourceTranslator getFileTranslator() {
347
348        return m_fileTranslator;
349    }
350
351    /**
352     * Gets the name of the parent folder of the requested file.<p>
353     *
354     * @return the name of the parent folder of the requested file
355     */
356    public String getFolderUri() {
357
358        return CmsResource.getFolderPath(m_uri);
359    }
360
361    /**
362     * Returns the locale used by this request context.<p>
363     *
364     * In normal operation, the request context locale is initialized using
365     * {@link org.opencms.i18n.I_CmsLocaleHandler#getI18nInfo(javax.servlet.http.HttpServletRequest, CmsUser, CmsProject, String)}
366     * depending on the requested resource URI.<p>
367     *
368     * @return the locale used by this request context
369     *
370     * @see org.opencms.i18n.I_CmsLocaleHandler#getI18nInfo(javax.servlet.http.HttpServletRequest, CmsUser, CmsProject, String)
371     * @see org.opencms.i18n.CmsLocaleManager#getDefaultLocale(CmsObject, String)
372     */
373    public Locale getLocale() {
374
375        return m_locale;
376    }
377
378    /**
379     * Returns the fully qualified name of the organizational unit.<p>
380     *
381     * @return the fully qualified name of the organizational unit
382     */
383    public String getOuFqn() {
384
385        return m_ouFqn;
386    }
387
388    /**
389     * Returns the remote ip address.<p>
390     *
391     * @return the remote ip address as string
392     */
393    public String getRemoteAddress() {
394
395        return m_remoteAddr;
396    }
397
398    /**
399     * Returns the matcher for the current request, that is the host part of the URI from the original http request.<p>
400     *
401     * @return the matcher for the current request, that is the host part of the URI from the original http request
402     */
403    public CmsSiteMatcher getRequestMatcher() {
404
405        return m_requestMatcher;
406    }
407
408    /**
409     * Returns the current request time.<p>
410     *
411     * @return the current request time
412     */
413    public long getRequestTime() {
414
415        return m_requestTime;
416    }
417
418    /**
419     * Returns this request contexts uri extended with the current site root path.<p>
420     *
421     * @return this request contexts uri extended with the current site root path
422     *
423     * @see #getUri()
424     * @see #addSiteRoot(String)
425     */
426    public String getRootUri() {
427
428        return addSiteRoot(m_siteRoot, m_uri);
429    }
430
431    /**
432     * Adjusts the absolute resource root path for the current site.<p>
433     *
434     * The full root path of a resource is always available using
435     * <code>{@link CmsResource#getRootPath()}</code>. From this name this method cuts
436     * of the current site root using
437     * <code>{@link CmsRequestContext#removeSiteRoot(String)}</code>.<p>
438     *
439     * If the resource root path does not start with the current site root,
440     * it is left untouched.<p>
441     *
442     * @param resource the resource to get the adjusted site root path for
443     *
444     * @return the absolute resource path adjusted for the current site
445     *
446     * @see #removeSiteRoot(String)
447     * @see CmsResource#getRootPath()
448     * @see CmsObject#getSitePath(CmsResource)
449     */
450    public String getSitePath(CmsResource resource) {
451
452        return removeSiteRoot(resource.getRootPath());
453    }
454
455    /**
456     * Returns the current root directory in the virtual file system.<p>
457     *
458     * @return the current root directory in the virtual file system
459     */
460    public String getSiteRoot() {
461
462        return m_siteRoot;
463    }
464
465    /**
466     * Returns the OpenCms VFS URI of the requested resource.<p>
467     *
468     * @return the OpenCms VFS URI of the requested resource
469     */
470    public String getUri() {
471
472        return m_uri;
473    }
474
475    /**
476     * Returns true if this is a secure request.<p>
477     *
478     * @return true if this is secure
479     */
480    public boolean isSecureRequest() {
481
482        return m_isSecureRequest;
483    }
484
485    /**
486     * Check if this request context will update the session.<p>
487     *
488     * This is used mainly for CmsReports that continue to use the
489     * users context, even after the http request is already finished.<p>
490     *
491     * @return true if this request context will update the session, false otherwise
492     */
493    public boolean isUpdateSessionEnabled() {
494
495        return m_updateSession;
496    }
497
498    /**
499     * Removes an attribute from the request context.<p>
500     *
501     * @param key the name of the attribute to remove
502     *
503     * @return the removed attribute, or <code>null</code> if no attribute was set with this name
504     */
505    public Object removeAttribute(String key) {
506
507        if (m_attributeMap != null) {
508            return m_attributeMap.remove(key);
509        }
510        return null;
511    }
512
513    /**
514     * Removes the current site root prefix from the absolute path in the resource name,
515     * that is adjusts the resource name for the current site root.<p>
516     *
517     * If the resource name does not start with the current site root,
518     * it is left untouched.<p>
519     *
520     * @param resourcename the resource name
521     *
522     * @return the resource name adjusted for the current site root
523     *
524     * @see #getSitePath(CmsResource)
525     */
526    public String removeSiteRoot(String resourcename) {
527
528        String siteRoot = getAdjustedSiteRoot(m_siteRoot, resourcename);
529        if ((siteRoot == m_siteRoot)
530            && resourcename.startsWith(siteRoot)
531            && ((resourcename.length() == siteRoot.length()) || (resourcename.charAt(siteRoot.length()) == '/'))) {
532            resourcename = resourcename.substring(siteRoot.length());
533        }
534        if (resourcename.length() == 0) {
535            // input was a site root folder without trailing slash
536            resourcename = "/";
537        }
538        return resourcename;
539    }
540
541    /**
542     * Sets an attribute in the request context.<p>
543     *
544     * @param key the attribute name
545     * @param value the attribute value
546     */
547    public void setAttribute(String key, Object value) {
548
549        if (m_attributeMap == null) {
550            // hash table is still the most efficient form of a synchronized Map
551            m_attributeMap = new Hashtable<String, Object>();
552        }
553        m_attributeMap.put(key, value);
554    }
555
556    /**
557     * Sets the current project for the user.<p>
558     *
559     * @param project the project to be set as current project
560     *
561     * @return the CmsProject instance
562     */
563    public CmsProject setCurrentProject(CmsProject project) {
564
565        if (project != null) {
566            m_currentProject = project;
567        }
568        return m_currentProject;
569    }
570
571    /**
572     * Sets the detail content resource.<p>
573     *
574     * @param detailResource the detail content resource
575     */
576    public void setDetailResource(CmsResource detailResource) {
577
578        m_detailResource = detailResource;
579    }
580
581    /**
582     * Sets the current content encoding to be used in HTTP response.<p>
583     *
584     * @param encoding the encoding
585     */
586    public void setEncoding(String encoding) {
587
588        m_encoding = encoding;
589    }
590
591    /**
592     * Sets the locale used by this request context.<p>
593     *
594     * @param locale the locale to set
595     *
596     * @see #getLocale() for more information about how the locale is set in normal operation
597     */
598    public void setLocale(Locale locale) {
599
600        m_locale = locale;
601    }
602
603    /**
604     * Sets the organizational unit fully qualified name.<p>
605     *
606     * @param ouFqn the organizational unit fully qualified name
607     */
608    public void setOuFqn(String ouFqn) {
609
610        String userOu = CmsOrganizationalUnit.getParentFqn(m_user.getName());
611        if (ouFqn != null) {
612            if (ouFqn.startsWith(userOu)
613                || (ouFqn.startsWith(CmsOrganizationalUnit.SEPARATOR) && ouFqn.substring(1).startsWith(userOu))) {
614                m_ouFqn = ouFqn;
615            } else {
616                throw new CmsIllegalArgumentException(
617                    Messages.get().container(Messages.ERR_BAD_ORGUNIT_2, ouFqn, userOu));
618            }
619        } else {
620            m_ouFqn = userOu;
621        }
622        m_ouFqn = CmsOrganizationalUnit.removeLeadingSeparator(m_ouFqn);
623    }
624
625    /**
626     * Sets the current request time.<p>
627     *
628     * @param time the request time
629     */
630    public void setRequestTime(long time) {
631
632        m_requestTime = time;
633    }
634
635    /**
636     * Sets the 'secure request' status.<p>
637     *
638     * @param secureRequest the new value
639     */
640    public void setSecureRequest(boolean secureRequest) {
641
642        m_isSecureRequest = secureRequest;
643    }
644
645    /**
646     * Sets the current root directory in the virtual file system.<p>
647     *
648     * @param root the name of the new root directory
649     */
650    public void setSiteRoot(String root) {
651
652        // site roots must never end with a "/"
653        if (root.endsWith("/")) {
654            m_siteRoot = root.substring(0, root.length() - 1);
655        } else {
656            m_siteRoot = root;
657        }
658    }
659
660    /**
661     * Mark this request context to update the session or not.<p>
662     *
663     * @param value true if this request context will update the session, false otherwise
664     */
665    public void setUpdateSessionEnabled(boolean value) {
666
667        m_updateSession = value;
668    }
669
670    /**
671     * Set the requested resource OpenCms VFS URI, that is the value returned by {@link #getUri()}.<p>
672     *
673     * Use this with caution! Many things (caches etc.) depend on this value.
674     * If you change this value, better make sure that you change it only temporarily
675     * and reset it in a <code>try { // do something // } finally { // reset URI // }</code> statement.<p>
676     *
677     * @param value the value to set the Uri to, must be a complete OpenCms path name like /system/workplace/style.css
678     */
679    public void setUri(String value) {
680
681        m_uri = value;
682    }
683
684    /**
685     * Switches the user in the context, required after a login.<p>
686     *
687     * @param user the new user to use
688     * @param project the new users current project
689     * @param ouFqn the organizational unit
690     */
691    protected void switchUser(CmsUser user, CmsProject project, String ouFqn) {
692
693        m_user = user;
694        m_currentProject = project;
695        setOuFqn(ouFqn);
696    }
697}