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