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