KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > meshcms > core > HitFilter


1 /*
2  * MeshCMS - A simple CMS based on SiteMesh
3  * Copyright (C) 2004-2007 Luciano Vernaschi
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  *
19  * You can contact the author at http://www.cromoteca.com
20  * and at info@cromoteca.com
21  */

22
23 package org.meshcms.core;
24
25 import java.io.*;
26 import java.net.*;
27 import java.util.*;
28 import java.util.zip.*;
29 import javax.servlet.*;
30 import javax.servlet.http.*;
31 import org.meshcms.util.*;
32
33 /**
34  * Filter used to handle the requests for web pages.
35  */

36 public final class HitFilter implements Filter {
37   /**
38    * Name of a cache file in the repository.
39    */

40   public static final String JavaDoc CACHE_FILE_NAME = "_cache.gz";
41
42   /**
43    * Name of the request attribute that contains the name of the current theme
44    * file.
45    *
46    * @see RequestDecoratorMapper
47    */

48   public static final String JavaDoc THEME_FILE_ATTRIBUTE = "meshcmstheme";
49
50   /**
51    * Name of the request attribute that contains the name of the current theme
52    * folder.
53    */

54   public static final String JavaDoc THEME_PATH_ATTRIBUTE = "meshcmsthemepath";
55
56   public static final String JavaDoc LOCALE_ATTRIBUTE = "meshcmslocale";
57
58   public static final String JavaDoc LAST_MODIFIED_ATTRIBUTE = "meshcmslastmodified";
59
60   public static final String JavaDoc BLOCK_CACHE_ATTRIBUTE = "meshcmsnocache";
61
62   public static final String JavaDoc WEBSITE_ATTRIBUTE = "webSite";
63
64   /**
65    * Name of the session attribute that allows hotlinking within the session
66    * itself.
67    */

68   public static final String JavaDoc HOTLINKING_ALLOWED = "meshcmshotlinkingallowed";
69
70   /**
71    * Name of the request parameter that is used to specify some actions.
72    * Currently only {@link #ACTION_EDIT} is used as value. This parameter is
73    * read by custom JSP tags.
74    */

75   public static final String JavaDoc ACTION_NAME = "meshcmsaction";
76
77   /**
78    * Value of {@link #ACTION_NAME} used to indicate that the current page
79    * must be edited.
80    */

81   public static final String JavaDoc ACTION_EDIT = "edit";
82
83   public static final String JavaDoc ROOT_WEBSITE = "meshcmsrootsite";
84
85   private FilterConfig filterConfig = null;
86
87   public void init(FilterConfig filterConfig) throws ServletException {
88     this.filterConfig = filterConfig;
89   }
90
91   public void destroy() {
92     this.filterConfig = null;
93   }
94
95   /**
96    * This filter manages a page to make sure it is served correctly.
97    */

98   public void doFilter(ServletRequest request, ServletResponse response,
99       FilterChain chain) throws IOException, ServletException {
100     if (filterConfig != null && (request instanceof HttpServletRequest)) {
101       ServletContext sc = filterConfig.getServletContext();
102       HttpServletResponse httpRes = (HttpServletResponse) response;
103       WebSite rootSite = getRootSite(sc, false);
104       WebSite webSite = rootSite.getWebSite(request);
105
106       if (webSite == null) {
107         httpRes.sendError(HttpServletResponse.SC_FORBIDDEN, "Site not found");
108         return;
109       }
110
111       request.setAttribute(WEBSITE_ATTRIBUTE, webSite);
112       request.setCharacterEncoding(WebSite.SYSTEM_CHARSET);
113       HttpServletRequest httpReq = webSite.wrapRequest(request);
114       Path pagePath = webSite.getRequestedPath(httpReq);
115
116       /* This is needed to avoid source code disclosure in virtual sites */
117       if (webSite instanceof VirtualWebSite &&
118           httpReq.getRequestURI().toLowerCase().endsWith(".jsp/")) {
119         httpRes.sendError(HttpServletResponse.SC_NOT_FOUND);
120         return;
121       }
122       
123       if (webSite.isDirectory(pagePath)) {
124         Path wPath = webSite.findCurrentWelcome(pagePath);
125         Configuration conf = webSite.getConfiguration();
126
127         if (conf == null || conf.isAlwaysDenyDirectoryListings()) {
128           if (wPath != null) {
129             redirect(httpReq, httpRes, wPath);
130           } else {
131             httpRes.sendError(HttpServletResponse.SC_FORBIDDEN,
132                 "Directory listing denied");
133           }
134
135           return;
136         }
137       }
138
139       SiteMap siteMap = null;
140       PageInfo pageInfo = null;
141       boolean isAdminPage = false;
142       boolean isGuest = true;
143       String JavaDoc pageCharset = null;
144
145       if (webSite.getCMSPath() != null) {
146         if (pagePath.isContainedIn(webSite.getVirtualSitesPath()) ||
147             pagePath.isContainedIn(webSite.getPrivatePath())) {
148           httpRes.sendError(HttpServletResponse.SC_NOT_FOUND);
149           return;
150         }
151
152         siteMap = webSite.getSiteMap();
153         isAdminPage = pagePath.isContainedIn(webSite.getAdminPath());
154         HttpSession session = httpReq.getSession();
155
156         if (webSite.getConfiguration().isSearchMovedPages() &&
157             !(isAdminPage || webSite.getFile(pagePath).exists())) {
158           Path redirPath = siteMap.getRedirMatch(pagePath);
159           
160           if (redirPath != null) {
161             redirect(httpReq, httpRes, redirPath);
162             return;
163           }
164         }
165
166         UserInfo userInfo = (session == null) ? null :
167             (UserInfo) session.getAttribute("userInfo");
168         isGuest = userInfo == null || userInfo.isGuest();
169
170         // Deal with all pages
171
if (FileTypes.isPage(pagePath.getLastElement())) {
172           // Block direct requests of modules from non authenticated users
173
/* if (isGuest && webSite.isInsideModules(pagePath) &&
174               pagePath.getLastElement().equalsIgnoreCase(SiteMap.MODULE_INCLUDE_FILE)) {
175             httpRes.sendError(HttpServletResponse.SC_NOT_FOUND);
176             return;
177           } */

178
179           WebUtils.updateLastModifiedTime(httpReq, webSite.getFile(pagePath));
180           blockRemoteCaching(httpRes);
181
182           // Find a theme for this page
183
Path themePath = null;
184           String JavaDoc themeParameter = request.getParameter(THEME_FILE_ATTRIBUTE);
185
186           if (themeParameter != null) {
187             themePath = (Path) siteMap.getThemesMap().get(themeParameter);
188           }
189
190           if (themePath == null || !webSite.getFile(themePath).exists()) {
191             themePath = webSite.getThemePath(pagePath);
192           }
193
194           if (themePath != null) { // there is a theme for this page
195
request.setAttribute(THEME_PATH_ATTRIBUTE, themePath);
196
197             // pages in /admin do not need a decorator to be specified:
198
if (!isAdminPage || themeParameter != null) {
199               request.setAttribute(THEME_FILE_ATTRIBUTE, "/" +
200                   webSite.getServedPath(themePath) + "/" +
201                   SiteMap.THEME_DECORATOR);
202             }
203           }
204
205           /* Since a true page has been requested, disable hotlinking prevention
206              for this session */

207           if (session != null && webSite.getConfiguration().isPreventHotlinking() &&
208               session.getAttribute(HOTLINKING_ALLOWED) == null && !isAdminPage) {
209             session.setAttribute(HOTLINKING_ALLOWED, HOTLINKING_ALLOWED);
210           }
211         }
212
213         if (webSite.getConfiguration().isPreventHotlinking() &&
214             FileTypes.isPreventHotlinking(pagePath.getLastElement()) &&
215             (session == null || session.getAttribute(HOTLINKING_ALLOWED) == null)) {
216           String JavaDoc agent = httpReq.getHeader("user-agent");
217
218           if (agent == null || agent.toLowerCase().indexOf("java") < 0) {
219             try {
220               String JavaDoc domain = WebUtils.get2ndLevelDomain(httpReq);
221
222               if (domain != null) {
223                 String JavaDoc referrer = httpReq.getHeader("referer");
224
225                 try {
226                   referrer = new URL(referrer).getHost();
227                 } catch (Exception JavaDoc ex) {
228                   referrer = null;
229                 }
230
231                 if (referrer == null || referrer.indexOf(domain) < 0) {
232                   httpRes.sendRedirect(httpReq.getContextPath() + "/" +
233                       webSite.getAdminPath() + "/hotlinking.jsp?path=" + pagePath);
234                   return;
235                 }
236               }
237             } catch (MalformedURLException ex) {}
238           }
239         }
240
241         pageInfo = siteMap.getPageInfo(pagePath);
242
243         if (pageInfo != null) { // this page is contained in the site map
244
if (isGuest) {
245             pageInfo.addHit();
246           }
247
248           if (pageInfo.getCharset() != null) {
249             pageCharset = pageInfo.getCharset();
250           }
251         } else { // not a page in the site map
252
// Let's try to apply the right charset to JavaScript lang files
253
if (isAdminPage && pagePath.getLastElement().endsWith(".js") &&
254               userInfo != null && userInfo.canDo(UserInfo.CAN_BROWSE_FILES)) {
255             String JavaDoc script = null;
256
257             if (pagePath.isContainedIn(webSite.getAdminScriptsPath().add("tiny_mce"))) {
258               script = "TinyMCE";
259             } else if (pagePath.isContainedIn(webSite.getAdminScriptsPath().add("jscalendar"))) {
260               script = "DHTMLCalendar";
261             }
262
263             if (script != null) {
264               Locale locale = Utils.getLocale(userInfo.getPreferredLocaleCode());
265               ResourceBundle bundle =
266                   ResourceBundle.getBundle("org/meshcms/webui/Locales", locale);
267               String JavaDoc s = bundle.getString(script + "LangCode");
268
269               if (!Utils.isNullOrEmpty(s) && pagePath.getLastElement().equals(s + ".js")) {
270                 s = bundle.getString(script + "LangCharset");
271
272                 if (!Utils.isNullOrEmpty(s)) {
273                   pageCharset = s;
274                 }
275               }
276             }
277           } // end of JavaScript stuff
278
}
279       } // end of CMS stuff
280

281       String JavaDoc mimeType = sc.getMimeType(pagePath.getLastElement());
282
283       if (mimeType == null) {
284         mimeType = "text/html";
285       }
286
287       httpRes.setContentType(mimeType + "; charset=" +
288           Utils.noNull(pageCharset, WebSite.SYSTEM_CHARSET));
289
290       try {
291         // Cache management
292
if (isGuest && pageInfo != null &&
293             httpReq.getMethod().equalsIgnoreCase("get") &&
294             Utils.isNullOrEmpty(httpReq.getQueryString()) &&
295             webSite.isVisuallyEditable(pagePath)) {
296           int cacheType = webSite.getConfiguration().getCacheType();
297
298           // Let's see if the browser supports GZIP
299
String JavaDoc ae = httpReq.getHeader("Accept-Encoding");
300           boolean gzip = ae != null && ae.toLowerCase().indexOf("gzip") > -1;
301           InputStream in = null;
302
303           if (cacheType == Configuration.IN_MEMORY_CACHE) {
304             byte[] pageBytes = siteMap.getCached(pageInfo.getPath());
305
306             // a cached page too small is suspicious
307
if (pageBytes != null && pageBytes.length > 256) {
308               in = new ByteArrayInputStream(pageBytes);
309             }
310           } else if (cacheType == Configuration.ON_DISK_CACHE) {
311             File cacheFile = WebUtils.getCacheFile(webSite, siteMap, pagePath);
312
313             if (cacheFile != null) {
314               in = new FileInputStream(cacheFile);
315             }
316           }
317
318           // if a valid cached version has been found, use it
319
if (in != null) {
320             ServletOutputStream sos = response.getOutputStream();
321
322             if (gzip) {
323               httpRes.setHeader("Content-Encoding", "gzip");
324             } else {
325               // uncompress the page on the fly for that spider or old browser
326
in = new GZIPInputStream(in);
327             }
328
329             Utils.copyStream(in, sos, false);
330             sos.flush();
331             return;
332           }
333
334           // otherwise, if cache is enabled, store the generated page
335
if (cacheType != Configuration.NO_CACHE) {
336             ByteArrayOutputStream baos = new ByteArrayOutputStream();
337             OutputStream os = new GZIPOutputStream(baos);
338             CacheResponseWrapper wrapper = new CacheResponseWrapper(httpRes, os);
339             chain.doFilter(httpReq, wrapper);
340             wrapper.finishResponse();
341             // os.flush();
342

343             /* If WebUtils.setBlockCache has not been called while creating
344                the page, it can be cached */

345             if (!WebUtils.isCacheBlocked(httpReq)) {
346               if (cacheType == Configuration.IN_MEMORY_CACHE) {
347                 siteMap.cache(pageInfo.getPath(), baos.toByteArray());
348               } else if (cacheType == Configuration.ON_DISK_CACHE) {
349                 File cacheFile = webSite.getRepositoryFile
350                     (siteMap.getServedPath(pagePath), CACHE_FILE_NAME);
351                 cacheFile.getParentFile().mkdirs();
352                 Utils.writeFully(cacheFile, baos.toByteArray());
353               }
354             }
355
356             return;
357           }
358         } // end of cache management
359

360         chain.doFilter(httpReq, httpRes);
361         webSite.updateSiteMap(false); // better here than nowhere :)
362
} catch (Exception JavaDoc ex) {
363         if (isAdminPage) {
364           webSite.setLastAdminThemeBlock(System.currentTimeMillis());
365         }
366
367         Path wPath = webSite.findCurrentWelcome(pagePath);
368
369         if (wPath != null) {
370           redirect(httpReq, httpRes, wPath);
371           return;
372         }
373
374         sc.log("--------\n\nIMPORTANT: an exception has been caught while serving " +
375             httpReq.getRequestURI(), ex);
376
377         Configuration c = webSite.getConfiguration();
378
379         if (c == null || c.isHideExceptions()) {
380           httpRes.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
381         } else {
382           throw new ServletException(ex);
383         }
384       }
385
386       return;
387     }
388
389     // should never be reached
390
chain.doFilter(request, response);
391   }
392
393   private static void redirect(HttpServletRequest httpReq,
394       HttpServletResponse httpRes, Path redirPath) throws IOException {
395     blockRemoteCaching(httpRes);
396     String JavaDoc q = httpReq.getQueryString();
397
398     if (Utils.isNullOrEmpty(q)) {
399       httpRes.sendRedirect(httpReq.getContextPath() + "/" + redirPath);
400     } else {
401       httpRes.sendRedirect(httpReq.getContextPath() + "/" + redirPath + '?' + q);
402     }
403   }
404
405   /**
406    * @return the main website instance. It will be created if not already done.
407    */

408   public static WebSite getRootSite(ServletContext sc, boolean alwaysCreate) {
409     WebSite rootSite = (WebSite) sc.getAttribute(ROOT_WEBSITE);
410
411     if (rootSite == null || alwaysCreate) {
412       File rootFile = new File(sc.getRealPath("/"));
413       Path cmsPath = new CMSDirectoryFinder(rootFile, false).getCMSPath();
414       boolean multisite = false;
415       File sitesDir = new File(rootFile, cmsPath + "/sites");
416
417       if (sitesDir.isDirectory()) {
418         File[] dirs = sitesDir.listFiles();
419
420         for (int i = 0; i < dirs.length; i++) {
421           if (dirs[i].isDirectory()) {
422             multisite = true;
423           }
424         }
425       }
426
427       rootSite = multisite ?
428           MainWebSite.create(sc, WebUtils.getWelcomeFiles(sc), rootFile,
429               Path.ROOT, cmsPath) :
430           WebSite.create(sc, WebUtils.getWelcomeFiles(sc), rootFile,
431               Path.ROOT, cmsPath);
432       sc.setAttribute(ROOT_WEBSITE, rootSite);
433     }
434
435     return rootSite;
436   }
437
438   /**
439    * Sets some headers to discourage remote caching of pages.
440    */

441   public static void blockRemoteCaching(HttpServletResponse httpRes) {
442     // HTTP 1.1
443
httpRes.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
444     // HTTP 1.0
445
httpRes.setHeader("Pragma", "no-cache");
446     // prevents caching at the proxy server
447
httpRes.setDateHeader("Expires", -1);
448   }
449 }
450
Popular Tags