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