001: /*
002: * Copyright (c) JForum Team
003: * All rights reserved.
004: *
005: * Redistribution and use in source and binary forms,
006: * with or without modification, are permitted provided
007: * that the following conditions are met:
008: *
009: * 1) Redistributions of source code must retain the above
010: * copyright notice, this list of conditions and the
011: * following disclaimer.
012: * 2) Redistributions in binary form must reproduce the
013: * above copyright notice, this list of conditions and
014: * the following disclaimer in the documentation and/or
015: * other materials provided with the distribution.
016: * 3) Neither the name of "Rafael Steil" nor
017: * the names of its contributors may be used to endorse
018: * or promote products derived from this software without
019: * specific prior written permission.
020: *
021: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT
022: * HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
023: * EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
024: * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
025: * MERCHANTABILITY AND FITNESS FOR A PARTICULAR
026: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
027: * THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
028: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
029: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES
030: * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
031: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
032: * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
033: * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
034: * IN CONTRACT, STRICT LIABILITY, OR TORT
035: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
036: * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
037: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE
038: *
039: * This file creation date: Mar 16, 2003 / 1:31:30 AM
040: * The JForum Project
041: * http://www.jforum.net
042: */
043: package net.jforum.context.web;
044:
045: import java.io.File;
046: import java.io.IOException;
047: import java.io.UnsupportedEncodingException;
048: import java.util.ArrayList;
049: import java.util.Enumeration;
050: import java.util.HashMap;
051: import java.util.Iterator;
052: import java.util.List;
053: import java.util.Map;
054: import java.util.StringTokenizer;
055:
056: import javax.servlet.http.HttpServletRequest;
057: import javax.servlet.http.HttpServletRequestWrapper;
058:
059: import net.jforum.UrlPattern;
060: import net.jforum.UrlPatternCollection;
061: import net.jforum.context.RequestContext;
062: import net.jforum.context.SessionContext;
063: import net.jforum.exceptions.MultipartHandlingException;
064: import net.jforum.util.legacy.commons.fileupload.FileItem;
065: import net.jforum.util.legacy.commons.fileupload.FileUploadException;
066: import net.jforum.util.legacy.commons.fileupload.disk.DiskFileItemFactory;
067: import net.jforum.util.legacy.commons.fileupload.servlet.ServletFileUpload;
068: import net.jforum.util.legacy.commons.fileupload.servlet.ServletRequestContext;
069: import net.jforum.util.preferences.ConfigKeys;
070: import net.jforum.util.preferences.SystemGlobals;
071:
072: import org.apache.commons.lang.StringUtils;
073:
074: /**
075: * @author Rafael Steil
076: * @version $Id: WebRequestContext.java,v 1.14 2007/09/25 23:38:48 rafaelsteil Exp $
077: */
078: public class WebRequestContext extends HttpServletRequestWrapper
079: implements RequestContext {
080: private Map query;
081:
082: /**
083: * Default constructor.
084: *
085: * @param superRequest Original <code>HttpServletRequest</code> instance
086: * @throws IOException
087: */
088: public WebRequestContext(HttpServletRequest super Request)
089: throws IOException {
090: super (super Request);
091:
092: this .query = new HashMap();
093: boolean isMultipart = false;
094:
095: String requestType = super Request.getMethod().toUpperCase();
096: String contextPath = super Request.getContextPath();
097: String requestUri = this .extractRequestUri(super Request
098: .getRequestURI(), contextPath);
099: String encoding = SystemGlobals.getValue(ConfigKeys.ENCODING);
100: String servletExtension = SystemGlobals
101: .getValue(ConfigKeys.SERVLET_EXTENSION);
102:
103: boolean isPost = "POST".equals(requestType);
104: boolean isGet = !isPost;
105:
106: boolean isQueryStringEmpty = (super Request.getQueryString() == null || super Request
107: .getQueryString().length() == 0);
108:
109: if (isGet && isQueryStringEmpty
110: && requestUri.endsWith(servletExtension)) {
111: super Request.setCharacterEncoding(encoding);
112: this .parseFriendlyURL(requestUri, servletExtension);
113: } else if (isPost) {
114: isMultipart = ServletFileUpload
115: .isMultipartContent(new ServletRequestContext(
116: super Request));
117:
118: if (isMultipart) {
119: this .handleMultipart(super Request, encoding);
120: }
121: }
122:
123: if (!isMultipart) {
124: boolean isAjax = "XMLHttpRequest".equals(super Request
125: .getHeader("X-Requested-With"));
126:
127: if (!isAjax) {
128: super Request.setCharacterEncoding(encoding);
129: } else {
130: // Ajax requests are *usually* sent using application/x-www-form-urlencoded; charset=UTF-8.
131: // In JForum, we assume this as always true.
132: super Request.setCharacterEncoding("UTF-8");
133: }
134:
135: String containerEncoding = SystemGlobals
136: .getValue(ConfigKeys.DEFAULT_CONTAINER_ENCODING);
137:
138: if (isPost) {
139: containerEncoding = encoding;
140: }
141:
142: for (Enumeration e = super Request.getParameterNames(); e
143: .hasMoreElements();) {
144: String name = (String) e.nextElement();
145:
146: String[] values = super Request.getParameterValues(name);
147:
148: if (values != null && values.length > 1) {
149: for (int i = 0; i < values.length; i++) {
150: this
151: .addParameter(
152: name,
153: new String(
154: values[i]
155: .getBytes(containerEncoding),
156: encoding));
157: }
158: } else {
159: this .addParameter(name, new String(super Request
160: .getParameter(name).getBytes(
161: containerEncoding), encoding));
162: }
163: }
164:
165: if (this .getModule() == null && this .getAction() == null) {
166: int index = requestUri.indexOf('?');
167:
168: if (index > -1) {
169: requestUri = requestUri.substring(0, index);
170: }
171:
172: this .parseFriendlyURL(requestUri, servletExtension);
173: }
174: }
175: }
176:
177: /**
178: * @param requestUri
179: * @param servletExtension
180: */
181: private void parseFriendlyURL(String requestUri,
182: String servletExtension) {
183: requestUri = requestUri.substring(0, requestUri.length()
184: - servletExtension.length());
185: String[] urlModel = requestUri.split("/");
186:
187: int moduleIndex = 1;
188: int actionIndex = 2;
189: int baseLen = 3;
190:
191: UrlPattern url = null;
192:
193: if (urlModel.length >= baseLen) {
194: // <moduleName>.<actionName>.<numberOfParameters>
195: StringBuffer sb = new StringBuffer(64).append(
196: urlModel[moduleIndex]).append('.').append(
197: urlModel[actionIndex]).append('.').append(
198: urlModel.length - baseLen);
199:
200: url = UrlPatternCollection.findPattern(sb.toString());
201: }
202:
203: if (url != null) {
204: if (url.getSize() >= urlModel.length - baseLen) {
205: for (int i = 0; i < url.getSize(); i++) {
206: this .addParameter(url.getVars()[i], urlModel[i
207: + baseLen]);
208: }
209: }
210:
211: this .addParameter("module", urlModel[moduleIndex]);
212: this .addParameter("action", urlModel[actionIndex]);
213: } else {
214: this .addParameter("module", null);
215: this .addParameter("action", null);
216: }
217: }
218:
219: public SessionContext getSessionContext(boolean create) {
220: return new WebSessionContext(this .getSession(true));
221: }
222:
223: public SessionContext getSessionContext() {
224: return new WebSessionContext(this .getSession());
225: }
226:
227: /**
228: * @param superRequest HttpServletRequest
229: * @param encoding String
230: * @throws UnsupportedEncodingException
231: */
232: private void handleMultipart(HttpServletRequest super Request,
233: String encoding) throws UnsupportedEncodingException {
234: String tmpPath = new StringBuffer(256).append(
235: SystemGlobals.getApplicationPath()).append('/').append(
236: SystemGlobals.getValue(ConfigKeys.TMP_DIR)).toString();
237:
238: File tmpDir = new File(tmpPath);
239: boolean success = false;
240:
241: try {
242: if (!tmpDir.exists()) {
243: tmpDir.mkdirs();
244: success = true;
245: }
246: } catch (Exception e) {
247: // We won't log it because the directory
248: // creation failed for some reason - a SecurityException
249: // or something else. We don't care about it, as the
250: // code below tries to use java.io.tmpdir
251: }
252:
253: if (!success) {
254: tmpPath = System.getProperty("java.io.tmpdir");
255: tmpDir = new File(tmpPath);
256: }
257:
258: ServletFileUpload upload = new ServletFileUpload(
259: new DiskFileItemFactory(100 * 1024, tmpDir));
260: upload.setHeaderEncoding(encoding);
261:
262: try {
263: List items = upload.parseRequest(super Request);
264:
265: for (Iterator iter = items.iterator(); iter.hasNext();) {
266: FileItem item = (FileItem) iter.next();
267:
268: if (item.isFormField()) {
269: this .addParameter(item.getFieldName(), item
270: .getString(encoding));
271: } else {
272: if (item.getSize() > 0) {
273: // We really don't want to call addParameter(), as
274: // there should not be possible to have multiple
275: // values for a InputStream data
276: this .query.put(item.getFieldName(), item);
277: }
278: }
279: }
280: } catch (FileUploadException e) {
281: throw new MultipartHandlingException(
282: "Error while processing multipart content: " + e);
283: }
284: }
285:
286: /**
287: * @see javax.servlet.ServletRequestWrapper#getParameterValues(java.lang.String)
288: */
289: public String[] getParameterValues(String name) {
290: Object value = this .getObjectParameter(name);
291:
292: if (value instanceof String) {
293: return new String[] { (String) value };
294: }
295:
296: List l = (List) value;
297:
298: return l == null ? super .getParameterValues(name)
299: : (String[]) l.toArray(new String[0]);
300: }
301:
302: private String extractRequestUri(String requestUri,
303: String contextPath) {
304: // First, remove the context path from the requestUri,
305: // so we can work only with the important stuff
306: if (contextPath != null && contextPath.length() > 0) {
307: requestUri = requestUri.substring(contextPath.length(),
308: requestUri.length());
309: }
310:
311: // Remove the "jsessionid" (or similar) from the URI
312: // Probably this is not the right way to go, since we're
313: // discarding the value...
314: int index = requestUri.indexOf(';');
315:
316: if (index > -1) {
317: int lastIndex = requestUri.indexOf('?', index);
318:
319: if (lastIndex == -1) {
320: lastIndex = requestUri.indexOf('&', index);
321: }
322:
323: if (lastIndex == -1) {
324: requestUri = requestUri.substring(0, index);
325: } else {
326: String part1 = requestUri.substring(0, index);
327: requestUri = part1 + requestUri.substring(lastIndex);
328: }
329: }
330:
331: return requestUri;
332: }
333:
334: /**
335: * @see javax.servlet.ServletRequest#getParameter(java.lang.String)
336: */
337: public String getParameter(String parameter) {
338: return (String) this .query.get(parameter);
339: }
340:
341: /**
342: * Gets an parameter that is a number.
343: * A call to <code>Integer#parseInt(String)</code> is made
344: * to do the conversion
345: * @param parameter The parameter name to get the value
346: * @return int
347: */
348: public int getIntParameter(String parameter) {
349: return Integer.parseInt(this .getParameter(parameter));
350: }
351:
352: /**
353: * Gets some request parameter as <code>Object</code>.
354: * This method may be used when you have to get some value
355: * of a <i>multipart/form-data</i> request, like a image
356: * of file. <br>
357: *
358: * @param parameter String
359: * @return Object
360: */
361: public Object getObjectParameter(String parameter) {
362: return this .query.get(parameter);
363: }
364:
365: public void addParameter(String name, Object value) {
366: if (!this .query.containsKey(name)) {
367: this .query.put(name, value);
368: } else {
369: Object currentValue = this .getObjectParameter(name);
370: List l;
371:
372: if (!(currentValue instanceof List)) {
373: l = new ArrayList();
374: l.add(currentValue);
375: } else {
376: l = (List) currentValue;
377: }
378:
379: l.add(value);
380: this .query.put(name, l);
381: }
382: }
383:
384: public void addOrReplaceParameter(String name, Object value) {
385: this .query.put(name, value);
386: }
387:
388: /**
389: * Gets the <i>action</i> of the current request.
390: *
391: * An <i>Action</i> is the parameter name which specifies
392: * what next action should be done by the system. It may be
393: * add or edit a post, editing the groups, whatever. In the URL, the
394: * Action can the represented in two forms:
395: * <p>
396: * <blockquote>
397: * <code>
398: * http://www.host.com/webapp/servletName?module=groups&action=list
399: * </code>
400: * </blockquote>
401: * <p>
402: * or
403: * <p>
404: * <blockquote>
405: * <code>
406: * http://www.host.com/webapp/servletName/groups/list
407: * </code>
408: * </blockquote>
409: * <p>
410: * In both situations, the action's name is "list".
411: *
412: * @return String representing the action name
413: */
414: public String getAction() {
415: return this .getParameter("action");
416: }
417:
418: public void changeAction(String newAction) {
419: if (this .query.containsKey("action")) {
420: this .query.remove("action");
421: this .query.put("action", newAction);
422: } else {
423: this .addParameter("action", newAction);
424: }
425: }
426:
427: /**
428: * Gets the <i>module</i> of the current request.
429: *
430: * A <i>Module</i> is the parameter name which specifies
431: * what module the user is requesting. It may be the group
432: * administration, the topics or anything else configured module.
433: *In the URL, the Module can the represented in two forms:
434: * <p>
435: * <blockquote>
436: * <code>
437: * http://www.host.com/webapp/servletName?module=groups&action=list
438: * </code>
439: * </blockquote>
440: * <p>
441: * or
442: * <p>
443: * <blockquote>
444: * <code>
445: * http://www.host.com/webapp/servletName/groups/list
446: * </code>
447: * </blockquote>
448: * <p>
449: * In both situations, the module's name is "groups".
450: *
451: * @return String representing the module name
452: */
453: public String getModule() {
454: return this .getParameter("module");
455: }
456:
457: public Object getObjectRequestParameter(String parameter) {
458: return this .query.get(parameter);
459: }
460:
461: /**
462: * @see javax.servlet.http.HttpServletRequestWrapper#getContextPath()
463: */
464: public String getContextPath() {
465: String contextPath = super .getContextPath();
466: String proxiedContextPath = SystemGlobals
467: .getValue(ConfigKeys.PROXIED_CONTEXT_PATH);
468:
469: if (!StringUtils.isEmpty(proxiedContextPath)) {
470: contextPath = proxiedContextPath;
471: }
472:
473: return contextPath;
474: }
475:
476: /**
477: * @see javax.servlet.ServletRequestWrapper#getRemoteAddr()
478: */
479: public String getRemoteAddr() {
480: // We look if the request is forwarded
481: // If it is not call the older function.
482: String ip = super .getHeader("x-forwarded-for");
483:
484: if (ip == null) {
485: ip = super .getRemoteAddr();
486: } else {
487: // Process the IP to keep the last IP (real ip of the computer on the net)
488: StringTokenizer tokenizer = new StringTokenizer(ip, ",");
489:
490: // Ignore all tokens, except the last one
491: for (int i = 0; i < tokenizer.countTokens() - 1; i++) {
492: tokenizer.nextElement();
493: }
494:
495: ip = tokenizer.nextToken().trim();
496:
497: if (ip.equals("")) {
498: ip = null;
499: }
500: }
501:
502: // If the ip is still null, we put 0.0.0.0 to avoid null values
503: if (ip == null) {
504: ip = "0.0.0.0";
505: }
506:
507: return ip;
508: }
509: }
|